Path aliases with TypeScript in Node.js
Lars Wächter

Lars Wächter @larswaechter

About: 25 | CS Student | Software Engineer

Location:
Germany
Joined:
May 30, 2017

Path aliases with TypeScript in Node.js

Publish Date: Feb 6 '19
233 34

This post was originally published on my blog.


Some days ago I included path aliases in my TypeScript Node.js projects. Since they make the code look much cleaner in my opinion I like to show you how to setup these in a project.

The problem

In Node.js (or TS/JS in general) you can import single modules into your code.
This might look the following:

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

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

Noticed these dots ('../') to access upper modules?

The problem we have here is that the deeper your project tree is the more '../' are required to access modules in higher layers. Actually, this doesn't look very beautiful to be honest. Fortunately we can change that.

The solution: path aliases

What are path aliases?

In TypeScript you can avoid these "bad" looking imports with the help of path aliases. With path aliases you can declare aliases that map to a certain absolute path in your application.

Here a quick example:

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

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

In this case our two aliases are

  • @modules that maps to './src/rest/modules'
  • @services that maps to './src/services'

Setup

Let's get into it and setup some path aliases. Note that I won't explain how to setup a TypeScript project in Node.js. I assume that you did this already.

Imagine we have the following project structure:

folder structure
└───src
   │
   └───rest
   │   │
   │   └───modules
   │   │   │
   │   │   └───article
   │   │   │
   │   │   └───user
   │   │
   │   │   server.ts
   │
   │
   └───services
   │   │    cache.ts
   │   │    mongodb.ts
   │    
   │   index.ts
Enter fullscreen mode Exit fullscreen mode

Step 1: Update tsconfig.json

First of all, we have to declare the path aliases in our tsconfig file

"baseUrl": "./src",
"paths": {
    "@modules/*": ["rest/modules/*"],
    "@services/*": ["services/*"]
}
Enter fullscreen mode Exit fullscreen mode

Now, you can use the new path aliases for module imports in your application. There occur any errors in your IDE (in my case VSC) or when you compile the code.

However, we are not done yet. When you try compile the TS code into JS you won't see any errors. But as soon as you run your compiled JS code you will get an error:

For example:

Error: Cannot find module '@modules/user'
Enter fullscreen mode Exit fullscreen mode

That's because JS can't resolve the modules for the declared path aliases.

Step 2: Install module-alias package

Next, we'll install an npm package called module-alias

npm i --save module-alias
Enter fullscreen mode Exit fullscreen mode

This module registers the path aliases in the compiled JS files. Therefor we need to make some changes to our package.json:

"_moduleAliases": {
    "@modules": "dist/rest/modules",
    "@services": "dist/services"
  }
Enter fullscreen mode Exit fullscreen mode

Note that 'dist' is the folder where the compiled JS files are located.

Last but not least we have to register the path aliases in our application.
Add the following line at the top of your startup file:

import 'module-alias/register';
Enter fullscreen mode Exit fullscreen mode

Finally, when you compile and execute the code you shouldn't see any import errors.

Here you can find some examples for path aliases in a side project I'm currently working on.

