Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Express.js and Docker

Docker is a platform for developing, shipping, and running applications in containers. This guide covers key concepts, examples, and best practices for containerizing Express.js applications with Docker.

Key Concepts of Docker

  • Containers: Lightweight, standalone, executable packages that include everything needed to run a piece of software.
  • Dockerfile: A text file that contains instructions for building a Docker image.
  • Docker Image: A read-only template used to create Docker containers.
  • Docker Compose: A tool for defining and running multi-container Docker applications.
  • Volumes: Mechanisms for persisting data generated by and used by Docker containers.

Creating a Dockerfile for Express.js

Create a Dockerfile to define the environment in which your Express.js application will run:

Example: Basic Dockerfile

// Dockerfile
# Use the official Node.js image as a base image
FROM node:14

# Set the working directory inside the container
WORKDIR /app

# Copy package.json and package-lock.json to the container
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code to the container
COPY . .

# Expose the port the app runs on
EXPOSE 3000

# Define the command to run the application
CMD ["node", "server.js"]

// server.js
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
    res.send('Hello, World!');
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Building and Running the Docker Image

Build and run your Docker image using Docker commands:

Example: Building and Running the Image

// Build the Docker image
// docker build -t express-app .

// Run the Docker container
// docker run -p 3000:3000 express-app

// Open the application in your browser
// http://localhost:3000

Using Docker Compose for Multi-Container Applications

Use Docker Compose to define and run multi-container applications:

Example: Docker Compose Setup

// docker-compose.yml
version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development

// Build and run the application using Docker Compose
// docker-compose up --build

// Open the application in your browser
// http://localhost:3000

Managing Environment Variables

Manage environment variables in your Docker containers:

Example: Using Environment Variables

// .env
PORT=3000
NODE_ENV=production

// Dockerfile (additional configuration)
# Use the official Node.js image as a base image
FROM node:14

# Set the working directory inside the container
WORKDIR /app

# Copy package.json and package-lock.json to the container
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code to the container
COPY . .

# Expose the port the app runs on
EXPOSE 3000

# Define the command to run the application
CMD ["node", "server.js"]

// docker-compose.yml (additional configuration)
version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    env_file:
      - .env

// Build and run the application using Docker Compose
// docker-compose up --build

Persisting Data with Volumes

Use Docker volumes to persist data generated by and used by Docker containers:

Example: Using Volumes

// docker-compose.yml (additional configuration)
version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
      - app_data:/app/data
    env_file:
      - .env

volumes:
  app_data:

// server.js (additional code)
const express = require('express');
const fs = require('fs');
const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
    res.send('Hello, World!');
});

app.get('/data', (req, res) => {
    fs.writeFileSync('/app/data/data.txt', 'Hello, Docker!');
    res.send('Data written to /app/data/data.txt');
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

Best Practices for Docker

  • Use Multi-Stage Builds: Use multi-stage builds to reduce the size of your Docker images.
  • Keep Images Small: Minimize the size of your Docker images to improve build times and reduce the attack surface.
  • Use .dockerignore: Use a .dockerignore file to exclude files and directories from the Docker build context.
  • Leverage Caching: Leverage Docker's layer caching to speed up builds.
  • Use Environment Variables: Manage configuration settings securely using environment variables.

Testing Dockerized Applications

Test your Dockerized applications to ensure they work as expected:

Example: Testing with Mocha

// Install Mocha and Chai
// npm install --save-dev mocha chai

// test/docker.test.js
const chai = require('chai');
const expect = chai.expect;
const request = require('supertest');
const express = require('express');

const app = express();
app.get('/', (req, res) => {
    res.send('Hello, World!');
});

describe('Dockerized Application', () => {
    it('should return Hello, World!', (done) => {
        request(app)
            .get('/')
            .expect(200)
            .end((err, res) => {
                if (err) return done(err);
                expect(res.text).to.equal('Hello, World!');
                done();
            });
    });
});

// Define test script in package.json
// "scripts": {
//   "test": "mocha"
// }

// Run tests with NPM
// npm run test

Key Points

  • Containers: Lightweight, standalone, executable packages that include everything needed to run a piece of software.
  • Dockerfile: A text file that contains instructions for building a Docker image.
  • Docker Image: A read-only template used to create Docker containers.
  • Docker Compose: A tool for defining and running multi-container Docker applications.
  • Volumes: Mechanisms for persisting data generated by and used by Docker containers.
  • Follow best practices for Docker, such as using multi-stage builds, keeping images small, using a .dockerignore file, leveraging caching, and using environment variables.

Conclusion

Docker is a powerful platform for developing, shipping, and running applications in containers. By understanding and implementing the key concepts, examples, and best practices covered in this guide, you can effectively containerize your Express.js applications with Docker. Happy coding!