DEV Community

Cover image for Resolving path alias in nestjs projects
Rubin
Rubin

Posted on • Edited on

Resolving path alias in nestjs projects

Building a nestjs project in typescript is really awesome but as the project starts to grow , your directories will grow too thus making your imports lengthy like this:

import { User } from '../../user/model';
import { Article } from '../../article/model';

import { Cache } from '../../../../cache';
import { MongoDB } from '../../../../mongodb';
Enter fullscreen mode Exit fullscreen mode

Path mapping to the rescue!

TypeScript allows the use of path mapping which allows arbitrary module paths (that doesn’t start with “/” or “.”) to be specified and mapped to physical paths in the filesystem in the compiler options in tsconfig file like below:

{
  "compilerOptions": {
    ...
    "baseUrl": "./src",
    "paths": {
      "@datorama/utils/*": ["app/utils/*"],
      "@datorama/pipes/*": ["app/pipes/*"]
    }
    ...
  }
}
Enter fullscreen mode Exit fullscreen mode

The first property that we must add is the baseUrl property. Notice that paths are resolved relative to baseUrl .
The second property is the paths property. This tells the compiler for any module import that matches the pattern "@datorama/utils/*" , to look in the following location:

The Problem

When you run the code inline (during execution) , it works as intended but when you build it for production and try to run it, you get the following error:

Error: Cannot find module '@datorama/utils'
Enter fullscreen mode Exit fullscreen mode

The issue mentioned is of execution with node.Actually the problem occurs when executing the built files with node dist/main.js, not during the build process with tsc.

The Solution

After doing some search on the problem, I came across multiple solutions.

Although there are bunch of solutions above, the first and second one didn't work for me. So I went with the third one which was actually mentioned in the docs of tsconfig-paths which nest uses to resolve these paths at runtime.

To apply the solution, make a file named tsconfig-paths-bootstrap.js (The name is up to you) and copy/paste the below snippet

// tsconfig-paths-bootstrap.js

const tsConfig = require('./tsconfig.json');
const tsConfigPaths = require('tsconfig-paths');

const baseUrl = './dist'; // Either absolute or relative path. If relative it's resolved to current working directory.
tsConfigPaths.register({
  baseUrl,
  paths: tsConfig.compilerOptions.paths,
});
Enter fullscreen mode Exit fullscreen mode

Build the dist file normally as you do with tsc. After that, you can run it with

node -r ./tsconfig-paths-bootstrap.js dist/main.js
Enter fullscreen mode Exit fullscreen mode

Note: Check the file name with the filename in the command and you are good to go

Shoutout to Jay McDoniel for his help.

Follow me on Github: www.github.com/rubiin

Top comments (13)

Collapse
 
mohiuddinakib profile image
mohammad mohiuddin mostafa kamal akib

import * as path from 'path';
import * as moduleAlias from 'module-alias';

moduleAlias.addAliases({
'@src': path.resolve(dirname),
'@interfaces': path.resolve(
dirname, 'interfaces'),
'@modules': path.resolve(__dirname, 'modules'),
});

at top of the main.ts will work

Collapse
 
duongdev profile image
Dương Đỗ

Thanks, this helps me better.

Collapse
 
durchanek profile image
Láďa Durchánek • Edited

The first solution worked for me like this:

const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');

module.exports = {
  webpack(config) {
    config.resolve.plugins.push(new TsconfigPathsPlugin());
    return config;
  },
};
Collapse
 
rubiin profile image
Rubin

thanks for sharing

Collapse
 
hamidjafari profile image
hamid jafari

thankkkk youuu
you saved my day :)
after some truble doing absolute path with ts i was about to forget about absolute path and come back to old relative paths :x
until i found your article
thank you <3

Collapse
 
rubiin profile image
Rubin

Any time.i had the same trouble so I wrote this article for those in need

Collapse
 
raychz profile image
Ray Chavez

Great tutorial, Rubin! I've followed everything here, but I'm now running into an issue where Nest cannot resolve the dependencies if I import a class that is within the same directory using the tsconfig path alias. I opened up an issue with Nest, but I wonder if there's just something I'm not doing right. Issue here:

Nest dependency resolution issue with tsconfig paths and same directory imports #2897

raychz avatar
raychz commented on Sep 06, 2019

Bug Report

NOTE: This is not a duplicate of #986 as I am already using tsconfig-paths-bootstrap.js.

Current behavior

I've set a few different paths in tsconfig.json to make importing of controllers, entities, services, etc. easier to deal with. Relevant portion of tsconfig.json:

    ...
    "baseUrl": "./src",
    "paths": {
      "@hello/controllers": [
        "./controllers"
      ],
      "@hello/entities": [
        "./entity"
      ],
      "@hello/services": [
        "./services"
      ]
    },
    ...

I've also created barrel files (index.ts) within the src/controllers/, src/entity/, and src/services/ directories that re-exports all of the classes that I need from within those directories.

Everything works as expected when importing a service from a file that is within my controllers directory. Example:

// src/controllers/comment.controller.ts
// This works
import { CommentService } from '@hello/services';

@Controller()
export class CommentController {...}

Things do NOT work when importing a service from another service file that is within the same directory. Example

// src/services/comment.service.ts
// This does NOT work
import { StoryService, UserService } from '@hello/services';
// StoryService, UserService, and CommentService are in the src/services directory 

@Injectable()
export class CommentService {...}

The error that I get when doing the above is:

Error: Nest can't resolve dependencies of the CommentService (?, +). Please make sure that the argument at index [0] is available in the AppModule context.

Expected behavior

I expect dependencies to resolve using a path defined in tsconfig.json, even if they're being imported from within the same directory.

Possible Solution

My current workaround is to import the files using relative paths:

// src/services/comment.service.ts
// This does work
import { StoryService } from './story.service';
import { UserService } from './user.service';
// I'd prefer to do this:
// import { StoryService, UserService } from '@hello/services';

@Injectable()
export class CommentService {...}

Environment


@nestjs/common@5.7.4
@nestjs/core@5.7.4
typescript@3.6.2

Collapse
 
rubiin profile image
Rubin

Also I would recommend to skip barrel if you are using path aliases because they do kind of the same thing. Also You could achieve a folderwise import with dir/* so there is no point in using it with path

Collapse
 
rubiin profile image
Rubin • Edited

Its possibly an module import problem. Make sure to import the corresponding modules if you are using the services. Do check if you are missing an import

Collapse
 
tkssharma profile image
tkssharma
import * as path from 'path';
import * as moduleAlias from 'module-alias';

moduleAlias.addAliases({
'@src': path.resolve(dirname),
'@interfaces': path.resolve(dirname, 'interfaces'),
'@modules': path.resolve(__dirname, 'modules'),
});
Enter fullscreen mode Exit fullscreen mode

is this the only way to do this ??
i want to remove ../../../../../ stuff in my code !!

Collapse
 
ofuochi profile image
Fortune

Please how can I get this working on test? I'm still getting the error when running unit/integration tests

Collapse
 
davychhouk profile image
Davy CHHOUK

Good job. Save me hours :)

Collapse
 
crazyoptimist profile image
crazyoptimist

Perfect!
Thank you.