Perfect Nest.js Guide with Docker Compose and PostgreSQL

Posted July 4, 2024
Perfect Nest.js with Docker Compose and PostgreSQL

In this tutorial we’re going to use Docker and Docker Compose to create and package Nest.js with PostgreSQL, pgAdmin, and Adminer. As a tutorial Bonus, you will learn how to use Nest.js and PostgreSQL with the TypeORM layer.

You will learn:

  • Setting up a basic Nest.js API with Docker.
  • Adding TypeORM to access PostgreSQL with Nest.js
  • How to package the whole Nest.js app with Docker Compose.
  • Using pgAdmin and Adminer to access the PostgreSQL Database graphically.

If you’re ready to follow along, ensure Docker is installed and running.

Setting Up Nest.js

In the previous blog, we covered how to create a RESTFul API with Nest.js, Postgres, and TypeORM. We will use this guide and learn how to package the same application with Docker for a perfect Nest.js local development environment.

If you do not have a Ready Nest.js App, use the following command and get one:

# Add cli
npm i -g @nestjs/cli
# Create the app
nest new nest-app

The app should be fine up to this point.

Adding Nest.js to Docker with Dockerfile

Dockerfile packages the whole Nest.js app based on the instructions the Docker will need. We will use Dockerfile to lay out the instructions on how to build a Nest.js Docker image.

touch Dockerfile

If you are for the simplest and the most basic Dockerfile for PostgreSQL and Nest.js, you will use the following instructions:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
CMD [ "npm", "run", "start:dev" ]

Here:

  • Docker will use the Alpine image to get Node.js as the base image.
  • All the Nest.js source code will be added to the app folder on Docker.
  • We will install the packages and use the CMD to run the app.

Although this example will work, a multistage build approach will optime your image even further. This should be perfect for local development to reduce the final image size. We’re going to use the following Dockerfile as the multistage build approach:

###################
# development build
###################

# Stage 1: Build the NestJS application
FROM node:20-alpine AS build

# Docker working directory
WORKDIR /app

# Copying file into APP directory of docker
# Copy package.json and package-lock.json
# A * will ensure both package.json AND package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install --production=false

# Copy the rest of the application code
# Copy the current directory to the APP folder
COPY . .

# Build the application
RUN npm run build

###################
# PRODUCTION BUILD
###################

# Stage 2: Create a smaller production image
FROM node:20-alpine AS production

# Set the working directory
WORKDIR /app

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

# Install only production dependencies
RUN npm install --production

# Copy the built application from the previous stage
COPY --from=build /app/dist ./dist

# Command to run the app
CMD ["node", "dist/main"]

Let’s use this example in this guide. We’ll leverage intermediate images for build dependencies and final deployment as follows:

Stage 1 is built build to

  • Sets the working directory to /app.
  • Install dependencies, both production and development dependencies.
  • Copy the entire application code.
  • Build the application using npm run build.

The Stage 2 will target production to:

  • Create the production image.
  • Install production dependencies only with npm install --production.
  • Copy the compiled and optimized output on the ./dist directory from the build stage into the /app/dist as production.
  • Run the application using CMD ["node", "dist/main"] to expose port 3000.

Adding Nest.js PostgreSQL dockerignore File

Still, on the same, we’ll create a new file to tell Docker what files and folders to ignore to avoid unneeded coping:

touch .dockerignore

Add the following:

node_modules
npm-debug.log
.vscode
.git
.idea
.env
coverage
*.log

The benefits of .dockerignore are:

  • Reduced Image Size by Excluding unnecessary files
  • Preventing sensitive files (like environment configurations) from being included in the image (if not needed)
  • The Docker ignored files during the speed of the build process.

Writing the Nest.js PostgreSQL Docker Compose File

Dockerfile lays down the instructions to build your Nest.js image. The docker-compose.yml file lays out the instructions to run the images as containers. Let’s create this file within your Nest.js directory.

touch docker-compose.yml

Let’s now add Docker Compose commands to spin up a local Nest.js development environment with Docker. We’ll add the following instructions to docker-compose.yml:

version: '3.8'

services:
  nestjs:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
 - "3000:3000"  # Assuming NestJS runs on port 3000
    volumes:
 - .:/usr/src/app

Let’s highlight the important section of this docker-compose.yml of this file:

Running the Basic Nest.js PostgreSQL Docker Container

Because we have Dockerfile and docker-compose.yml files ready. We’ll run the following command to the container up and running:

docker compose up -d

Nest.js with Docker Compose and PostgreSQ

The created container should be ready as such:

Nest.js with Docker Compose and PostgreSQ

Confirm if the container is working by opening http://localhost:3000/ and getting a Nest.js Hello World!.

Adding PostgreSQL to Nest.js and Docker

We now have the structure of the application, We need Nes.js to access PostgreSQL while still within Docker. This section will let’s do so.

Before diving into using PostgreSQL, this guide will assume you have already set up PostgreSQL within Nest.js. But for clarity, we’ll set up PostgreSQL with TypeORM. Check out this tutorial for a deeper guide to using TypeORM with PostgreSQL and Nest.js. Install PostgreSQL and TypeORM as such:

