How to: Dockerizing a Node.js & TypeScript Project

June 23, 2023

Introduction

The purpose of this new How to post, is to teach you how to run your Node.js and TypesScript application in a Docker container. We will explore the process of Dockerizing your Node.js and TypeScript project, enabling you to leverage the benefits of containerization for your development workflow.

Prerequisites

In order to follow this guide, ensure that you have the following prerequisites in place:

  • Basic knowledge of Docker: Familiarize yourself with the core concepts and terminology of Docker. If you are new to Docker or need a refresher, I recommend reading my previous post to gain a foundational understanding of Docker's key features and functionality.
  • Docker installed: Make sure you have Docker installed on your system.
  • cURL installed: Verify that you have cURL installed on your system. cURL is a command-line tool used for making HTTP requests, which we will use for testing our Dockerized application.
  • Node.js and TypeScript project: Have a Node.js and TypeScript project ready for Dockerization. Ensure that your project is properly set up and configured to run successfully on your local machine.

With these prerequisites fulfilled, you are well-prepared to proceed with Dockerizing your Node.js and TypeScript application. Let's get started!

Creating Our Dockerfile

In order to Dockerize our App, we need to create a Dockerfile as a recipe, where we will specify all steps and actions required for the build, testing, and running of our Node.js-TypeScript App.

  1. Create a Dockerfile at the root of your Node.js-TypeScript project.

  2. Define a base image, in this case, we will start from a node.js base image.

    FROM node:16
  3. Now, we will create our app directory for the container. Any RUN, CMD, ADD, COPY, or ENTRYPOINT command will be executed in the specified working directory. If the WORKDIR command is not written in the Dockerfile, it will automatically be created by the Docker compiler.

    # Create app directory
    WORKDIR /usr/src/app
  4. Rather than copying the entire working directory, we need to copy package.json and lock-package.json files and then, run the npm install command.

    Note: This allows us to take advantage of cached Docker layers, the npm install command will be executed only if the package.json is changed.

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN npm install
# If you are building your code for production
# RUN npm ci --omit=dev
  1. Next, is time to bundle our app's source code inside the Docker image by using the COPY command:
# Copy source code (./) to the root directory of the image (./)
COPY ./ ./
  1. Now, we need to transpile our TypeScript code to JavaScript code:
# Compile TS
RUN npm run compile
  1. Now, we will inform Docker that our app listens on the specified network ports at runtime:
# Expose App to 4000 port
EXPOSE 4000
  1. Finally, define the command to run the app using CMD which defines your runtime. Here we will use the transpiled app.js file located in /dist folder to start our server:
CMD ["node", "dist/app.js"]
  1. Now, our Dockerfile should now look like this:
FROM node:16

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN npm install
# If you are building your code for production
# RUN npm ci --omit=dev

# Copy source code (./) to the root directory of the image (./)
COPY ./ ./

# Compile TS
RUN npm run compile

# Expose App to 4000 port
EXPOSE 4000

CMD ["node", "dist/app.js"]
  1. Create a .dockerignore file in the same directory as your Dockerfile with the following content:
node_modules
npm-debug.log

All files and folders specified in the .dockerignore file will be ignored and they will not be copied onto our Docker image and possibly overwriting modules installed within your image.

Running and Testing our Dockerized App

  1. First, we will build the Docker Image we created previously in the Dockerfile. Go to the directory that has your Dockerfile and run the following command to build the Docker image.
docker build . -t <your_username>/<app_name>:<version>

Note: The -t flag lets you tag our image allowing us an easy way to manage our images.

  1. Verify if your Image was created successfully by running the following command:
docker images
  1. It’s time to run a new container based on the image created before with the following command:
docker run -p 4040:4000 -d --name <app_name> <image_name>:<version>

Note1: By default, when we run a container with Docker, the container doesn't expose any of its ports to the outside world. For that reason, we need to expose our container to a public port with the -p flag.

Note2: With -d flags, we are running the container in detached mode, leaving the container running in the background.

  1. Finally, to test our app, we will use the exposed port, 4040 to make an HTTP request to our app by using curl.
curl -i localhost:4040

And the result:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Content-Length: 48
ETag: W/"30-7Yi3uWpxIQxoqTst0a5RvC2U8SY"
Date: Wed, 26 Apr 2023 14:51:12 GMT
Connection: keep-alive
Keep-Alive: timeout=5

Conclusion

In conclusion, Dockerizing your Node.js and TypeScript application offers numerous advantages, such as improved portability, reproducibility, and scalability. By encapsulating your application and its dependencies within a Docker container, you can ensure consistent behavior across different environments, simplify deployment processes, and enhance collaboration among developers.

In this guide, we discussed the steps involved in Dockerizing your Node.js and TypeScript project. We started by creating a Dockerfile to define the necessary instructions for building and running the application within a container. Then, we explored how to build and run the Docker image, exposing it to the desired port. Finally, we tested our Dockerized application using cURL.

Start Dockerizing your Node.js and TypeScript applications today, and experience the benefits of containerization firsthand.

I see you soon in the next How to post. Happy coding! :)


Profile picture

Written by Marco Ciau who is apassionate about providing solutions by using software. I thoroughly enjoy learning new things and am always eager to embrace new challenges. You can follow me on Twitter.