Search code examples
javascriptnode.jstypescriptexpress

Cannot find module when running Node.js app with PM2 and TypeScript path aliases


I'm encountering an issue when trying to run my ExpressApp application using PM2. The project is written in TypeScript, and I'm using path aliases defined in my tsconfig.json file. The build completes successfully, but when I start the app with PM2, I get the following error:

 Error: Cannot find module '@/config/routes'
 Require stack:
 - /Users/nicolasbispo/projetosgabriel/athenasbackend/build/index.js
     at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1144:15)
     at Function.Module._resolveFilename (/Users/nicolasbispo/projetosgabriel/athenasbackend/node_modules/tsconfig-paths/src/register.ts:115:36)
     at Module.Hook._require.Module.require (/Users/nicolasbispo/.nvm/versions/node/v20.11.1/lib/node_modules/pm2/node_modules/require-in-the-middle/index.js:81:25)
     at require (node:internal/modules/helpers:176:18)
     at Object.<anonymous> (/Users/nicolasbispo/projetosgabriel/athenasbackend/src/index.ts:8:1)
     at Module._compile (node:internal/modules/cjs/loader:1376:14)
     at Object.Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
     at Module.load (node:internal/modules/cjs/loader:1207:32)
     at Function.Module._load (node:internal/modules/cjs/loader:1023:12)
     at Object.<anonymous> (/Users/nicolasbispo/.nvm/versions/node/v20.11.1/lib/node_modules/pm2/lib/ProcessContainerFork.js:33:23) {
   code: 'MODULE_NOT_FOUND',
   requireStack: [
     '/Users/nicolasbispo/projetosgabriel/athenasbackend/build/index.js'
   ]
 }

my tsconfig.json

{
    "compilerOptions": {
        "lib": ["ES2016", "ES2017", "DOM"], //DOM Necessary due to puppeteer package i use in the app
        "target": "ES2017", 
        "module": "commonjs",
        "moduleResolution": "node",
        "outDir": "./build",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "sourceMap": true,
        "esModuleInterop": true,
        "useDefineForClassFields": true,
        "strictNullChecks": true,
        "rootDirs": ["./src"],
        "baseUrl": "./",
        "paths": {
            "@/*": ["./src/*"],
        },
    },
    "include": ["./src/**/*.ts", "./src/**/*.js", ],
    "exclude": ["build", "./node_modules/*"]
}

My src/index.ts file

import "reflect-metadata";

import express, {
    type Express
} from "express";
import "dotenv/config";
import routes from "@/config/routes";
import { SERVER_PORT } from "./config/constants";
import cors from "./middlewares/cors";
import errorMiddleware from "./middlewares/error.middleware";

const app: Express = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(errorMiddleware);
app.use(cors);
app.use(routes);


app.listen(SERVER_PORT, () => {
    console.log(`[server]: Server running at: http://localhost:${SERVER_PORT}`);
});

package.json scripts

"build": "npx tsc --build",
"start": "pm2 start build/index.js --name backendathenas --watch --node-args=\"-r tsconfig-paths/register\"",
"deploy": "pm2 delete backendathenas || true && npm run build && npm run start",

I did a lot of adjusts in tsconfig file, but none of those seems to fix the problem, the problem only occurs when using alias source imports.

I read some solutions to use ts-loader or tsconfig-paths-webpack-plugin but im not that sure if including webpack in my backend app would be the best solution


Solution

  • You are building merely with tsc but tsc does not change the module specifiers as described in tsconfig.json.

    the module specifier (the string from which you import, or pass to require) is always emitted as-written. https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-specifiers-are-not-transformed

    You might need another build tool that changes module specifiers. For instance there is tsc-alias.