NestJS Logo

Hot Reload

TypeScript compilation has the biggest impact on your application's bootstrapping time. With webpack HMR (Hot-Module Replacement), you don't need to recompile the entire project each time a change occurs. This significantly decreases the time it takes to instantiate your application and makes iterative development much faster.

Warningwebpack doesn't automatically copy your assets (e.g., graphql files) to the dist folder. Similarly, webpack isn't compatible with static glob paths (e.g., the entities property in TypeOrmModule).
Warning As of NestJS v12, the webpack builder is deprecated and Rspack is the default bundler for monorepos. This recipe targets webpack-based CommonJS projects, which is why the examples below use module.hot and a plain bootstrap() call rather than the ESM top-level await used elsewhere in these docs. For new projects, prefer --builder rspack.

With CLI

If you use the Nest CLI, the configuration process is straightforward. The CLI wraps webpack, which lets you use the HotModuleReplacementPlugin.

Installation#

First, install the required packages:


$ npm i --save-dev webpack-node-externals run-script-webpack-plugin webpack
Hint If you use Yarn Berry (not classic Yarn), install the webpack-pnp-externals package instead of webpack-node-externals.

Configuration#

Once the installation is complete, create a webpack-hmr.config.js file in the root directory of your application:


const nodeExternals = require('webpack-node-externals');
const { RunScriptWebpackPlugin } = require('run-script-webpack-plugin');

module.exports = function (options, webpack) {
  return {
    ...options,
    entry: ['webpack/hot/poll?100', options.entry],
    externals: [
      nodeExternals({
        allowlist: ['webpack/hot/poll?100'],
      }),
    ],
    plugins: [
      ...options.plugins,
      new webpack.HotModuleReplacementPlugin(),
      new webpack.WatchIgnorePlugin({
        paths: [/\.js$/, /\.d\.ts$/],
      }),
      new RunScriptWebpackPlugin({ name: options.output.filename, autoRestart: false }),
    ],
  };
};
Hint With Yarn Berry (not classic Yarn), instead of using nodeExternals in the externals configuration property, use WebpackPnpExternals from the webpack-pnp-externals package: WebpackPnpExternals({ exclude: ['webpack/hot/poll?100'] }).

This function takes the original object containing the default webpack configuration as its first argument, and a reference to the underlying webpack package used by the Nest CLI as its second argument. It returns a modified webpack configuration with the HotModuleReplacementPlugin, WatchIgnorePlugin, and RunScriptWebpackPlugin plugins.

Hot-Module Replacement#

To enable HMR, open the application entry file (main.ts) and add the following webpack-related instructions:


declare const module: any;

async function bootstrap() {
  if (module.hot?.data?.closePromise) {
    // wait for the previous application instance to fully shut down
    await module.hot.data.closePromise;
  }

  const app = await NestFactory.create(AppModule, {
    forceCloseConnections: !!module.hot,
  });
  await app.listen(process.env.PORT ?? 3000);

  if (module.hot) {
    module.hot.accept();
    module.hot.dispose((data: any) => {
      data.closePromise = app.close();
    });
  }
}
bootstrap();
Hintapp.close() is asynchronous, but webpack does not await the dispose() callback. Stashing the returned promise on module.hot.data lets the next application instance await it before binding to the port again, which (together with forceCloseConnections) prevents EADDRINUSE errors on reload.

To simplify the execution process, add a script to your package.json file:


"start:dev": "nest build --webpack --webpackPath webpack-hmr.config.js --watch"

Then run the following command:


$ npm run start:dev

Without CLI

If you don't use the Nest CLI, the configuration is slightly more complex and requires more manual steps.

Installation#

First, install the required packages:


$ npm i --save-dev webpack webpack-cli webpack-node-externals ts-loader run-script-webpack-plugin
Hint If you use Yarn Berry (not classic Yarn), install the webpack-pnp-externals package instead of webpack-node-externals.

Configuration#

Once the installation is complete, create a webpack.config.js file in the root directory of your application:


const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const { RunScriptWebpackPlugin } = require('run-script-webpack-plugin');

module.exports = {
  entry: ['webpack/hot/poll?100', './src/main.ts'],
  target: 'node',
  externals: [
    nodeExternals({
      allowlist: ['webpack/hot/poll?100'],
    }),
  ],
  module: {
    rules: [
      {
        test: /.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  mode: 'development',
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  plugins: [new webpack.HotModuleReplacementPlugin(), new RunScriptWebpackPlugin({ name: 'server.js', autoRestart: false })],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'server.js',
  },
};
Hint With Yarn Berry (not classic Yarn), instead of using nodeExternals in the externals configuration property, use WebpackPnpExternals from the webpack-pnp-externals package: WebpackPnpExternals({ exclude: ['webpack/hot/poll?100'] }).

This configuration tells webpack a few essential things about your application: the location of the entry file, the directory that holds the compiled files, and the loader used to compile source files. You can generally use this file as-is, even if you don't fully understand all of the options.

Hot-Module Replacement#

To enable HMR, open the application entry file (main.ts) and add the following webpack-related instructions:


declare const module: any;

async function bootstrap() {
  if (module.hot?.data?.closePromise) {
    // wait for the previous application instance to fully shut down
    await module.hot.data.closePromise;
  }

  const app = await NestFactory.create(AppModule, {
    forceCloseConnections: !!module.hot,
  });
  await app.listen(process.env.PORT ?? 3000);

  if (module.hot) {
    module.hot.accept();
    module.hot.dispose((data: any) => {
      data.closePromise = app.close();
    });
  }
}
bootstrap();
Hintapp.close() is asynchronous, but webpack does not await the dispose() callback. Stashing the returned promise on module.hot.data lets the next application instance await it before binding to the port again, which (together with forceCloseConnections) prevents EADDRINUSE errors on reload.

To simplify the execution process, add a script to your package.json file:


"start:dev": "webpack --config webpack.config.js --watch"

Then run the following command:


$ npm run start:dev

Example#

A working example is available in the webpack sample on GitHub.

Edit on GitHub

Support us

Nest is an MIT-licensed open source project. It can grow thanks to the support of these awesome people. If you'd like to join them, please read more here.

Principal Sponsors

SerpApi LogoTrilon LogoMojam Logo

Sponsors / Partners

Become a sponsor