Comments 34 total

  • Shubham Sinha
    Shubham SinhaFeb 7, 2019

    Does VSC automatic imports work with this?

  • Joe Mainwaring
    Joe MainwaringFeb 7, 2019

    How complex is the folder organization for your source code? I had looked into this but it doesn't have as much value with flatter source-orgs

    • Lars Wächter
      Lars WächterFeb 7, 2019

      The complexity of my folder organization depends on the project. Mostly I have some root folders like: config, rest, services that I declare path aliases for. Inside these I have my components for example.

  • Michał Urbanek
    Michał UrbanekJun 11, 2019

    I found that using extension to import marketplace.visualstudio.com/items... works good enough and not require much logic.

  • jimstack
    jimstackJun 20, 2019

    Thank you! I read several posts about using path aliases and thought it was perfect for my project, but I hit the *cannot find module" issue. I was banging my head off the desk all afternoon. It's been really difficult to find anything on this.

  • Sergey S. Volkov
    Sergey S. VolkovAug 16, 2019

    Thanks!

    Also I find out that if we have webpack in project we just use resolve.alias option:

    
      resolve: {
        extensions: ['.js', '.ts', '.tsx', '.styl'],
        mainFields: ['module', 'browser', 'main'],
        alias: Object.keys(tsconfig.compilerOptions.paths).reduce((aliases, aliasName) => {
    
          aliases[aliasName] = path.resolve(__dirname, `src/${tsconfig.compilerOptions.paths[aliasName][0]}`)
    
          return aliases
        }, {})
      },
    
    Enter fullscreen mode Exit fullscreen mode
  • Ruslan Gonzalez
    Ruslan GonzalezSep 1, 2019

    How cool is that eh! thanks for posting.

  • Marcin Paździora
    Marcin PaździoraNov 11, 2019

    Thanks for the article!

    However, to utilize this solution we have to define the aliases in 2 locations: tsconfig.json and package.json. Is it possible to avoid this duplication?

  • Miguel
    MiguelDec 24, 2019

    Thank you!

  • José Avilez
    José AvilezApr 28, 2020

    Thanks a lot for sharing, I was getting crazy with the issues where modules were not being found.

  • Ayush Nawani
    Ayush NawaniMay 3, 2020

    Thanks for sharing.

  • Ken Snyder
    Ken SnyderMay 25, 2020

    These aliases -- which I've grown used to on the frontend frameworks which use webpack -- are a VERY welcome addition to writing typescript on the backend (or in other library code). My one question comes down to testing. I have a library with hundreds of tests but right now none of them run because I'm using Mocha/Chai with ts-node and I'm not sure but I think that ts-node is not able to use the alias.

    The command I use is:

    ./node_modules/.bin/mocha --no-timeouts --require ts-node/register --exit
    

    This is a pretty standard way of testing as it allows you to test directly on the source rather than needing to transpile before testing.

  • Dainius Stepulevicius
    Dainius StepuleviciusJun 11, 2020

    Hey,

    I have followed your tutorial by the letter and even restarted code editor but keep getting the error, no matter what I do

  • Darren
    DarrenJun 12, 2020

    Had an issue with zeit/pkg because the generated files (in the dist folder) still had the @/dir/to/your/file references, which pkg could not handle.

    In case you need to change your js files from the @/your-file back into their ../../../your-file form, you can use ef-tspm to bring it back. Note, if you do so, you won't need to deal with the extra steps for the module-alias specified above. Trade-off is you have an additional build step. So first you would tsc to build the typescript code, then ef-tspm to properly remove the module aliases.

  • Dave Stewart
    Dave StewartAug 25, 2020

    I've just released a new package Alias HQ, which allows you to reuse your js/tsconfig.json path aliases in Webpack, Jest, Rollup, or any other library.

    Just call hq.get('<library name>') in the config file you want to use aliases in, and you're done:

    github.com/davestewart/alias-hq

  • Deschant Kounou
    Deschant KounouSep 23, 2020

    I couldn't get the aliases working without first building the project since they are registered using the dist folder. Feels hacky. Is there a way around this ?

  • Lu-Vuong Le 🚀
    Lu-Vuong Le 🚀Oct 6, 2020

    For anyone reading this, when updating the tsconfig file, you'll need to add the "baseUrl" and "paths" options inside the "compilerOptions" object :)

  • Evgeniy Troynov
    Evgeniy TroynovNov 7, 2020

    You can use:
    npmjs.com/package/tsconfig-paths

    it much easier

    • itsikbelson-spotlight
      itsikbelson-spotlightJun 20, 2023

      I also kept receiving module_not_found while running ts-node.
      The way that worked for me (taken from stackoverflow.com/questions/566507...

      In tsconfig.json add the following section:

      "ts-node": {
            "require": ["tsconfig-paths/register"]
      },
      
      Enter fullscreen mode Exit fullscreen mode

      In order to make the script run on the compiled js (for production distribution), you can defined the following script in package.json:

      "scripts": {
            "start": "TS_NODE_BASEURL=./dist node -r tsconfig-paths/register dist/index.js"
      }
      
      Enter fullscreen mode Exit fullscreen mode

      Don't forget to run npm i tsconfig-paths

    • empflow
      empflowOct 5, 2023

      Thank you so much! I've probably spent like 6 hours in total trying to get this to work and I finally see the listening on port 3000 log 😁

    • Vital
      VitalDec 11, 2023

      you made my day, thx!

  • Stanislav Yeshchenko
    Stanislav YeshchenkoNov 21, 2020

    Did you have a situation where shared folder has its own package.json with node_modules?

    During compilation node_modules are not included in the dist folder, and the compiler is complaining about missing npm modules from the shared

  • DeVoresyah ArEst
    DeVoresyah ArEstJul 29, 2021

    hi, thanks for your sharing. I've followed step-by-step from the article, but however I can't click to navigate to the path when using alias in my vscode. Normally when I click the path, it goes directly to the path... can you help me with this case? I'm not using typescript, so I can't add tsconfig

  • liyaxuanliyaxuan
    liyaxuanliyaxuanMar 23, 2022

    marked, thanks!

  • Isaac Pak
    Isaac PakJul 16, 2022

    I don't get any errors and I don't have to install any npm packages...

  • Ram Tobolski
    Ram TobolskiSep 5, 2023

    Hello.
    I don't understand how this solution is supposed to work in production.
    We don't have a package.json file in production. So how the aliases will be recognized?

    • Lars Wächter
      Lars WächterSep 15, 2023

      The path aliases only play a role during development since the TS code is compiled to JS.

  • Henrique Maximo Lima
    Henrique Maximo LimaNov 26, 2024

    for who wants using @/* at importings, only insert:

    """
    "baseUrl": "./",
    "paths": {
    "@/": ["./src/"]
    }

    """
    Example: import { authenticateFromGithubCode } from "@/use-cases/authenticate-from-github-code";

    • Sanchit Bajaj
      Sanchit BajajMay 18, 2025

      I have setup of this with tsconfig-paths but I am getting module not found error when I am running my dist file. Do you have any idea?

  • toykam
    toykamJan 29, 2025

    Thank you for sharing this, it was soo helpful.

Add comment