DEV Community

Cover image for Dockerizing an Express.js API with PostgreSQL Database for Testing and Production
Matheus Bernardes Spilari
Matheus Bernardes Spilari

Posted on • Edited on

Dockerizing an Express.js API with PostgreSQL Database for Testing and Production

Docker is a powerful tool that allows you to create, deploy, and run applications in containers. In this post, we will show you how to Dockerize an Express.js API with separate PostgreSQL databases for testing and production, and how to configure the environment so that local changes are reflected in the container.

Prerequisites

  • Node.js installed
  • Docker installed
  • Docker Compose installed
  • An existing Node.js API (or a new project)

Creating an Express.js API

First, create a new Express.js project.

mkdir node-docker-api
cd node-docker-api
npm init -y
npm install express
npm install -D nodemon 
Enter fullscreen mode Exit fullscreen mode

Create an index.js file:

const express = require('express');
const app = express();
const port = 3333;

app.use(express.json());

app.get('/', (req, res) => {
  res.send(`Hello, Docker from ${process.env.NODE_ENV} server!`);
});

app.listen(port, () => {
  console.log(`API of ${process.env.NODE_ENV} listening at http://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Enhancing your package.json

Create two scripts in your package.json: one for "dev":"NODE_ENV=development nodemon src/index.js" for development/testing, and another one for "start": "NODE_ENV=production node src/index.js" for production.

{
  "name": "node-docker-api",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "NODE_ENV=development nodemon src/index.js",
    "start": "NODE_ENV=production node src/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "express": "^4.19.2"
  },
  "devDependencies": {
    "nodemon": "^3.1.4"
  }
}
Enter fullscreen mode Exit fullscreen mode

Creating the Dockerfile

Create a Dockerfile in the root of your project.

# Using an official Node.js base image
FROM node:20-alpine

# Working directory inside the container
WORKDIR /app

# Copying package.json and package-lock.json
COPY package*.json ./

# Installing project dependencies based on NODE_ENV
ARG NODE_ENV
RUN if [ "$NODE_ENV" = "production" ]; then npm install --omit=dev; else npm install; fi

# Copying the rest of the application
COPY . .

# Exposing the port where the application will run
EXPOSE 3333

# Setting the environment variable for development or production
ENV NODE_ENV $NODE_ENV

# Command to run the application
CMD ["sh", "-c", "if [ \"$NODE_ENV\" = 'production' ]; then npm start; else npm run dev; fi"]

Enter fullscreen mode Exit fullscreen mode

Creating a .dockerignore

Create a .dockerignore file to avoid copying unnecessary files to the Docker image:

node_modules
npm-debug.log
.dockerignore
Enter fullscreen mode Exit fullscreen mode

Creating Docker Compose Files

Let's create two Docker Compose files: one for development/testing (docker-compose.dev.yaml) and another for production (docker-compose.prod.yaml).

docker-compose.dev.yaml

This file will contain the configuration for the development and testing environment.

services:
  api:
    build: .
    ports:
      - "3333:3333"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/devdb
    depends_on:
      - postgres

  postgres:
    image: postgres:latest
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: devdb
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
Enter fullscreen mode Exit fullscreen mode

docker-compose.prod.yaml

This file will contain the configuration for the production environment.

services:
  api:
    build: .
    ports:
      - "3333:3333"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/proddb
    depends_on:
      - postgres

  postgres:
    image: postgres:latest
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: proddb
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
Enter fullscreen mode Exit fullscreen mode

Setting Up the Development Environment

To reflect local changes in the container, we will use Docker Compose with volumes. The docker-compose.dev.yaml file is already configured to mount the local working directory (.) in the container (/app), except for node_modules, which will be kept inside the container to avoid dependency conflicts.

Running the Application

Development/Testing Environment

For the development/testing environment, use the following command:

docker-compose -f docker-compose.dev.yaml up --build
Enter fullscreen mode Exit fullscreen mode

Production Environment

For the production environment, use the following command:

docker-compose -f docker-compose.prod.yaml up --build
Enter fullscreen mode Exit fullscreen mode

Testing the Application

Open your browser and access http://localhost:3333. You should see the message "Hello, Docker!".

Conclusion

Congratulations! You have successfully Dockerized your Express.js API, set up different PostgreSQL databases for testing and production, and configured the development environment to reflect local changes in the container. Dockerizing your applications makes the development, testing, and deployment process more consistent and easier to manage. Keep exploring Docker's features to further improve your workflow.

If you have any questions or suggestions, feel free to leave a comment below!

Thanks for reading !


👋 Talk to me

Top comments (0)