In this post, we’ll learn how to set up Prisma with PostgreSQL inside a TurboRepo monorepo, using PNPM workspaces, Node.js, and Docker for the database.
By the end, you’ll have a working Express.js API that connects to a PostgreSQL database via Prisma ORM — all inside a clean, shareable monorepo structure.
What Is a Monorepo?
A monorepo (short for monolithic repository) is a way of organizing multiple apps and packages under one codebase — with shared dependencies, tools, and configs.
For example:
This setup allows all projects to share code easily (like database config or TypeScript settings).
⚙️ Prerequisites
Make sure you have:
- Node.js (LTS version)
- PNPM (globally installed)
npm install -g pnpm
- Docker + PostgreSQL You can reference this Docker Postgres setup guide to install.
Step 1 : Create the TurboRepo Monorepo
pnpm dlx create-turbo@latest
When prompted:
- Select
.
to create inside your current folder. - Choose PNPM as the package manager.
After setup, you’ll see folders like apps/docs
and apps/web
.
Delete the docs
app — we’ll only keep the web
app for now.
Step 2 : Create a Node.js Backend (http-server)
Inside the apps
folder, create a new project folder:
cd apps
mkdir http-server
cd http-server
npm init -y
Your package.json
should look like this:
{
"name": "http-server",
"version": "1.0.0",
"type": "commonjs",
"main": "index.js",
"scripts": {
"build": "tsc -b",
"start": "node dist/index.js",
"dev": "npm run build && npm run start"
}
}
What These Scripts Do:
-
build
→ Compiles TypeScript to JavaScript in thedist
folder. -
start
→ Runs the built JS file (dist/index.js
). -
dev
→ Builds the code, then starts the server (useful in development).
Step 3 : Create a tsconfig.json
Inside apps/http-server
, add:
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
}
}
👉 This tells TypeScript where to find the source files (src
) and where to put compiled files (dist
).
Now create:
mkdir src
touch src/index.ts
Step 4 : Add Dependencies
Install dependencies for the backend:
pnpm add express @types/express bcryptjs @types/bcryptjs cors @types/cors
And link the shared TypeScript config from the monorepo:
"dependencies": {
"@repo/typescript-config": "workspace:*"
}
Then run:
pnpm install
Step 5 : Create a Shared Database Package
In the packages
folder, create database
:
cd ../../packages
mkdir database
cd database
npm init -y
Update the package.json
:
{
"name": "@repo/database",
"version": "1.0.0",
"type": "commonjs",
"main": "index.js",
"dependencies": {
"@repo/typescript-config": "workspace:*"
}
}
And add a TypeScript config file:
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
}
}
Why Create These Files?
-
package.json
: Defines this as a workspace package that other apps (likehttp-server
) can import. -
tsconfig.json
: Tells TypeScript how to compile code in this package.
Step 6 : Install and Setup Prisma
Inside packages/database
:
pnpm add prisma @prisma/client
pnpm prisma init
What These Commands Do:
-
pnpm add prisma @prisma/client
→ Installs Prisma ORM and its generated client. -
pnpm prisma init
→ Creates aprisma
folder with aschema.prisma
file and.env
.
Step 7 : Configure Prisma Schema
Open prisma/schema.prisma
and replace it with:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
name String
email String @unique
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Step 8 : Setup the Environment Variable
In packages/database/.env
, add your PostgreSQL connection string:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/monorepo-prisma"
Step 9 : Run Prisma Commands
Run the migration and generate the Prisma client:
npx prisma migrate dev --name init_user_model
👉 This creates a migration file in /prisma/migrations
and applies it to your PostgreSQL database.
npx prisma studio
👉 Opens Prisma Studio — a beautiful web UI to view and edit your database data.
npx prisma generate
👉 Generates the Prisma client that you’ll use in your code to query the database.
Step 10 : Export the Prisma Client
Inside packages/database/src/index.ts
:
import { PrismaClient } from "@prisma/client";
export const prisma = new PrismaClient();
Then update your package.json
exports:
{
"name": "@repo/database",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"exports": {
"./config": "./src/index.ts"
},
"dependencies": {
"@prisma/client": "^6.17.1",
"prisma": "^6.17.1",
"@repo/typescript-config": "workspace:*"
}
}
Step 11 : Connect Prisma in the Express Server
Go to your apps/http-server/src/index.ts
and paste this:
import express, { Request, Response } from "express";
import { prisma } from "@repo/database/config";
import bcrypt from "bcryptjs";
import cors from "cors";
const app = express();
app.use(express.json());
app.use(cors());
app.post("/api/register", async (req: Request, res: Response) => {
try {
const { name, email, password } = req.body;
if (!name || !email || !password) {
return res.status(400).json({ error: "All fields are required" });
}
const existingUser = await prisma.user.findUnique({ where: { email } });
if (existingUser) {
return res.status(409).json({ error: "User already exists" });
}
const hashedPassword = await bcrypt.hash(password, 10);
const user = await prisma.user.create({
data: { name, email, password: hashedPassword },
select: { id: true, name: true, email: true },
});
res.status(201).json({ message: "User registered successfully", user });
} catch (error) {
console.error("Error while registering user:", error);
res.status(500).json({ error: "Internal server error" });
}
});
app.listen(3001, () => console.log("🚀 Server is running on port 3001"));
Step 12 : Link Everything Together
Add this to apps/http-server/package.json
:
"dependencies": {
"@repo/database": "workspace:*"
}
Then in the root of your project, run:
pnpm install
Step 13 : Test the API
Run your server:
pnpm dev --filter http-server
Now test the /api/register
endpoint using Postman:
POST http://localhost:3001/api/register
Body (JSON):
{
"name": "Waseem Adil",
"email": "waseem@gmail.com",
"password": "12345678"
}
If everything works, you’ll see:
{
"message": "User registered successfully",
"user": {
"id": "uuid-generated",
"name": "Waseem Adil",
"email": "waseem@gmail.com"
}
}
Final Project Structure
Summary
Step | Command / Concept | Explanation |
---|---|---|
pnpm dlx create-turbo@latest |
Creates a new monorepo | TurboRepo project setup |
pnpm add prisma @prisma/client |
Installs Prisma ORM | ORM + generated client |
pnpm prisma init |
Initializes Prisma | Creates schema + .env |
npx prisma migrate dev |
Creates migration + syncs DB | Updates schema in Postgres |
npx prisma studio |
Opens Prisma UI | View/Edit database data |
npx prisma generate |
Generates client | Makes PrismaClient usable in code |
What Is Prisma?
Prisma is an ORM (Object-Relational Mapper) for Node.js and TypeScript.
It helps you interact with your database (Postgres, MySQL, etc.) using clean TypeScript code instead of writing raw SQL.
Example:
const users = await prisma.user.findMany();
is equivalent to:
SELECT * FROM users;
Top comments (0)