NestJS with Prisma and PostgreSQL is one of the most productive backend stacks available in 2026. NestJS provides structure and dependency injection, Prisma handles database access with type safety, and PostgreSQL gives you a battle-tested relational database. Here's how to build a complete REST API with this stack.
Project Setup
Create a new NestJS project and install Prisma:
npm i -g @nestjs/cli
nest new my-api
cd my-api
npm install prisma @prisma/client
npx prisma init
This creates a prisma/schema.prisma file and a .env with a placeholder DATABASE_URL.
Configure the Database Connection
Update .env with your PostgreSQL connection string:
DATABASE_URL="postgresql://postgres:password@localhost:5432/myapi_db"
If you don't have PostgreSQL installed, the quickest way is Docker:
docker run --name my-postgres -e POSTGRES_PASSWORD=password -e POSTGRES_DB=myapi_db -p 5432:5432 -d postgres:16
Define Your Schema
Open prisma/schema.prisma and define your models. Let's build a simple task management API:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String
tasks Task[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Task {
id String @id @default(cuid())
title String
description String?
priority Priority @default(MEDIUM)
status TaskStatus @default(TODO)
dueDate DateTime?
userId String
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum Priority {
LOW
MEDIUM
HIGH
}
enum TaskStatus {
TODO
IN_PROGRESS
DONE
}
Run the migration to create the tables:
npx prisma migrate dev --name init
Create a Prisma Service
Create a shared Prisma service that the rest of your application injects:
// src/prisma/prisma.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
}
// src/prisma/prisma.module.ts
import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
Build the Tasks Module
// src/tasks/tasks.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateTaskDto } from './dto/create-task.dto';
@Injectable()
export class TasksService {
constructor(private prisma: PrismaService) {}
async findAll(userId: string) {
return this.prisma.task.findMany({
where: { userId },
orderBy: { createdAt: 'desc' },
});
}
async findOne(id: string, userId: string) {
const task = await this.prisma.task.findFirst({
where: { id, userId },
});
if (!task) throw new NotFoundException('Task not found');
return task;
}
async create(userId: string, dto: CreateTaskDto) {
return this.prisma.task.create({
data: { ...dto, userId },
});
}
async update(id: string, userId: string, dto: Partial<CreateTaskDto>) {
await this.findOne(id, userId); // throws if not found
return this.prisma.task.update({
where: { id },
data: dto,
});
}
async remove(id: string, userId: string) {
await this.findOne(id, userId);
return this.prisma.task.delete({ where: { id } });
}
}
Validation with DTOs
Install validation packages and use class-validator decorators:
npm install class-validator class-transformer
// src/tasks/dto/create-task.dto.ts
import { IsString, IsOptional, IsEnum, IsDateString } from 'class-validator';
import { Priority, TaskStatus } from '@prisma/client';
export class CreateTaskDto {
@IsString()
title: string;
@IsString()
@IsOptional()
description?: string;
@IsEnum(Priority)
@IsOptional()
priority?: Priority;
@IsDateString()
@IsOptional()
dueDate?: string;
}
Enable validation globally in main.ts:
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
Testing Your Endpoints
Once running (npm run start:dev), your API is available at http://localhost:3000:
POST /tasks β Create a task
GET /tasks β List all tasks
GET /tasks/:id β Get one task
PATCH /tasks/:id β Update a task
DELETE /tasks/:id β Delete a task
Skip the Setup With PromptForge
This tutorial covered the basics of a NestJS + Prisma + PostgreSQL setup. A production application also needs authentication, rate limiting, error handling, Swagger documentation, Docker configuration, and CI/CD. Setting all of that up from scratch takes 1β2 days.
PromptForge generates all of it β the schema, services, controllers, auth module, Dockerfile, and documentation β from a single prompt in under five minutes. Try it for free.