How to use Prisma ORM 8 in NestJS: setup, queries, deploy
Prisma ORM 8 replaces the extends PrismaClient service in NestJS with one shared client and plain query helpers. Scaffold, query, and deploy to Prisma Compute.

The new way to use Prisma ORM 8 in NestJS

Ankur DattaSeptember 24, 2026
Prisma 8series.
Prisma ORM 8, the new major version of Prisma's TypeScript ORM, changes how you wire the client into NestJS. The Prisma ORM 8 client is a plain object exported by
src/prisma/db.ts
, so there is no class to extend, and the NestJS template that
create-prisma
scaffolds has no service that extends
PrismaClient
. One shared client lives in
src/prisma/db.ts
, and an injectable
PrismaService
exposes query helpers written as ordinary functions. Prisma ORM 8 is the version npm installs by default.
This post is the fifteenth in our Prisma 8 series, following Is Prisma 8 ready for long-lived production apps?. It walks through scaffolding a NestJS API on Prisma Postgres, following a request from the controller to the database, writing typed queries, and deploying the API to Prisma Compute. We ran every command below against
[email protected]
,
@prisma/[email protected]
, and NestJS 11.
Share your client without extending it
If you have used Prisma with NestJS, you have probably written a service that extends
PrismaClient
. In Prisma ORM 7, the setup commonly looked like this:
import { Injectable, OnModuleInit } from "@nestjs/common";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "./generated/prisma/client";
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
constructor() {
super({
adapter: new PrismaPg({
connectionString: process.env.DATABASE_URL!,
}),
});
}
async onModuleInit() {
await this.$connect();
}
}
That works for a basic client, but adding client extensions is where the class pattern gets awkward:
$extends()
returns an extended instance, while the service inherits from the original class. NestJS users have reported this for years in issue 18628.
Prisma ORM 8 removes the class. The client is the object that
postgres({ ... })
returns in
src/prisma/db.ts
, so there is no
PrismaClient
to inherit from.
$extends
is gone too: cross-cutting behavior is middleware that you pass as the
middleware
option when you create the client, so there is no extended instance to keep in sync with a subclass. The NestJS starter wraps the client in a small injectable service and exposes the queries your application needs:
import { Injectable } from "@nestjs/common";
import { db, listUsers, type StarterUser } from "./prisma/users";
@Injectable()
export class PrismaService {
readonly db = db;
listUsers(limit = 10): Promise<StarterUser[]> {
return listUsers(limit);
}
}
NestJS injects
PrismaService
, and the service delegates to functions in
src/prisma/
. You keep query helpers in ordinary TypeScript files and call them from the service. You could wrap
PrismaClient
in a service before. What changes in Prisma ORM 8 is that there is no class and no generated package: the client is a value in
src/prisma/db.ts
, and the middleware that replaced
$extends
is an option you pass when you create it.
db.ts
first asks the Prisma Composer service binding for a database client. Outside Composer that lookup throws, so it falls back to
postgres({ contractJson, url: process.env.DATABASE_URL })
. Locally you use the fallback. On Prisma Compute the binding wins.
There is also no generated client package. Prisma ORM 7 generated one into an output directory, and importing it from NestJS raised recurring questions about import paths and pnpm type errors. Prisma ORM 8 emits the contract into
src/prisma/
as
contract.json
and
contract.d.ts
, and
db.ts
imports them with relative paths.
1. Create the NestJS project
You need Node.js 24.11 or newer, or 22.18 or newer on the 22 line. Then run:
npm create prisma@latest -- nest-prisma-8 --template nest --provider postgres --yes
cd nest-prisma-8
This creates the NestJS app, installs Prisma ORM 8, writes the contract to
src/prisma/contract.prisma
(what Prisma ORM 7 called
schema.prisma
) with its emitted
contract.json
and
contract.d.ts
, and adds
module.ts
and
service.ts
for deployment.
--yes
accepts the scaffold defaults and answers no to the deploy prompt. You deploy explicitly at the end. The defaults also install Prisma agent skill files into
.claude/
,
.cursor/
,
.agents/
, and
.devin/
, plus a
postinstall
script that keeps them in sync. Pass
--skills none
to skip that.
The app already has a
/users
endpoint. These are the files to look at:
| File | What it does |
|---|---|
src/users.controller.ts | Handles HTTP requests to /users |
src/users.service.ts | Calls the query exposed by PrismaService |
src/prisma.service.ts | Makes the shared client and query helpers injectable |
src/app.module.ts | Registers both controllers and both providers |
src/prisma/users.ts | Holds the starter listUsers query, which runs the seed first |
src/prisma/seed.ts | Upserts three sample users, once per process |
src/prisma/db.ts | Creates the shared database client |
contract.json
and
contract.d.ts
are emitted from
contract.prisma
. After you change the contract, run
npm run contract:emit
to regenerate them, then
npm run db:update
to apply the change to the database.
You can also choose a different contract authoring style, either TypeScript or Prisma Schema Language, or a different package manager through the create-prisma options.
2. Connect to Prisma Postgres and initialize the database
Create a temporary Prisma Postgres database:
npx create-db@latest
The command prints a connection string and a claim link. The database is deleted after 24 hours unless you open the claim link and keep it.
Copy the connection string, export it in your terminal, then apply the starter contract to the database:
export DATABASE_URL="<your PostgreSQL connection string>"
npm run db:init
The scaffold ships an
.env.example
that tells you to copy it to
.env
. Do not rely on that for this walkthrough: neither
prisma.config.ts
nor the CLI loads
.env
, so
db:init
and
npm run dev
only see a variable you exported in the shell.
db:init
ends with
Applied 5 operation(s) across 1 contract space
and lists the tables, constraints, and indexes it created. If it stops with
Connection terminated unexpectedly
, the new database is still starting. Wait a few seconds and run it again.
There is no seed command.
listUsers
upserts three sample users the first time it runs in a process, so the data appears on the first request to
/users
.
3. Follow a request from controller to database
AppModule
registers
PrismaService
and
UsersService
, and
UsersService
receives the shared Prisma service through NestJS dependency injection:
import { Inject, Injectable } from "@nestjs/common";
import { PrismaService } from "./prisma.service";
@Injectable()
export class UsersService {
constructor(@Inject(PrismaService) private readonly prisma: PrismaService) {}
async findAll() {
return this.prisma.listUsers(10);
}
}
The controller calls that service when a request arrives:
import { Controller, Get, Inject } from "@nestjs/common";
import { UsersService } from "./users.service";
@Controller("users")
export class UsersController {
constructor(@Inject(UsersService) private readonly usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
}
The explicit
@Inject(...)
decorators name each provider token. The starter runs
tsx watch
in development, and
tsx
transpiles with esbuild, which ignores the
emitDecoratorMetadata
flag in
tsconfig.json
. Without the metadata, NestJS cannot infer a constructor parameter's provider from its type, so the token is spelled out and injection behaves the same in development and in the
tsdown
production build. This applies to code you add too: put
@Inject(Token)
on every injected constructor parameter.
Start the app:
npm run dev
In a second terminal, call the endpoint:
curl http://localhost:3000/users
You get the starter users as JSON. The request goes through the controller, the service, and the Prisma query helper, with one shared client behind them.
4. Write queries for the endpoint
Prisma ORM 8 queries chain the conditions you need, then execute with
.all()
or
.first()
.
For example, a helper that fetches a short list of user ids and emails can look like this:
import { db } from "./db";
export async function listUserIdsAndEmails() {
return db.orm.public.User.select("id", "email")
.orderBy((user) => user.email.asc())
.limit(10)
.all();
}
Here,
public
is the PostgreSQL schema and
User
is the model.
Selecting two fields also narrows the result type to those fields. A service can call this helper the same way it calls the starter's
listUsers
function. Helpers you write yourself do not call
seed()
, so on a fresh database request
/users
once before you expect rows from them. See the reading data guide for filtering and pagination.
When you need lower-level control, the same client exposes a SQL builder:
const query = db.sql.public.user
.select("id", "email")
.where((fields, operators) => operators.ilike(fields.email, "%@prisma.io"))
.limit(10)
.build();
const users = await db.runtime().query(query);
The SQL builder is keyed by table name: the model name with a lowercase first letter, unless the model sets
@@map
. That is why the ORM path says
User
and the SQL path says
user
.
The builder sends
%@prisma.io
as a query parameter, so the pattern is never concatenated into the SQL text. You can use the SQL builder for one endpoint and the ORM everywhere else; both run on the same client and pool. The advanced queries guide covers joins, the SQL builder, and the MongoDB pipeline builder.
The client is lazy:
postgres({ ... })
builds the query surface up front and opens the driver pool the first time a query runs. The starter's
seed()
calls
connectDatabase()
in
src/prisma/db.ts
to open that pool explicitly before its upserts. Nothing closes the pool. Keep that one client shared across requests. To release connections on shutdown, implement
OnModuleDestroy
on
PrismaService
and call
db.close()
there, then enable shutdown hooks with
app.enableShutdownHooks()
in
main.ts
:
import { Injectable, type OnModuleDestroy } from "@nestjs/common";
import { db } from "./prisma/db";
import { listUsers, type StarterUser } from "./prisma/users";
@Injectable()
export class PrismaService implements OnModuleDestroy {
readonly db = db;
listUsers(limit = 10): Promise<StarterUser[]> {
return listUsers(limit);
}
async onModuleDestroy() {
await this.db.close();
}
}
If you are coming from Prisma ORM 7, the Prisma ORM 7 to 8 upgrade guide shows how contracts and the new query APIs fit together, one route at a time.
Deploy the API to Prisma Compute
The scaffold includes
module.ts
and
service.ts
, which describe the app and its infrastructure through Prisma Composer. The Prisma CLI can deploy that declaration to Prisma Compute.
npx prisma auth login
npm run build
npx prisma deploy module.ts
prisma auth login
opens the browser once.
npm run build
bundles the app with
tsdown
into
dist/server.mjs
.
prisma deploy module.ts
provisions the database and the compute service and prints the app URL.
npm run deploy
runs the last two together.
nest-prisma-8
├─ database postgres-database db_abc123
└─ app compute-service cps_abc123
https://xyz.ewr.prisma.build
The deploy does not copy your local database.
prisma deploy
provisions a Prisma Postgres database for the module and hands it to the app through the Composer binding, so the deployed app never reads your local
DATABASE_URL
. The first request to
/users
seeds it.
Take the app URL printed by the CLI and check it:
curl https://<your-app-host>/users
You now have a NestJS API on Prisma Compute and its database on Prisma Postgres. For the full deployment walkthrough, see the NestJS guide in the docs. For previews per Git branch and deploy-on-push, see Deploy on push.
Prisma ORM 8 supports MongoDB too
NestJS with MongoDB was a recurring question on Prisma ORM 7. Prisma ORM 8 supports MongoDB in early access: document models in the contract, a pipeline builder for aggregations, and migration plans that create collections and indexes.
Start a separate NestJS project with the MongoDB provider:
npm create prisma@latest -- nest-mongo --template nest --provider mongodb --package-manager pnpm --yes
With npm, the current
create-prisma
release stops at an
ERESOLVE
peer-dependency error:
@prisma/orm-mongo
requires the MongoDB 7 driver, while
alchemy
, the deployment library Prisma Composer depends on, still declares
mongodb@^6
. pnpm resolves it, so the command above pins pnpm.
The scaffold writes
.env
with
mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true
, but nothing loads that file. MongoDB projects use two variables: the Prisma CLI scripts such as
db:init
read
MONGODB_URL
, and the dev server in
src/prisma/db.ts
still reads
DATABASE_URL
. On a standalone
mongod
, drop
replicaSet=rs0
and export both before
pnpm run db:init
and
pnpm run dev
:
export MONGODB_URL="mongodb://localhost:27017/mydb?directConnection=true"
export DATABASE_URL="$MONGODB_URL"
Prisma ORM 8 does not run MongoDB transactions yet. If you need them, use the driver's sessions directly. Transactions need a replica set, so in that case keep
replicaSet=rs0
and run
mongod
as a replica set.
The MongoDB quickstart walks through the connection variables, the migration plan, and the first query.
Frequently asked questions
No. The Prisma ORM 8 client is a plain object, created by
postgres({ ... })
or handed in by the Prisma Composer service binding, so there is no class to extend. The NestJS starter created by
create-prisma
keeps one shared client in
src/prisma/db.ts
, writes query helpers as plain TypeScript functions in
src/prisma/users.ts
, and exposes them through a small injectable
PrismaService
.
The starter annotates each constructor parameter with
@Inject(PrismaService)
or
@Inject(UsersService)
so NestJS resolves dependencies by token. The development script runs on
tsx
, whose esbuild transform ignores the
emitDecoratorMetadata
flag, so NestJS cannot infer the provider from the parameter type. Explicit tokens keep injection working the same under
tsx
in development and under the
tsdown
production build.
Yes, in early access. Scaffold with
npm create prisma@latest -- my-app --template nest --provider mongodb --package-manager pnpm --yes
(plain npm hits an
ERESOLVE
error in the current
create-prisma
release). The scaffold gives you document models in the contract, and the ORM adds a pipeline builder for aggregations and migration plans that create collections and indexes. The Prisma CLI reads
MONGODB_URL
while the dev server reads
DATABASE_URL
, so export both with the same value. Prisma ORM 8 does not run MongoDB transactions yet.
The Prisma ORM 7 to 8 upgrade guide for PostgreSQL has you install Prisma ORM 8 next to Prisma ORM 7, infer a contract from the existing database, and move queries over one route at a time, with
findMany
and
findFirst
becoming
.all()
and
.first()
. In a NestJS app, replace the service that extends
PrismaClient
with an
@Injectable()
class that holds the shared
db
object and calls query functions from
src/prisma/
.
Building with NestJS? Tell us how it goes
Try Prisma ORM 8 in your NestJS app and tell us what works well or where you get stuck. Join our Discord to report what breaks and ask questions. Feedback posted there goes straight to the ORM team.
About the author

Ankur is a member of the Prisma team who works closely with the developer community, with hundreds of contributions across Prisma's open source repositories and a background that includes founding an ed-tech startup. He writes about TypeScript, Node.js, PostgreSQL, and modern application stacks.
Keep reading
[ORMSeptember 5, 2024
Why Use Prisma to Build Your Data Layer in 2024?
Discover how Prisma's powerful tools help you build a scalable, secure, and high-performing data layer. From the popular TypeScript and Node.js ORM to advanced features like connection pooling, caching, and query optimization, Prisma equips you to scale your app to millions of users.
Nikolas Burk, Marc Hess](https://www.prisma.io/blog/why-prisma-2024)


[ORMJune 22, 2023
Prisma Client Extensions Are Now Production Ready
Make Prisma Client do even more with Client extensions, now Generally Available. Extend your client, models, queries, and results to tailor Prisma Client to your use case.
Jon Harrell](https://www.prisma.io/blog/client-extensions-ga-4g4yIu8eOSbB)

Build your next app with Prisma
Start free. Scale when you’re ready.
منتشرشده در
Prisma Blog
ادامه مطالعه




