Lets’s learn how to dockerize Node.js application, which may be written in JavaScript or TypeScript.
Docker has been a backbone on the deployment side since it got rise in popularity in 2013, along with the rise in a microservice architecture. Deployment of most of the application backends and frontends happens through docker containers. It helps not only in deployment but also, during the development phase as it follows the concept of build once run anywhere. Since the applications running in the docker containers run in isolation. They do not depend on the host computer’s environment dependencies as long as docker is present on the host computer.
We are looking into how to dockerize node.js written in JavaScript and TypeScript, which uses the Express framework.
Prerequisites
Dockerize Node.js Application
Dockerize Node.js JavaScript
Let’s create the Node.js project, create a simple route and edit the package.json
file.
## create a node project
$ npm init -y
## create the source file and starting point for the project
$ mkdir src && touch src/index.js
## installing the dependencies
$ npm install express
## Installing dev dependencies
$ npm install --save-dev nodemon
Edit the index.js
file
// index.js
const express = require("express");
const app = express();
app.get('/', (req, res)=> {
res.send("OK")
})
app.listen(3000, () => console.log("app start in the port 3000"));
Edit the package.json
{
"name": "javascript",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "npm install --omit=dev",
"start": "node src/index.js",
"dev": "nodemon src/index.js"
},
"keywords": [],
"author": "shuvam",
"license": "ISC",
"dependencies": {
"express": "^4.18.1"
},
"devDependencies": {
"nodemon": "^2.0.19"
}
}
The above package.json file has an additional build that only installs dependencies, and ignores the dev dependencies. The start script starts the application, and the dev starts the application with hot reload enabled.
Docker file for Javascript Node.js
We will create a docker file and instruct docker. On what the application requires to build and run this application.
$ touch Dockerfile
# Dockerfile
FROM node:16-alpine # 1
WORKDIR /app # 2
COPY package.json . # 3
RUN npm run build # 4
COPY src ./src # 5
EXPOSE 3000 # 6
CMD ["npm", "run", "start"] # 7
In docker file #
is used for comment, in the above docker file the hashtags tell the process. Let’s see what each process does.
- 1: It tells the docker engine what docker base image to use, in this it tells to use alpine with node version 16
- 2: It tells the working point for the application, and where the application file should be present
- 3: It copies the package.json from the filesystem to the workdir
- 4: It runs the
npm run build
script that we defined in the package.json file - 5: It copies everything from the src folder from the filesystem to the workdir location
- 6: It exposes the 3000 port of the docker container
- 7: The container will run the ‘npm run build’ script that we gave on the package.json file when the container starts
Running the application
Build the docker container
$ docker build -t scanskill-node-js .
Running the application
$ docker run -p 3000:3000 scanskill-node-js:latest
Dockerize Node.js TypeScript
Let’s initialize a typescript-based Node.js project, create a simple route and edit the package.json
file.
## create a node project
$ npm init -y
## create the source file and starting point for the project
$ mkdir src && touch src/index.ts
## installing the dependencies
$ npm install express
## installing dev dependencies
$ npm install --save-dev nodemon ts-node typescript @types/node @types/express
Edit the index.ts
file
// index.ts
import * as express from "express";
const app = express();
const PORT = 3000;
app.get("/", (req, res) => {Building the TypeScript container (One)
res.send("OK");
});
app.listen(PORT, () => console.log(`Server started on some port: ${PORT}`));
tsconfig.json
file
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
package file for Typescript
{
"name": "typescript",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"dev": "nodemon src/index.ts",
"build": "tsc --build",
"start": "node dist/index.js"
},
"keywords": [],
"author": "shuvam",
"license": "ISC",
"description": "",
"dependencies": {
"express": "^4.18.1"
},
"devDependencies": {
"@types/express": "^4.17.13",
"@types/node": "^18.7.14",
"nodemon": "^2.0.19",
"ts-node": "^10.9.1",
"typescript": "^4.8.2"
}
}
Docker file for TypeScript Node.js
We will create a docker file and instruct docker on what needs to be done to run this application.
# build stage
FROM node:16-alpine as build # 1
WORKDIR /app # 2
COPY package*.json ./ # 3
RUN npm install # 4
COPY . . # 5
RUN npm run build # 6
# production build stage
FROM node:16-alpine as production # 7
WORKDIR /app # 8
COPY --from=build /app/package*.json ./ # 9
RUN npm install --omit=dev # 10
COPY --from=build /app/dist ./dist # 11
EXPOSE 3000 # 12
CMD ["npm", "run", "start"] # 13
In docker #
is used for comments, in the above docker file hashtags are used for showing different steps of building the docker image.
Build stage
- 1: It tells the docker engine what base docker image to use to build the container, in this case, this is an alpine image with node 16 and the container to be built is named as build
- 2: It tells the working directory where the project will reside.
- 3: It copies
package
andpackage-lock.json
from the filesystem to the container - 4: It runs the command
npm install
which installs all the dependencies - 5: It copies all the files from the filesystem to the build container
- 6: It runs the
npm run build
script present inside the package.json file
Production build stage
- 7: It tells the base image that the container will use, named ‘production’.
- 8: It tells the working directory where the project will reside.
- 9: It copies package.json and package-lock.json from the
build container to the
production container - 10: It runs
npm install --omit=dev
which only installs dependencies and ignores dev dependencies - 11: It copies the dist file from build container to the production container
- 12: It exposes the port 3000 of the container
- 13: It runs
npm run start
when the docker container is started
Running the application
Build the docker container
$ docker build -t scanskill-node-ts .
Running the application
$ docker run -p 3000:3000 scanskill-node-ts:latest
So, these were the steps that were taken while learning to dockerize Node.js applications.
Conclusion
So, in this article, we learned how to dockerize Node.js application. We saw how to add it to both JavaScript and TypeScript-based Node.js applications.
For more about docker visit: docs.docker.com