Using Prisma with Node.js in a Dockerized Environment

Aung Thu Oo
6 min readSep 22, 2024

--

Prequirements

To complete this tutorial, you will need:

Using Prisma with Node.js in a Dockerized Environment

I will use Node.js and Express. We will also use Prisma to interact with the database.

Prisma is an open-source ORM (Object-Relational Mapping) tool for Node.js and TypeScript applications. It simplifies database access by providing an intuitive and type-safe way to interact with databases.

Create a folder called backend at the root of the project.

mkdir -p <YOUR_PROJECT_FOLDER>/backend

Then open the folder in your terminal and initialize a Node.js project:

cd <YOUR_PROJECT_FOLDER>/backend
npm init -y

Build a FULL STACK Web app with Javascript API, Next.js, Node.js, Express, Prisma,Postgres,Docker

Install the dependencies:

  • express: to create the server
  • prisma: to interact with the database
  • @prisma/client: to generate the code to interact with the database
npm i express prisma @prisma/client

Initialize the prisma project:

npx prisma init

This initializes the prism project. We will use Prisma to interact with the database. Prisma will generate the code to interact with the database, so we don’t have to write it ourselves.

Open the file called .env and replace the content with the following:

.env

DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres?schema=public"

As a model for our project, we will use a ‘User’ with an id, a name, and an email. The id will be an autoincrementing integer, the name will be a string, and the email will also be a string.

Open the file /prisma/schema.prisma and replace the content with the following:

<YOUR_PROJECT_FOLDER>/backend/prisma/schema.prisma

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
//User with id as int autoincrement, name as string, email as string
model User {
id Int @id @default(autoincrement())
name String
email String
}

Now create a file called index.js inside the <YOUR_PROJECT_FOLDER>/backend folder and add the following content:

<YOUR_PROJECT_FOLDER>/backend/index.js

const express = require('express');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const app = express();
//use json
app.use(express.json());
//cors
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
//test api with error handling
app.get('/test', (req, res, next) => {
try {
res.status(200).json({ message: 'Success!' });
} catch (err) {
next(err);
}
});
//get all users
app.get('/users', async (req, res, next) => {
try {
const users = await prisma.user.findMany();
res.status(200).json(users);
} catch (err) {
next(err);
}
});
//get user by id
app.get('/users/:id', async (req, res, next) => {
try {
const user = await prisma.user.findUnique({
where: { id: Number(req.params.id) },
});
res.status(200).json(user);
} catch (err) {
next(err);
}
});
//create user
app.post('/users', async (req, res, next) => {
try {
const user = await prisma.user.create({
data: { ...req.body },
});
res.status(201).json(user);
} catch (err) {
next(err);
}
});
//update user
app.put('/users/:id', async (req, res, next) => {
try {
const user = await prisma.user.update({
where: { id: Number(req.params.id) },
data: { ...req.body },
});
res.status(200).json(user);
} catch (err) {
next(err);
}
});
//delete user
app.delete('/users/:id', async (req, res, next) => {
try {
const user = await prisma.user.delete({
where: { id: Number(req.params.id) },
});
res.status(200).json(user);
} catch (err) {
next(err);
}
});
//Start server
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Now you can generate the Prisma schema

npx prisma generate

Before we dockerize the backend, let's test it.

Type in your terminal:

node index.js

Open your browser and go to http://localhost:4000/test. You should see the message Success!.

But if we go on localhost:4000/users, we actually see an error! This is because we don't have the schema in our database yet

We can proceed with the dockerization part, and we will solve this problem later.

Dockerize the backend

Let’s create 2 files, called .dockerignore and backend.dockerfile in the backend folder.

Open the file .dockerignore and add the following content:

**/node_modules

Open the file <YOUR_PROJECT_FOLDER>/backend/backend.dockerfile and add the following content:

<YOUR_PROJECT_FOLDER>/backend/backend.dockerfile

FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY prisma ./prisma
RUN npx prisma generate
COPY . .
EXPOSE 4000
CMD ["node", "index.js"]

Let’s update the <YOUR_PROJECT_FOLDER>/compose.yaml file in the project's root, adding the backend service.

Create or updated compose.yaml:

<YOUR_PROJECT_FOLDER>/compose.yaml

version: '3.9'
services:
backend:
container_name: backend
image: backend
build:
context: ./backend
dockerfile: backend.dockerfile
ports:
- "4000:4000"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres?schema=public
depends_on:
- db
db:
container_name: pgdb
image: postgres:12
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata: {}

Build the backend image:

docker compose build

Start the backend container:

docker compose up -d backend

Check if the container is running:

docker ps -a

Something interesting before we proceed or make any http requests.

docker exec -it pgdb psql -U postgres
\dt

And we should see no relations.

Now type

docker exec -it backend npx prisma migrate dev --name init
docker exec -it pgdb psql -U postgres
\dt

And we should see the table User in the database. Of course it’s still empy, but Prisma has created it for us. Cool!

Testing

I will add users in 3 different ways.

  • one user using Prisma Studio
  • one user using Postman
  • one user using psql

Create a user using Prism Studio

Open a new terminal and type:

cd <YOUR_PROJECT_FOLDER>/backend/
npx prisma studio

This will open Prisma Studio in your browser at http://localhost:5555 (Note: we are not using Docker to run Prisma Studio, we are running it directly on our machine).

Add a record: Aung Thu Oo and example@gmail.com

hit Save 1 change. You can leave Prisma Studio open in a tab, we will use it later.

If we make an http request to http://localhost:4000/users we should see 1 user (the one we just created using Prisma Studio).

[{
"id" : "1",
"name": "Aung Thu Oo",
"email": "example@gmail.com"
}]

Now let’s add another user using Postman (or any other tool you like).

If we check localhost:4000/users we should see users now:

We can also check again on Prisma Studio, to check the consistency of our operations.

Insert user from psql

Let’s insert another user using psql.

cd <YOUR_PROJECT_FOLDER>/
docker exec -it pgdb psql -U postgres
\dt

Let’s insert a new user manually using psql (don't do this in production, it's just for testing purposes!).

insert into "User" (name, email) values ('ATO developer', 'atodev@gmail.com');
select * from "User";

Let’s check again on Prisma Studio, and we should see 3 users now:

#daily_dev_note

--

--

Aung Thu Oo

Senior Developer (Nex.js, React.js, Node.js, Laravel, Flutter)