How to Containerize Express.js

How to Containerize Express.js

This is the third installment of my series, Can You Containerize It? Today, I will be teaching you how to use Docker to containerize an Express.js app! Express is a Node.js framework used to build API's. You can easily scale a containerized API to handle dynamic traffic. You can also take advantage of technologies that allow for zero-downtime deployment and are self-healing! Let's get started.

TLDR: Here is the fulling working code on my Github.

Install locally

Just like my previous posts, I would like to get Express running locally before I containerize it. This helps you debug your code if something goes wrong. You can refer to one of my two posts on Getting Started with Express.js in 5 minutes or How to Convert Express.js to Typescript based on whether you want to use JavaScript or TypeScript as your base. Both articles contain links to my Github. I am going to use TypeScript for this tutorial. The containerization process should essentially be the same for either one.

git clone https://github.com/fourgates/blog-express-ts-docker.git
cd blog-express-ts-docker/
git checkout part-2-typescript
npm i
npm run start

You should have express running locally!

server started at http://localhost:8080

Create Dockerfile

Next, let's create a Dockerfile in the root directory of the project (same folder where package.json is). We will use this to create our "base" image.

FROM node:12

# 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 ./
# remove this line if you are not using TypeScript
COPY tsconfig.json ./ 
COPY tslint.json ./

RUN npm install

EXPOSE 8080

A couple of notes.

  • FROM node:12 - This is the base image we are using to build our own Express base image.
  • WORKDIR /usr/src/app - This sets a default directory for the image. Anytime you run a command for this image it will get run in this folder.
  • COPY - We want to copy dependency related files into our image. We need package.json and package-lock.json so node know what dependencies to install when running npm i. Also, if you are using TypeScript you need to copy the tsconfig.json to tell node how to compile your TypeScript and tslint.json to be able to lint your project.

Create docker-compose.yml

Now that we have instructions for Docker to build a base image. Next, we need to create a docker-compose.yml file to encapsulate commands for building a base image to develop our code in.

version: '3.7'

services:
  express:
    build:
      context: .
      dockerfile: Dockerfile  
    image: express/builder:0.0.1
    container_name: express-container
    ports:
      - '8080:8080'
    volumes:
      - ./src:/usr/src/app/src 
    command: npm run start

A couple of notes.

  • version - This is the version of docker-compose to use. Newer versions add improved capabilities.
  • services - You can have docker start multiple containers for full-stack applications. A good use case for multiple services would be if you want to start up an Express server and a DynamoDb instance for your local API to interact with.
  • build - This is the build section of the compose. We are using context to point to where the Docker files are located. In this case, they are in the current directory. dockerfile is used to point to the correct file with instructions on how to build the image.
  • image - This is the name Docker will give to your image once it is created.
  • container_name - This is the name that is used for the container when the images start.

Build Base Image

Let's build the base image!

docker-compose build

Start Express!

Finally, start your server!

docker-compose up dev

Success! :timetopartyemoji:

express-container | server started at http://localhost:8080

Conclusion

That's it! You can now develop code as you would any other Express app. You don't need to install any dependencies~ There are a few things we can do to improve the build time and image size but I will leave that exercise for a future post. You can also push this image to your favorite Docker repo and use it anywhere you would like. Happy Container, Happy Developer!

Here is the fulling working code on my Github.

Caveats

Remember to bump the image version number and push a new base image any time dependencies change! This means anytime you update package.json there needs to be a new base image.

Pro Tip

docker ps - This is a useful command to list all running containers.

docker ps -a - List all containers, including those that are not running.

Did you find this article valuable?

Support Phillip Ninan by becoming a sponsor. Any amount is appreciated!