Express.js + Prisma v7 Setup (TypeScript + pnpm)

Express.js + Prisma v7 Setup (TypeScript + pnpm)
Preface
Prisma has introduced a major update with Prisma v7, bringing notable structural changes to how Prisma Client is generated and managed.
Key changes include:
A new
prisma.config.tsfileA separate output directory for generated clients
The Prisma Client and generated types are no longer stored inside
node_modules
Instead, when running prisma generate, Prisma now requires an explicit output path to be defined in your project’s schema.prisma. For example:
output = "../src/generated/prisma"
This guide walks through a clean and modern setup for building an Express.js + Prisma v7 application using TypeScript and the pnpm package manager.
Project Setup
Initialize the Node.js project
pnpm init
This command creates a package.json file and initializes your Node.js project.
Ensure that pnpm is installed globally before continuing.
Initialize TypeScript
tsc --init
This creates a tsconfig.json file with default compiler settings, which we will customize later.
Update package.json
Add the following scripts:
"scripts": {
"build": "tsc -b",
"start": "node dist/src/index.js",
"dev": "pnpm run build && pnpm run start"
}
Also add:
"type": "module"
Why "type": "module" is required
By default, Node.js treats .js files as CommonJS modules (require() syntax).
However, modern Prisma versions and recent tooling use ES Modules (ESM) with import/export.
Adding "type": "module" tells Node.js:
“Treat all .js files in this project as ES Modules.”
Without this, Node.js will attempt to execute your compiled files as CommonJS and fail when encountering ESM syntax or Prisma’s generated ESM client.
Configure tsconfig.json
Replace the contents of tsconfig.json with the following:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"outDir": "dist"
},
"include": ["src/**/*"]
}
Understanding the Key Compiler Options
Think of tsconfig.json as instructions for the TypeScript compiler:
What language to output, how strict to be, and where files should go.
1. Runtime Target (target)
Setting:
ES2022Purpose: Defines the JavaScript version emitted by TypeScript
Why it matters:
Modern Node.js versions support ES2022 natively. This avoids unnecessary polyfills and keeps the compiled output clean and performant.
2. Module System (module & moduleResolution)
Setting:
NodeNextPurpose: Enables native ES Module behavior
Important note:
import { x } from "./file.js";
When using NodeNext, file extensions are mandatory in imports. This is a common source of confusion, but it aligns with how modern Node.js resolves modules.
3. Type Safety & Build Stability
strict: trueEnables comprehensive type safety checks—essential for production-grade code.
skipLibCheck: truePrevents build failures caused by type errors in third-party libraries.
4. Output Structure
includelimits compilation to thesrcdirectoryoutDirensures compiled files are emitted todist
This keeps source and build artifacts clearly separated.
At this point, the base application setup is complete.
Prisma Setup
Install Dependencies
pnpm add prisma @types/node @types/pg --save-dev
pnpm add @prisma/client @prisma/adapter-pg pg dotenv
Package Breakdown
prisma– Prisma CLI for migrations and generation@prisma/client– Type-safe database client@prisma/adapter-pg– PostgreSQL driver adapterpg– PostgreSQL driver@types/pg– Type definitions forpgdotenv– Loads environment variables from.env
Initialize Prisma
pnpm prisma init
This creates:
prisma.config.tsprisma/schema.prisma
Configure the Prisma Client Generator
Update schema.prisma:
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
}
This ensures the Prisma Client is generated outside node_modules.
📌 Add the following to .gitignore:
/generated/prisma
dist
Configure Database URL
Update the DATABASE_URL in your .env file.
DATABASE_URL="postgresql://username:password@localhost:5432/mydatabase"
You may use a local PostgreSQL instance or a hosted provider such as Neon.
Define the Data Model
Edit prisma/schema.prisma:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
Run Migrations
pnpm prisma migrate dev --name init
This creates database tables based on your schema.
Generate Prisma Client
pnpm dlx prisma generate
A new generated/prisma directory will be created with the client and types.
Instantiate Prisma Client
Create src/lib/prisma.ts:
import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../../generated/prisma/client.js";
const connectionString = process.env.DATABASE_URL!;
const adapter = new PrismaPg({ connectionString });
const prisma = new PrismaClient({ adapter });
export { prisma };
💡 In Development, consider implementing a singleton Prisma Client to avoid connection exhaustion.
Express Setup
Install Express
pnpm add express @types/express
Create the Application Entry Point
Create src/index.ts:
import express from "express";
import { prisma } from "./lib/prisma.js";
const app = express();
const port = 3009;
app.get("/test", async (_req, res) => {
const user = await prisma.user.create({
data: {
name: "Alice",
email: "alice@prisma.io",
posts: {
create: {
title: "Hello World",
content: "This is my first post!",
published: true,
},
},
},
include: { posts: true },
});
const allUsers = await prisma.user.findMany({
include: { posts: true },
});
res.json({ users: allUsers });
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
⚠️ This approach is not recommended for production, but it is sufficient for demonstration and verification purposes.
Test The Endpoint

Conclusion
By following this guide step by step, you will have a fully working Express.js + Prisma v7 application using:
TypeScript
pnpm
PostgreSQL
Prisma Client outside
node_modulesNative ES Modules
This setup aligns with modern Node.js and Prisma best practices and serves as a solid foundation for scalable backend applications.d types are no longer stored inside node_modules
If this guide helped you understand how to set up Express.js with Prisma v7 using TypeScript and pnpm, consider giving it a ❤️ like and sharing it with other developers who might be struggling with Prisma’s new client generation changes.
Have questions, edge cases, or improvements in mind?
Drop them in the comments—I actively read and respond.
Follow me on Hashnode for more deep-dive backend guides, Prisma internals, and modern Node.js workflows 🚀