npm i typeorm pg @nestjs/typeorm

Next, create an Entity file named Entity.ts inside the src folder:

// Entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
 @PrimaryGeneratedColumn()
 id: number;

 @Column()
 firstName: string;

 @Column()
 lastName: string;

 @Column()
 age: number;
}

Key Points here are:

  • The Entity Class User Represents a table TypeORM that will be created in the database.
  • The Decorators used like column definition and primary key designation are the table structure.

You will then create a connection to PostgreSQL using Nest.js and TypeORM in your AppModule (src/app.module.ts):

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './Entity';

 @Module({
 imports: [
     TypeOrmModule.forRoot({
 type: 'postgres',
 host: 'localhost',
 port: 5432,
 username: 'Add-postgres-username',
 password: 'Add-postgres-password',
 database: 'Users',
 entities: [User],
 synchronize: true,
 }),],
 controllers: [AppController],
 providers: [AppService],
})
export class AppModule {}

Let’s check what is happening here:

  • TypeORM will access the Users. This means you must create this database upfront. We will check this later.
  • Nest.js will use TypeORM to create the User table in the Users database.

Adding Nest.js and PostgreSQL to Docker Compose

At this stage, we need PostgreSQL on Docker. At the same time, pgAdmin will be used to access PostgreSQL. These two tools will be created as follows:

Go to docker-compose.yml and add new to services. It’s here you will Docker package PostgreSQL and create the Users database. NestJS will access this database to lay out the User table.

Note that NestJS on Docker will look up to the database. This means the API will depend on the Docker service running the DB. Your whole docker-compose.yml file will take a new look as such:

version: '3.8'

services:
  backend:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
 - "3000:3000"  # Assuming NestJS runs on port 3000
    volumes:
 - .:/usr/src/app
    depends_on:
 - db

  db:
    image: postgres:16
    environment:
 - POSTGRES_USER=postgres
 - POSTGRES_PASSWORD=pass
 - POSTGRES_DB=Users
    volumes:
 - ./docker/db/data:/var/lib/postgresql/data
 - ./docker/db/sql:/docker-entrypoint-initdb.d
    ports:
 - 5432:5432

  pgadmin:
    image: dpage/pgadmin4
    environment:
 - [email protected]
 - PGADMIN_DEFAULT_PASSWORD=password
    ports:
 - 5050:80
    depends_on:
 - db

Take a closer look at this section:configs:

    environment:
 - POSTGRES_USER=postgres
 - POSTGRES_PASSWORD=pass
 - POSTGRES_DB=Users

These details will be the same based on your database connection on Nest.js and TypeORM setup:

 username: 'Add-postgres-username',
 password: 'Add-postgres-password',
 database: 'Users',

Keep an eye on the host. The data in running on Docker. This means Nest.js will not look at PostgreSQL on localhost but on Docker. This way, the host changes to the services running the database, db. Within these changes, your Nest.js app should let TypeORM use the following environments:

 port: 5432,
 host: 'bd',
 username: 'postgres',
 password: 'pass',
 database: 'Users',
 entities: [User],

The pgAdmin will access the PostgreSQL UI. If you choose to use Adminer, replace the pgAdmin service with the following:

  adminer:
    image: adminer
    ports:
 - 8080:8080
    depends_on:
 - db

Spin up PostgreSQL and Nest.js with Docker Compose

We have added some changes to the Compose and Source Code. Docker must have these changes. First, stop and remove the running Nest.js container:

docker compose down
[+] Running 2/2
 ✔ Container nest-app-backend-1  Removed                                                                                           0.0s 
 ✔ Network nest-app_default      Removed 

Next, use the Docker Compose command to spin up all the services:

docker-compose up --build -d
[+] Running 32/2
 ✔ pgadmin Pulled                                                                                                                127.5s 
 ✔ db Pulled                                                                                                                     179.3s 
[+] Running 4/4
 ✔ Network nest-app_default      Created                                                                                           0.1s 
 ✔ Container nest-app-db-1       Started                                                                                           1.2s 
 ✔ Container nest-app-pgadmin-1  Started                                                                                           1.5s 
 ✔ Container nest-app-backend-1  Started  

The Goal now is to check if the Docker PostgreSQL Nest.js Connection is, In this case, we’ll access pgAdmin on http://localhost:5050/ and use email [email protected] and password password to log in.

Once in, Add New save and give in a name:

Go to the connection tab and add PostgreSQL connection details based on your Docker Compose Yml file. Save these settings:

Nest.js with Docker Compose and PostgreSQ

To confirm PostgreSQL Nest.js connection with Docker and TypeORM, check the Users database section. It should be Created. Under Users db, you will have the user table ready:

Nest.js with Docker Compose and PostgreSQ

Conclusion

We have perfectly created a Nest.js PostgreSQL Container with Docker. To further this guide, check this TypeORM with NestJS and Postgres guide to create a complete CRUD API.

Perfect Nest.js Guide with Docker Compose and PostgreSQL

Written By:

Joseph Chege