Skip to content
NestJS ns database 5 min read

Prisma Setup

Prisma is a type-safe ORM that generates a fully typed query client from a declarative schema, making it a natural fit for NestJS and TypeScript. Unlike decorator-based ORMs, Prisma keeps your data model in a single schema.prisma file and produces a PrismaClient whose methods know the exact shape of every table. The idiomatic way to use it in Nest is to wrap that client in an injectable PrismaService that opens the connection during bootstrap, then expose it through a PrismaModule so any provider can inject it. This page walks through installing Prisma, defining the schema, generating the client, and wiring it into the DI container.

Installing Prisma

You need the Prisma CLI as a dev dependency and the @prisma/client runtime package. Initialize Prisma to scaffold the schema file and a .env entry for your connection string.

npm install prisma --save-dev
npm install @prisma/client
npx prisma init --datasource-provider postgresql

prisma init creates a prisma/ directory with a starter schema.prisma and adds a DATABASE_URL placeholder to your .env.

.env
prisma/schema.prisma

Keep the DATABASE_URL in .env and never commit it. Prisma reads this variable through env("DATABASE_URL") in the schema, so the same code runs against local, staging, and production databases.

Defining the schema

The schema has three parts: a generator that emits the client, a datasource that points at your database, and one or more model blocks that describe your tables. Models are the source of truth — Prisma derives both the SQL schema (via migrations) and the TypeScript types from them.

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  published Boolean @default(false)
  author    User    @relation(fields: [authorId], references: [id])
  authorId  Int
}

Generating the client

Run prisma generate to produce the typed client into node_modules/@prisma/client. To push the schema to a fresh database during development, prisma db push creates the tables without a migration history; for tracked changes use prisma migrate dev instead.

npx prisma generate
npx prisma db push

Output:

✔ Generated Prisma Client (v5.x) to ./node_modules/@prisma/client in 84ms
🚀  Your database is now in sync with your Prisma schema. Done in 312ms

Add prisma generate to a postinstall script in package.json. The generated client lives in node_modules, so it is wiped on a clean npm install and must be regenerated in CI and Docker builds.

Building the PrismaService

PrismaClient already manages a connection pool, so the cleanest integration is to extend it with an @Injectable() service and let Nest’s lifecycle hooks control the connection. Calling $connect() in onModuleInit makes Nest establish the connection during bootstrap rather than lazily on the first query, which surfaces credential errors immediately.

import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  async onModuleInit(): Promise<void> {
    await this.$connect();
  }

  async onModuleDestroy(): Promise<void> {
    await this.$disconnect();
  }
}

Because the service is a PrismaClient, every model accessor (prisma.user, prisma.post) and method (findMany, create, $transaction) is available directly on the injected instance, fully typed.

Creating the PrismaModule

Wrap the service in a module and mark it @Global() so you only import it once in the root module instead of in every feature module. Export the service so other providers can inject it.

import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Global()
@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}

Then import PrismaModule once in AppModule:

import { Module } from '@nestjs/common';
import { PrismaModule } from './prisma/prisma.module';
import { UsersModule } from './users/users.module';

@Module({
  imports: [PrismaModule, UsersModule],
})
export class AppModule {}

Injecting and using the service

Inject PrismaService into any provider through the constructor and call the typed query methods. The return types are inferred from your schema, so the editor autocompletes fields and flags typos at compile time.

import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { User } from '@prisma/client';

@Injectable()
export class UsersService {
  constructor(private readonly prisma: PrismaService) {}

  findAll(): Promise<User[]> {
    return this.prisma.user.findMany({ include: { posts: true } });
  }

  create(email: string, name?: string): Promise<User> {
    return this.prisma.user.create({ data: { email, name } });
  }
}

When you start the app, the connection is established before any request is served.

npm run start:dev

Output:

[Nest] 5120  - 06/14/2026, 9:11:02 AM     LOG [InstanceLoader] PrismaModule dependencies initialized +2ms
[Nest] 5120  - 06/14/2026, 9:11:02 AM     LOG [NestApplication] Nest application successfully started +5ms

Sync vs. push vs. migrate

Three commands move your schema into the database, each suited to a different stage.

CommandWhat it doesWhen to use
prisma generateRegenerates the typed client from the schemaAfter any schema edit
prisma db pushSyncs the database to the schema with no historyEarly prototyping, throwaway dev DBs
prisma migrate devCreates a versioned SQL migration and applies itTracked schema changes, teams, production

Best Practices

  • Extend PrismaClient in a single PrismaService and connect via onModuleInit so failures surface at boot.
  • Make PrismaModule global and import it once in AppModule; export PrismaService for injection.
  • Add prisma generate to a postinstall script so the client is rebuilt in CI and Docker images.
  • Use prisma migrate dev for tracked schema changes and reserve db push for disposable development databases.
  • Keep DATABASE_URL in .env and out of version control; reference it through env() in the schema.
  • Always call $disconnect() in onModuleDestroy to release pooled connections on graceful shutdown.
Last updated June 14, 2026
Was this helpful?