Skip to content

Node.js is a JavaScript runtime for building servers, CLIs, and backend services. Nx scales your Node monorepo with caching, distributed task execution, and affected-only builds.

The Node plugin contains generators and executors to manage Node applications within an Nx workspace.

Below is a reference table that matches the most recent major versions of Nx to the versions of Node.js that they officially support, and are tested against.

The Nx policy is to support the LTS versions (i.e. actively maintained even numbered versions) of Node.js, but we will only remove support for older versions in a major version of Nx to avoid unexpected disruption. We may add support for newer LTS versions in a minor version of Nx as long as it would not break existing projects.

Nx VersionNode Version
23.x (current)26.x, 24.x, ^22.12.0
22.x26.x, 24.x, ^22.12.0, ^20.19.0
21.x24.x, ^22.12.0, ^20.19.0
20.x22.x, 20.x, 18.x

We intentionally do not include an "engines" field in the package.json file for Nx in order to allow for user flexibility, but this table should be considered the official compatibility matrix.

This table will be updated from time to time to reflect the latest versions of Node.js that are supported. If you encounter issues with Nx, please make sure you are using a supported version of Node.js before filing an issue.

In any Nx workspace, you can install @nx/node by running the following command:

Terminal window
nx add @nx/node

This will install the correct version of @nx/node.

You can add a new application with the following:

Terminal window
nx g @nx/node:application apps/my-new-app

You can run your application with nx serve my-new-app, which starts it in watch mode.

The serve task comes from the first two lines of src/main.ts:

#!/usr/bin/env node
// @nx-target-name: serve

@nx/js/node picks up files whose shebang names node, and @nx-target-name sets the task name. The generator adds that plugin to nx.json unless a registration already exists. The plugin runs scripts in watch mode by default, so nx serve runs the file with node --watch and restarts it on changes without a build step. Set watch: false in the plugin options to turn watch off for every script the registration covers, serve included. Workspace libraries the application depends on are built first, through dependsOn on the serve entry in the project configuration. Node runs the TypeScript source directly, which requires Node.js 22.18 or later. These applications are ES modules, with "type": "module" in their package.json, so relative imports name the .ts file (./message.ts). The build rewrites them to .js.

In workspaces that use TypeScript path aliases, and for esbuild, Jest, or JavaScript (--js) applications, the shebang names tsx instead, which resolves path aliases and extensionless imports. Applications that use webpack, or workspaces with inference plugins turned off, get an explicit serve target in project.json instead.

Node libraries are a good way to separate features within your organization. To create a Node library run the following command:

Terminal window
nx g @nx/node:lib libs/my-new-lib
# If you want the library to be buildable or publishable to npm
nx g @nx/node:lib libs/my-new-lib --buildable
nx g @nx/node:lib libs/my-new-lib \
--publishable \
--importPath=@myorg/my-new-lib

You can run unit tests with:

Terminal window
nx test my-new-app
nx test my-new-lib

Replace my-new-app with the name or your project. This command works for both applications and libraries.

Node applications can be build with:

Terminal window
nx build my-new-app

And if you generated a library with --buildable, then you can build a library as well:

Terminal window
nx build my-new-lib

Applications build with tsc in TypeScript workspaces that use project references, and with Rolldown in workspaces that use TypeScript path aliases. Fastify applications use esbuild there instead. Pass --bundler when generating the application to choose tsc, rolldown, esbuild, or webpack. The tsc bundler needs project references.

The output is in a dist folder. To change it, set outDir in tsconfig.app.json for tsc, or output.dir in rolldown.config.ts for Rolldown.

Generating Node applications has an option to configure other projects in the workspace to proxy API requests. This can be done by passing the --frontendProject with the project name you wish to enable proxy support for.

Terminal window
nx g @nx/node:application apps/my-new-app \
--frontendProject my-react-app

Tasks that depend on serve with waitFor: "ready", such as Playwright and Cypress e2e tasks, start once the server reports that it's listening. Applications generated with the Express, Fastify, Koa, or NestJS framework report it. Their src/main.ts calls emitReadySignal from @nx/js/ready-signal once the server listens, and their serve entry sets "readyWhen": { "signal": true }.

To add this to an existing server, check that the root package.json lists @nx/js, so the server can import it. If it doesn't, add @nx/js at the same version as nx:

Terminal window
npm install -D @nx/js@<nx-version>

Call emitReadySignal with the server once it listens:

const server = app.listen(port, host, async () => {
// Set when Nx waits for this server to be ready
if (process.env.NX_READY_SIGNAL_SOCKET) {
try {
// Function keeps TypeScript and bundlers from rewriting or bundling the import
const load = new Function('s', 'return import(s)') as (
s: string
) => Promise<{ emitReadySignal(server: unknown): boolean }>;
(await load('@nx/js/ready-signal')).emitReadySignal(server);
} catch {
// @nx/js isn't installed
}
}
});

Then set readyWhen on the serve entry in the project configuration:

"serve": {
"readyWhen": { "signal": true }
}

emitReadySignal reads the address from the server and passes it to the waiting tasks as NX_READY_URL. Without readyWhen, a task that waits for serve starts as soon as serve starts. For why the import is guarded, and for the other checks, see wait for a continuous task to be ready.

When generating Node applications, Nx automatically creates a VSCode debugging configuration:

  • Automatic setup: A .vscode/launch.json file is created with pre-configured debugging settings.
  • Smart port allocation: Debug ports are automatically assigned starting from 9229, preventing conflicts between multiple applications.
  • Source map support: Webpack configurations include devtoolModuleFilenameTemplate for accurate source mapping.

To debug your Node application in VSCode:

  1. Open your workspace in VSCode.
  2. Set breakpoints in your TypeScript source files.
  3. Go to the Debug panel (Ctrl+Shift+D).
  4. Select "Debug {your-app-name} with Nx" from the dropdown.
  5. Click the play button or press F5.

The debugger will automatically:

  • Start your application with nx serve.
  • Attach to the Node.js debugger on the allocated port.
  • Stop on breakpoints in your TypeScript source files, which nx serve runs directly.

To open the debugger on a specific port, add Node's --inspect flag to the shebang in src/main.ts, after the existing node or tsx:

#!/usr/bin/env -S node --inspect=9230

If the application has an explicit serve target in project.json, set its port option or run nx serve my-new-app --port <number> instead.

For additional information on Node.js debugging, see the Node.js debugging getting started guide.

In CI, Nx runs nx affected to rebuild and retest only the projects a change touches, and caches results to skip repeated work.

For a complete pipeline, see Set up CI.