Deploying a full web service on EC2 using Docker (1) Lightweight Deployment of Nest.js
Tags: nestjs, webdev, ec2, node
Published: 2024-03-07 14:05:22

**Note: This is an article series on deploying web projects on AWS, including:**

1. Lightweight construction of backend Nest.js through Docker deployment of the entire web application service (1).
2. Lightweight construction of frontend Next.js through Docker deployment of the entire web application service (2).
3. Setting up an AWS EC2 server through Docker deployment of the entire web application service (3).
4. Deploying frontend and backend services through Docker-compose in Docker deployment of the entire web application service (4).
5. Distributing API routes and hosting Next.js static resources through Nginx in Docker deployment of the entire web application service (5).
6. Automating CI/CD deployment through Git Action in Docker deployment of the entire web application service (6).

**1、Introduce**

As we all know, Nest.js by default only compiles TypeScript files instead of bundling all resources into bundles and chunks like in frontend projects. For example, to run the build command in the terminal: “npm run build”.

Comparing the compiled `dist` directory with the source code, we can see that Nest.js simply converts TypeScript files to JavaScript.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/07mavasg3shunpzzh7c8.png)

The compiled JavaScript source code only contains your business logic. If you upload them to a server and try to run them, you will encounter errors because the JS business code does not include the `@nestjs/xxx` related code.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5isbtlls0po6905go5u.png)

This means that on the server, you still need to install the dependencies and devDependencies specified in your `package.json` using `pnpm i` or `npm i`. However, installing dependencies with `npm i` can be very disk-intensive. If your server has limited memory, you may encounter disk space issues during dependency installation. This is because the `node_modules` folder contains a lot of unused code without tree-shaking from bundling tools like `webpack`, `turbopack`, or `rollup`, leading to disk space waste.

The best practice for lightweight deployment is to use `webpack` in your `CI/CD` pipeline to bundle your code and then deploy only the bundle to the server.

**2、How to implement this:**

**2.1、You can enable webpack bundling by modifying `nest-cli.json`.**

```json
{
  "compilerOptions": {
    "deleteOutDir": true,
    "webpack": true //enable webpack
  }
}
```

After modifying `nest-cli.json` and bundling again, although it has been bundled into a single `bundle` (as shown in the screenshot below), the source code of `@nestjs/core` and other dependencies is still not included in the `bundle`. Instead, they are transformed into the `commonjs` format used by Node.js. The bundling process only applies to your project’s business logic. In other words, this code still cannot be directly run on the server. You still need to run `pnpm i` to install dependencies before you can run it.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4zg52d1tck8k2p7fvy96.png)

**2.2. Customize webpack.config.js to implement packaging**

1、In order to address the issue mentioned above, which is the failure to include dependencies like `@nestjs/core` in the bundle, we need to create a `webpack.config.js` file in the root directory and utilize `webpack.IgnorePlugin` to achieve this.

```javascript
const path = require('path');
const webpack = require('webpack');
// Improving TypeScript build performance
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// Note: These libraries do not need to be bundled into the bundle. 
// Exclude them using `webpack.IgnorePlugin` as shown below.
const lazyImports = [
  '@nestjs/microservices',
  '@nestjs/microservices/microservices-module',
  '@nestjs/websockets/socket-module',
  'cache-manager',
  'class-validator',
  'class-transformer'
];

module.exports = {
  entry: './src/main',
  output: {
    // output name
    path: path.resolve(__dirname, 'nest-blog'),
    // output filename
    filename: 'main.js'
  },
  target: 'node',
  // Setting it to empty will ignore the `webpack-node-externals` plugin.
  externals: {},
  // Handling TypeScript files
  module: {
    rules: [
      {
        test: /\.ts?$/,
        use: {
          loader: 'ts-loader',
          options: { transpileOnly: true }
        },
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.ts', '.json']
  },
  plugins: [
    new webpack.IgnorePlugin({
      checkResource(resource) {
        if (!lazyImports.includes(resource)) {
          return false;
        }
        try {
          require.resolve(resource, {
            paths: [process.cwd()]
          });
        } catch (err) {
          return true;
        }
        return false;
      }
    }),
    new ForkTsCheckerWebpackPlugin()
  ]
};
```

2、Then add a new script in `package.json` to make Nest use the above `webpack.config.js` configuration when building.

```json
"scripts": {
    "build:bundle": "NODE_ENV=production && nest build --webpack --  webpackPath=./webpack.config.js",
    .....
},
```

**3、Run `pnpm build:bundle` again.**

Finally, a bundle file `nest-blog/main.js` will be generated. This `main.js` no longer has dependencies like `@nestjs/core`; all related dependencies are bundled into this `main.js`. The remaining dependencies are Node.js native modules, so it can be directly run in a Node.js environment. Now you can simply upload this bundle to the server and run it without installing any dependencies.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ej6o43sfs8qwtbv7pg76.png)

**4. Deployment**

After uploading /nest-blog/main.js` to the server, you only need to run the following command in the terminal to start the Nest.js service.

```shell
node ./nest-blog/main.js
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apxxeygzttfogotwtzvf.png)


5、Finally

Comparing with traditional deployment, Nest.js lightweight deployment only requires deploying a single bundle file. You don’t need to install a large number of dependencies using `pnpm i` on the server. This approach saves disk space and is much more convenient.

_Next Article： Lightweight construction of frontend Next.js through Docker deployment of the entire web application service (2)._