How to deploy nodeJS app to production

Releasing javascript/typescript applications normally ends up with producing a huge artifact, the reason is obviously the node_modules. Here I am showing you how to deploy a NodeJS app to production with very small artifact and very efficient deploy time.

node modules

When you create a modern javascript / typescript project you are taking care of the dependencies using package managers like npm or yarn. On npm package json lock version 1 or 2 , I told you about your project’s dependencies and dependencies of dependencies. All of these dependencies are libraries that reside inside node_modules folder. You actually don’t use all of these files, normally only a small portion of these library files are actually used in your application. The node_modules tends to grow large as you add dependencies. Some of these dependencies are dev-only dependencies (like unit test libraries) and you don’t need them on the production server.

A good solution would be to only pick up parts of the libraries you need and ship it with your artifact. Lets see how we can do it.

Compile a Node.js project into a single file

You can compile a Node.js application into a single file, together with all its dependencies. When it comes to React.js project it is already possible using the build command, you can read more on ReactJS Release CICD for azure devops Pipeline and GitHub actions.

For pure Node.js applications or modules,on the other hand, you can use @vercel/ncc tool. Via ncc you can create your own build script command! Although you can install ncc globally with -g flag, I like to install it as a dev dependency. This way on automated pipelines you always use the same version of ncc.

npm i -D  @vercel/ncc 

Then you can add a build command script to package.json like this

"scripts": {
    ...,
    "build": "ncc build index.js -o dist"
  },

In this case index.js is the start point of your application, and dist is the folder you want your compiled files endup in. Then, you can run command below to build your solution.

npm run build

In result, it creates a dist folder containing a single compiled file that contains all your application code and dependencies from node_moduels. If you want to run the compiled app locally, you can run

node ./dist/index.js

Please note that if you install ncc globally (using -g flag), you can simply run ncc command without any npm or npx

ncc build index.js -o dist

Read more on the ncc github page

Compile typescript with ncc

For typescript projects, you just need to point to index.ts instead of index.js the ncc tool is smart enough to do the rest!

Deploy a NodeJS app to production

As long as you know how to make the single bundle file of your project (that is very light weight compared to all your app plus the node modules folder) the deployment is really easy. You can put this file on a docker container and push it to container registry or you can make an artifact and push it to a serverless app or even a linux or windows server.

Faster deployments is the biggest advantage of compiling: deploying a rather small Node.js app with all node modules can take like 40 minutes – from github to azure app services. When you compile it and deploy a single file the whole workflow runs less than 3 minutes!

This way you can select node as runtime for the azure app services and run the app simply with the command

node index.js

Here is an example action workflow to create an artifact on github using a the compiled file (note that you need to define build script as described above):

name: Build and deploy Node.js app to Azure Web App on GitHub

on:
  push:
    branches:
      - main
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js version
      - uses: actions/setup-node@v4
        with:
           node-version: 20
           cache: 'npm'
      - name: build the artifact
        shell: bash
        run: |
          npm  ci
          npm run build

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v3
        with:
          name: nodejs-app
          path: dist/* 
          
  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: "Production"
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v3
        with:
          name: nodejs-app

      - name: "Deploy to Azure Web App"
        id: deploy-to-app-services
        uses: azure/webapps-deploy@v3
        with:
          app-name: "nodejs-app"
          slot-name: "Production"
          publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_XXXXXXXXX }}
          package: .

Not that here we deployed the webapp using secret for publish profile, there is a better way to deploy to azure Resources using OIDC, to learn more please read GitHub Federated integration: Azure Key Vault & ACR- Example


Posted

in

,

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *