Dockerizing a NestJS app with Prisma and PostgreSQL
How to dockerize a NestJS application with Prisma and PostgreSQL.
- Authors
- Marc Stammerjohann
- Published at
Docker π³ enables you to build consistent containers of your applications for your development, testing and production environments. In this post you will dockerize a NestJS πΈ application with Prisma connecting to a PostgreSQL π database.
Requirements for this post are
You can find the full source code on GitHub.
Use this prisma schema to follow along:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Food {
id Int @id @default(autoincrement())
name String
}
And a .env
file in your prisma
directory for a dummy PostgreSQL connection url:
DATABASE_URL=postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public
TL;DR Multi-stage Dockerfile
Create a Dockerfile
in the root of your Nest application
touch Dockerfile
Open the Dockerfile
and use the multi-stage build steps π€
FROM node:14 AS builder
# Create app directory
WORKDIR /app
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
COPY prisma ./prisma/
# Install app dependencies
RUN npm install
COPY . .
RUN npm run build
FROM node:14
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]
But wait... what is going in the Dockerfile
π€β See the breakdown for each step below.
Don't forget to create a .dockerignore
file next to your Dockerfile
:
node_modules
npm-debug.log
The COPY
command ignores those local files and folder and won't copy them into your Docker image to prevent overwriting your installed modules in your image.
Your application structure should look like this:
Breakdown of the multi-stage Dockerfile
Let's breakdown the Dockerfile
step-by-step
π Builder Image
FROM node:14 AS builder
The first line tells Docker to use the latest LTS version 14
for node
as the base image to build the container from. To optimize the container image size you are using the multistage-build and assign a name to your base image AS builder
.
Before updating to a newer version of node
check the support of Nest, Prisma and other dependencies
π§° Working directory
# Create app directory
WORKDIR /app
Create the working directory for your application which stores your code. All commands (RUN
, COPY
) are executed inside this directory.
π¦ Installation
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
COPY prisma ./prisma/
# Install app dependencies
RUN npm install
Next you need to install your app dependencies inside the Docker image. package.json
and package-lock.json
are copied over. Generating the Prisma Client requires the schema.prisma
file. COPY prisma ./prisma/
copies the whole prisma
directory in case you also need the migrations.
Only package*.json
and prisma
directory is copied in this step to take advantage of the cached Docker layers.
Install all dependencies RUN npm install
(dev too). This allows you to build the Nest application inside the Docker image. Prisma Client is generated directly after, as it defines its own postinstall hook.
βοΈ Build app
COPY . .
RUN npm run build
To build your Nest application copy all of your source files (exceptions in .dockerignore
) into the Docker image. Now it's time to build your app RUN npm run build
.
π Run your app
FROM node:14
The second FROM
is the second stage in the multi-stage build and is used to run your application.
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
Copy from your builder
image only files and folders required to run the Nest app.
EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]
Nest apps usually bind to port 3000
, EXPOSE
the same port for your Docker image. Last step is the command to run the Nest application using CMD
.
Build and run your image
Enter the following command in the directory of your Dockerfile
. Give your build image a name using the -t
flag to easily start, stop and remove it.
# give your docker image a name
docker build -t <your username>/nest-api .
# for example
docker build -t nest-api .
After your Docker image is successfully build start it with this command
docker run -p 3000:3000 --env-file .env -d <your username>/nest-api
Prisma Client requires the DATABASE_URL
environment variable which you pass using the --env-file .env
flag. Use this .env
file for additional environment variables (Port, JWT Secret etc.) in your root folder.
Open up localhost:3000 to verify that your Nest app is running with Docker.
Add docker-compose with PostgreSQL
Docker Compose allows you to define and run multiple Docker container together. Here you are setting up a Docker compose file for the Nest application and a PostgreSQL database.
Create the Docker compose file
touch docker-compose.yml
version: '3.8'
services:
nest-api:
container_name: nest-api
build:
context: .
dockerfile: Dockerfile
ports:
- 3000:3000
depends_on:
- postgres
env_file:
- .env
postgres:
image: postgres:13
container_name: postgres
restart: always
ports:
- 5432:5432
env_file:
- .env
volumes:
- postgres:/var/lib/postgresql/data
volumes:
postgres:
name: nest-db
The first service nest-api is building the Docker image based on your Dockerfile
for your Nest app with Prisma. The second service is creating a postgres database using the postgres
Docker image in version 13
. For the Postgres image set POSTGRES_USER
, POSTGRES_PASSWORD
and POSTGRES_DB
environment variables in your root .env
file.
POSTGRES_USER=prisma
POSTGRES_PASSWORD=topsecret
POSTGRES_DB=food
To connect to the PostgreSQL database Docker image configure the DATABASE_URL
in your .env
file. Fill in your values into the Postgres connection url format
postgresql://USER:PASSWORD@HOST:PORT/DB?schema=NAME&sslmode=prefer
In this example add the following variable to the .env
file. The HOST
is when connecting from another Docker image either the service name or the container name - both postgres
.
DATABASE_URL=postgresql://prisma:topsecret@postgres:5432/food?schema=food&sslmode=prefer
Time π to start your Nest app and Postgres Docker image. Make sure the ports 3000
and 5432
are not in use already.
docker-compose up
# or detached
docker-compose up -d
You should have the following two docker containers started
Open again localhost:3000 to verify that your Nest app is running with Docker. Also verify that your endpoints using the Prisma Client have access to the Postgres DB.
Perform local migration
Replace the host postgres
with localhost
if you want to perform Prisma migrations locally of your Postgres Docker container. Update the DATABASE_URL
in prisma/.env
to
DATABASE_URL=postgresql://prisma:topsecret@localhost:5432/food?schema=food&sslmode=prefer
Now you can run npx prisma migrate dev
and seed the database if you like.
Perfect, now sit back and relax π and let Docker do the work for you.
Perform migrations with Docker
Checkout how to use prisma migrate deploy
in the Dockerfile to perform migrations for your testing, staging and production environment.
Sponsor us
Did you find this post useful? We at notiz.dev write about our experiences developing Apps, Websites and APIs and develop Open Source tools. Your support would mean a lot to us π. Receive a reward by sponsoring us on Patreon or start with a one-time donation on GitHub Sponsors.