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';
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/*"]
}
...
}
}
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'
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.
- Using webpack
- Using Module Aliases package
- Bootstraping tsc with explicit params
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,
});
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
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
Oldest comments (13)
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
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:
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:
Things do NOT work when importing a service from another service file that is within the same directory. Example
The error that I get when doing the above is:
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:
Environment
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
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
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
Thanks, this helps me better.
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
Any time.i had the same trouble so I wrote this article for those in need
The first solution worked for me like this:
thanks for sharing
Please how can I get this working on test? I'm still getting the error when running unit/integration tests
Good job. Save me hours :)
Perfect!
Thank you.
is this the only way to do this ??
i want to remove ../../../../../ stuff in my code !!