How to generate a production build of an API done with NESTJS
Asked Answered
H

6

29

I am generating the production version of an API I made using the NESTJS framework and would like to know which files I should upload to the server. When I run the "npm run start: prod" compile it generates the "dist" folder but I tried to run only with it but it is not enough to run my application. Do I need to upload all files to the server? I did several tests removing the folders I used during development but only managed to run in production mode when I was all the same in dev mode.

I looked in the documentation for something about this but found nothing. can anybody help me?

Thank you

Heartbroken answered 17/2, 2019 at 20:4 Comment(0)
O
17

Honestly, you should only really need the dist folder as that's the JS 'complied' files. To run your application, commonly you'd use this command node dist/main.js. As to what files you upload it's up to you. Me personally, I use a lot of continuous integration so I would just clone to repo into my container/server and use yarn start:prod. This is so everytime I deploy I'm generating the required files to run in a production environment.

Like @Kim Kern mentioned, some node modules are native built using node-gyro; so it's also always best to build your node_modules on the server/container when deploying. Your deployment script should look something like this

git clone [email protected]:myuser/myrepo.git /var/www/
cd /var/www/
node -v && \
yarn && \
yarn build && \
yarn start:prod

The above script should

1) pull the required repo into a 'hosted' directory
2) check the node version
3) install node_modules and build native scripts etc
4) build the production distribution
5) run the production JS scripts

If you look in your package.json file you'll notice the different scripts that are run when you use yarn start, yarn start:dev and yarn start:prod. When in dev you'll notice the use of ts-node which is a typescript node runner type thing (can't remember the correct phrase). Also the start:dev script uses nodemode to restart the ts-node script. You'll also see the start:prod script uses node dist/main.js and that the prestart:prod script runs rm -rf dist && tsc which removes the dist folder and 'compiles' the javascript required for a production environment.

However, the drawback of a typescript application on your server without continuous integration is that there is the possibility of typescript compilation errors which you wouldn't see or know about until running the prod scripts. I would recommend putting a procedure in place to compile the javascipt from typescript before making a deployment as you don't want to delete the current dist build before knowing the next release will build and run!

Oblivious answered 18/2, 2019 at 10:21 Comment(1)
Another drawback of building on the server is that it takes tons and tons of memory for Webpack to finish the Typescript type checking. For example Node.js runs fine on a AWS t2.micro with 1GB ram, but when building a node app even a 2gb ram server crashes. You could use transpileOnly: true in webpack config. Then the app builds in seconds but you haven o type checking.Korney
E
19

For me this approach worked and all you need is the dist folder for this:

  • Create a prod build of your application using npm run start:prod, this would create a dist folder within your application source
  • Copy the dist folder to your server.
  • For getting all the node_modules dependencies on your server just copy your package.json file into the dist folder (that you have copied onto the server) and then run npm install from there.
  • If you are using pm2 to run your node applications just run pm2 start main.js from within the dist folder
Endora answered 1/10, 2019 at 8:2 Comment(4)
Why do you need to copy the package json into the dist folder? You can run npm install from root and run your app in the dist folder from root, too.Korney
Hi @Korney of-course you are correct, your approach works if you don't mind having your source code also on the server. But in situations where you need bundle only the dist files onto your servers the approach you mentioned might not be a viable solution. Thats why you need to have the package.json install your node_modules for you to run your application using your dist files. I hope that helps.Endora
I believe, dist folder doesn't contain machine specific code, Correct?Jonasjonathan
pm2 + dist is cleanTippets
O
17

Honestly, you should only really need the dist folder as that's the JS 'complied' files. To run your application, commonly you'd use this command node dist/main.js. As to what files you upload it's up to you. Me personally, I use a lot of continuous integration so I would just clone to repo into my container/server and use yarn start:prod. This is so everytime I deploy I'm generating the required files to run in a production environment.

Like @Kim Kern mentioned, some node modules are native built using node-gyro; so it's also always best to build your node_modules on the server/container when deploying. Your deployment script should look something like this

git clone [email protected]:myuser/myrepo.git /var/www/
cd /var/www/
node -v && \
yarn && \
yarn build && \
yarn start:prod

The above script should

1) pull the required repo into a 'hosted' directory
2) check the node version
3) install node_modules and build native scripts etc
4) build the production distribution
5) run the production JS scripts

