Welcome back to the NestJS Expert Series! 🎉
In Part 1,we built a simple CRUD API using NestJS fundamentals—Modules, Controllers, and Services.
But any real-world application needs persistent storage. That’s where databases come in.
In this article, we’ll integrate databases with NestJS using two popular ORMs:
Prisma – Modern, type-safe ORM with great DX.
TypeORM – Mature ORM used widely in enterprise projects.
By the end, you’ll be able to hook up your NestJS app to a PostgreSQL database (the same steps apply for MySQL or SQLite).
⚡ Step 1: Setting Up a Database
For this guide, let’s use PostgreSQL. If you have Docker installed:
docker run --name nest-postgres -e POSTGRES_PASSWORD=admin -e POSTGRES_USER=admin -e POSTGRES_DB=tasks -p 5432:5432 -d postgres
📦 Step 2: Adding Prisma to NestJS
npm install prisma --save-dev
npm install @prisma/client
npx prisma init
This creates a prisma/schema.prisma file. Define a simple Task model:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Task {
id Int @id @default(autoincrement())
title String
isCompleted Boolean @default(false)
createdAt DateTime @default(now())
}
Run migrations:
npx prisma migrate dev --name init
🛠️ Step 3: Using Prisma in NestJS
`Create a Prisma service:
nest g service prisma`
prisma.service.ts:
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
Now inject it into your TasksService:
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class TasksService {
constructor(private prisma: PrismaService) {}
findAll() {
return this.prisma.task.findMany();
}
create(title: string) {
return this.prisma.task.create({ data: { title } });
}
update(id: number, isCompleted: boolean) {
return this.prisma.task.update({
where: { id },
data: { isCompleted },
});
}
delete(id: number) {
return this.prisma.task.delete({ where: { id } });
}
}
Now your Tasks API is connected to PostgreSQL with Prisma 🎉.
🔄 Alternative: TypeORM with NestJS
Some teams still prefer TypeORM for its flexibility. Here’s the quick setup:
npm install @nestjs/typeorm typeorm pg
Add it to app.module.ts:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Task } from './tasks/task.entity';
import { TasksModule } from './tasks/tasks.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'admin',
password: 'admin',
database: 'tasks',
entities: [Task],
synchronize: true,
}),
TasksModule,
],
})
export class AppModule {}
task.entity.ts:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Task {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column({ default: false })
isCompleted: boolean;
}
Inject repositories with @InjectRepository(Task) in your services.
🚀 Conclusion
Prisma gives you type safety and an amazing developer experience.
TypeORM is feature-rich and widely adopted.
👉 Choose Prisma if you want speed + DX.
👉 Choose TypeORM if you need complex queries and legacy support.
Now your NestJS app can persist data into a database like a real-world backend.
In the next part of the series, we’ll tackle Authentication & Authorization with JWTs and Guards.
💡 Found this useful? Drop a ❤️ on Dev.to and follow me for Part 3 of the NestJS Expert Series.
Top comments (0)