DEV Community

Wasim Adil
Wasim Adil

Posted on

Setting Up Prisma + PostgreSQL in a Monorepo (TurboRepo + PNPM + Node.js)

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
Enter fullscreen mode Exit fullscreen mode

Step 1 : Create the TurboRepo Monorepo

pnpm dlx create-turbo@latest
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

What These Scripts Do:

  • build → Compiles TypeScript to JavaScript in the dist 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"
  }
}
Enter fullscreen mode Exit fullscreen mode

👉 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
Enter fullscreen mode Exit fullscreen mode

Step 4 : Add Dependencies

Install dependencies for the backend:

pnpm add express @types/express bcryptjs @types/bcryptjs cors @types/cors
Enter fullscreen mode Exit fullscreen mode

And link the shared TypeScript config from the monorepo:

"dependencies": {
  "@repo/typescript-config": "workspace:*"
}
Enter fullscreen mode Exit fullscreen mode

Then run:

pnpm install
Enter fullscreen mode Exit fullscreen mode

Step 5 : Create a Shared Database Package

In the packages folder, create database:

cd ../../packages
mkdir database
cd database
npm init -y
Enter fullscreen mode Exit fullscreen mode

Update the package.json:

{
  "name": "@repo/database",
  "version": "1.0.0",
  "type": "commonjs",
  "main": "index.js",
  "dependencies": {
    "@repo/typescript-config": "workspace:*"
  }
}
Enter fullscreen mode Exit fullscreen mode

And add a TypeScript config file:

{
  "extends": "@repo/typescript-config/base.json",
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./dist"
  }
}
Enter fullscreen mode Exit fullscreen mode

Why Create These Files?

  • package.json: Defines this as a workspace package that other apps (like http-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
Enter fullscreen mode Exit fullscreen mode

What These Commands Do:

  • pnpm add prisma @prisma/client → Installs Prisma ORM and its generated client.
  • pnpm prisma init → Creates a prisma folder with a schema.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
}
Enter fullscreen mode Exit fullscreen mode

Step 8 : Setup the Environment Variable

In packages/database/.env, add your PostgreSQL connection string:

DATABASE_URL="postgresql://postgres:postgres@localhost:5432/monorepo-prisma"
Enter fullscreen mode Exit fullscreen mode

Step 9 : Run Prisma Commands

Run the migration and generate the Prisma client:

npx prisma migrate dev --name init_user_model
Enter fullscreen mode Exit fullscreen mode

👉 This creates a migration file in /prisma/migrations and applies it to your PostgreSQL database.

npx prisma studio
Enter fullscreen mode Exit fullscreen mode

👉 Opens Prisma Studio — a beautiful web UI to view and edit your database data.

npx prisma generate
Enter fullscreen mode Exit fullscreen mode

👉 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();
Enter fullscreen mode Exit fullscreen mode

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:*"
  }
}
Enter fullscreen mode Exit fullscreen mode

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"));
Enter fullscreen mode Exit fullscreen mode

Step 12 : Link Everything Together

Add this to apps/http-server/package.json:

"dependencies": {
  "@repo/database": "workspace:*"
}
Enter fullscreen mode Exit fullscreen mode

Then in the root of your project, run:

pnpm install
Enter fullscreen mode Exit fullscreen mode

Step 13 : Test the API

Run your server:

pnpm dev --filter http-server
Enter fullscreen mode Exit fullscreen mode

Now test the /api/register endpoint using Postman:

POST http://localhost:3001/api/register
Enter fullscreen mode Exit fullscreen mode

Body (JSON):

{
  "name": "Waseem Adil",
  "email": "waseem@gmail.com",
  "password": "12345678"
}
Enter fullscreen mode Exit fullscreen mode

If everything works, you’ll see:

{
  "message": "User registered successfully",
  "user": {
    "id": "uuid-generated",
    "name": "Waseem Adil",
    "email": "waseem@gmail.com"
  }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

is equivalent to:

SELECT * FROM users;
Enter fullscreen mode Exit fullscreen mode

🎉 That’s It!

Top comments (0)