If you look in your package.json file you'll notice the different scripts that are run when you use yarn start, yarn start:dev and yarn start:prod. When in dev you'll notice the use of ts-node which is a typescript node runner type thing (can't remember the correct phrase). Also the start:dev script uses nodemode to restart the ts-node script. You'll also see the start:prod script uses node dist/main.js and that the prestart:prod script runs rm -rf dist && tsc which removes the dist folder and 'compiles' the javascript required for a production environment.

However, the drawback of a typescript application on your server without continuous integration is that there is the possibility of typescript compilation errors which you wouldn't see or know about until running the prod scripts. I would recommend putting a procedure in place to compile the javascipt from typescript before making a deployment as you don't want to delete the current dist build before knowing the next release will build and run!

Oblivious answered 18/2, 2019 at 10:21 Comment(1)
Another drawback of building on the server is that it takes tons and tons of memory for Webpack to finish the Typescript type checking. For example Node.js runs fine on a AWS t2.micro with 1GB ram, but when building a node app even a 2gb ram server crashes. You could use transpileOnly: true in webpack config. Then the app builds in seconds but you haven o type checking.Korney
M
8

@edit Just a short comment that came to my mind when getting back to this answer: Typically you would use PM2 as process manager for NodeJS applications in production. You then just create an ecosystem.config.json file and use this to startup all your processes. An example will follow soon!

Just use the Nest-CLI and build with

nest build

Afterwards you get a dist folder with the compiled Code. You can then place it on a server an run e.g. with PM2 proccess manager:

production=true pm2 start dist/main.js

In former command the environment variable production is set to true. That could e.g. be usefull when running the Nest.js server over HTTPS.

If you want to run a HTTPS secured server you also have to include the certificates in the starting process of the server. When the environment variable production is set and true the certificates get included in the starting proccess of the Nest.js application in main.ts like following:

async function bootstrap() {
let appConfig = {}
if (process.env.production) {
    console.log('process env production: ', process.env.production)
    const httpsOptions = {
        key: fs.readFileSync('/etc/certs/letsencrypt/live/testtest.de/privkey.pem'),
        cert: fs.readFileSync('/etc/certs/letsencrypt/live/testtest.de/fullchain.pem'),
    }
    
    // prod config
    appConfig = {
        httpsOptions,
    }
}

const app = await NestFactory.create<NestExpressApplication>(
    AppModule,
    appConfig,
)

app.enableCors()
app.setGlobalPrefix('v1')

await app.listen(3300)
}
bootstrap()
Mackintosh answered 21/7, 2020 at 10:35 Comment(0)
A
6

Mostly, you will only need the dependencies in node_modules. You should build the libraries on your server though instead of copying them from your dev machine. Libraries like bcrypt have machine specific code and probably won't run on a different machine. (30% of the npm libraries have native bindings.)

So for your deployment I would recommend to checkout your git repository on your server and then just run npm run start:prod (which builds the project every time) directly there.

Antibaryon answered 17/2, 2019 at 20:36 Comment(2)
omg. After 6 hours I saw your "machine specific" phrase. I wish I knew that. Thanks!Thrash
A drawback of building on the server is that it takes tons and tons of memory for Webpack to finish the Typescript type checking. For example Node.js runs fine on a AWS t2.micro with 1GB ram, but when building a node app even a 2gb ram server crashes. You could use transpileOnly: true in webpack config. Then the app builds in seconds but you haven o type checking. The best option might be to build the javascript before deployment but run npm/yarn install on the server?Korney
C
5

We don't build our application on production, but instead build it when creating our docker container.

The steps for us roughly are:

  1. Run npm install and whatever tooling you need to build the application.
  2. Create docker container and copy dist/, node_modules and package.json
  3. Inside the docker container run npm rebuild bcrypt --update-binary
Confidence answered 24/4, 2019 at 14:33 Comment(2)
This seems smarter. You don't want the production build to end up with slightly different modules after the build passed on CI. Production builds should be EXACTLY what was built in CI, with no code downloaded from the internet to production.Ullyot
@MarkStosberg isn't it a problem that the code has been built in a different environment than the one it'll run in?Mispleading
S
0

We are using NX for monorepo where we hold our API's. And we use docker for our images and containers. When we have to create docker image, only run: npx nx build <project> and this generate build on dist/apps/<project>. This folder goes to the docker image, with the package.json and that's it. You don't need to add node_modules, because they are on the package.json. Just be sure to include npm install on your Dockerfile.

Semifinalist answered 15/10, 2020 at 22:21 Comment(1)
The caution with npm install is it doesn't actually install exactly the version defined in the package.json unless you pin versions. It's always better to run npm ci --production and copy the node_modules into the image - this will guarantee consistent versions. npm install where you have "lib": "^1.0.0" will upgrade "lib" to v1.0.1 and i've had mongoose introduce breaking changes on patch versions.Kilian

© 2022 - 2024 — McMap. All rights reserved.