Perfect Nest.js Guide with Docker Compose and PostgreSQL
Posted July 4, 2024
![Perfect Nest.js with Docker Compose and PostgreSQL](/nestjs-with-postgresql-and-docker/hero.jpg)
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
The created container should be ready as such:
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:
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:
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.