From Beginner to Pro: Setting Up a TypeScript NestJS Backend with Prisma
A complete, production-ready guide to building scalable backend applications with NestJS, TypeScript, and Prisma ORM
π Table of Contents
- Why This Stack?
- Prerequisites
- Project Structure
- Quick Start
- Step-by-Step Setup
- Database Configuration
- Building Your First API
- Authentication & Authorization
- Testing
- Deployment
- Best Practices
- Troubleshooting
- Resources
π Why This Stack?
NestJS
- Enterprise-grade architecture with dependency injection
- Built on Express/Fastify - familiar, battle-tested foundations
- TypeScript-first - catch bugs before runtime
- Microservices ready - scale as you grow
- Amazing ecosystem - authentication, GraphQL, WebSockets, etc.
Prisma
- Type-safe database access - autocomplete for your queries
- Auto-generated migrations - version control your database schema
- Multi-database support - PostgreSQL, MySQL, MongoDB, SQLite
- Prisma Studio - visual database browser included
- Performance - optimized queries out of the box
TypeScript
- Reduced bugs - 15% fewer bugs in production (Microsoft Research)
- Better refactoring - rename with confidence
- Self-documenting code - types as documentation
- IDE superpowers - IntelliSense everywhere
The result? A backend that's maintainable, scalable, and a joy to develop.
β Prerequisites
Before you begin, ensure you have:
- Node.js (v18+) - Download here
- npm or yarn - Comes with Node.js
- PostgreSQL (v12+) - Download here or use Docker
- Git - Download here
- Basic knowledge of:
- JavaScript/TypeScript
- REST APIs
- SQL (helpful but not required)
Quick Environment Check
node --version # Should be v18 or higher
npm --version # Should be 8 or higher
psql --version # Should be 12 or higher
π Project Structure
nestjs-prisma-backend/
βββ prisma/
β βββ schema.prisma # Database schema definition
β βββ migrations/ # Database migration history
β βββ seed.ts # Seed data for development
βββ src/
β βββ auth/ # Authentication module
β β βββ auth.controller.ts
β β βββ auth.service.ts
β β βββ auth.module.ts
β β βββ dto/ # Data Transfer Objects
β β βββ guards/ # Auth guards
β β βββ strategies/ # Passport strategies
β βββ users/ # Users module
β β βββ users.controller.ts
β β βββ users.service.ts
β β βββ users.module.ts
β β βββ dto/
β βββ posts/ # Posts module (example)
β β βββ posts.controller.ts
β β βββ posts.service.ts
β β βββ posts.module.ts
β β βββ dto/
β βββ common/ # Shared utilities
β β βββ decorators/ # Custom decorators
β β βββ filters/ # Exception filters
β β βββ guards/ # Global guards
β β βββ interceptors/ # Response interceptors
β β βββ pipes/ # Validation pipes
β βββ config/ # Configuration
β β βββ database.config.ts
β β βββ app.config.ts
β βββ prisma/ # Prisma service
β β βββ prisma.service.ts
β β βββ prisma.module.ts
β βββ app.module.ts # Root module
β βββ app.controller.ts # Root controller
β βββ app.service.ts # Root service
β βββ main.ts # Application entry point
βββ test/ # E2E tests
β βββ app.e2e-spec.ts
β βββ jest-e2e.json
βββ .env # Environment variables
βββ .env.example # Example environment file
βββ .eslintrc.js # ESLint configuration
βββ .prettierrc # Prettier configuration
βββ docker-compose.yml # Docker setup (optional)
βββ Dockerfile # Production Docker image
βββ nest-cli.json # NestJS CLI configuration
βββ package.json # Dependencies
βββ tsconfig.json # TypeScript configuration
βββ README.md # You are here!
β‘ Quick Start
Get up and running in under 5 minutes:
# 1. Clone or create new project
npx @nestjs/cli new nestjs-prisma-backend
cd nestjs-prisma-backend
# 2. Install Prisma
npm install prisma @prisma/client
npm install -D prisma
# 3. Initialize Prisma
npx prisma init
# 4. Set up environment variables
cp .env.example .env
# Edit .env with your database credentials
# 5. Create your schema (see Database Configuration section)
# 6. Run migrations
npx prisma migrate dev --name init
# 7. Generate Prisma Client
npx prisma generate
# 8. Start development server
npm run start:dev
# π Visit http://localhost:3000
π οΈ Step-by-Step Setup
1. Create NestJS Project
# Install NestJS CLI globally
npm install -g @nestjs/cli
# Create new project
nest new nestjs-prisma-backend
# Choose your package manager (npm/yarn/pnpm)
cd nestjs-prisma-backend
2. Install Dependencies
# Prisma ORM
npm install @prisma/client
npm install -D prisma
# Configuration
npm install @nestjs/config
# Validation
npm install class-validator class-transformer
# Authentication (we'll add this later)
npm install @nestjs/jwt @nestjs/passport passport passport-jwt
npm install -D @types/passport-jwt
# Security
npm install helmet
npm install @nestjs/throttler
# Documentation
npm install @nestjs/swagger swagger-ui-express
3. Initialize Prisma
npx prisma init
This creates:
-
prisma/schema.prisma- Your database schema -
.env- Environment variables file
4. Configure Environment Variables
Create .env file:
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/nestjs_db?schema=public"
# Application
PORT=3000
NODE_ENV=development
# JWT (for authentication)
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=7d
# CORS
CORS_ORIGIN=http://localhost:3000
Create .env.example (commit this to git):
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/database_name?schema=public"
# Application
PORT=3000
NODE_ENV=development
# JWT
JWT_SECRET=change-me-in-production
JWT_EXPIRES_IN=7d
# CORS
CORS_ORIGIN=http://localhost:3000
ποΈ Database Configuration
Step 1: Define Your Schema
Edit prisma/schema.prisma:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
name String?
password String
role Role @default(USER)
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
}
model Post {
id String @id @default(uuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
tags Tag[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("posts")
@@index([authorId])
}
model Tag {
id String @id @default(uuid())
name String @unique
posts Post[]
@@map("tags")
}
enum Role {
USER
ADMIN
MODERATOR
}
Step 2: Create Migration
npx prisma migrate dev --name init
This will:
- Create the database if it doesn't exist
- Generate SQL migration files
- Apply migrations to your database
- Generate Prisma Client
Step 3: Create Prisma Service
Create src/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();
console.log('β
Database connected');
}
async onModuleDestroy() {
await this.$disconnect();
console.log('β Database disconnected');
}
// Helper method for transactions
async executeTransaction<T>(fn: (prisma: PrismaClient) => Promise<T>): Promise<T> {
return this.$transaction(fn);
}
}
Create src/prisma/prisma.module.ts:
import { Module, Global } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global()
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
Step 4: Register Prisma Module
Update src/app.module.ts:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaModule } from './prisma/prisma.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
PrismaModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
ποΈ Building Your First API
Step 1: Generate Resource
nest generate resource users
Choose:
- Transport layer: REST API
- Generate CRUD entry points: Yes
Step 2: Create DTOs
Create src/users/dto/create-user.dto.ts:
import { IsEmail, IsNotEmpty, IsString, MinLength, IsOptional, IsEnum } from 'class-validator';
import { Role } from '@prisma/client';
export class CreateUserDto {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@MinLength(8, { message: 'Password must be at least 8 characters long' })
password: string;
@IsOptional()
@IsEnum(Role)
role?: Role;
}
Create src/users/dto/update-user.dto.ts:
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
import { IsOptional, IsString } from 'class-validator';
export class UpdateUserDto extends PartialType(CreateUserDto) {
@IsOptional()
@IsString()
name?: string;
}
Step 3: Implement Service
Update src/users/users.service.ts:
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import * as bcrypt from 'bcrypt';
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
async create(createUserDto: CreateUserDto) {
// Check if user already exists
const existingUser = await this.prisma.user.findUnique({
where: { email: createUserDto.email },
});
if (existingUser) {
throw new ConflictException('User with this email already exists');
}
// Hash password
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
// Create user
const user = await this.prisma.user.create({
data: {
...createUserDto,
password: hashedPassword,
},
select: {
id: true,
email: true,
name: true,
role: true,
createdAt: true,
updatedAt: true,
},
});
return user;
}
async findAll() {
return this.prisma.user.findMany({
select: {
id: true,
email: true,
name: true,
role: true,
createdAt: true,
updatedAt: true,
},
});
}
async findOne(id: string) {
const user = await this.prisma.user.findUnique({
where: { id },
select: {
id: true,
email: true,
name: true,
role: true,
posts: {
select: {
id: true,
title: true,
published: true,
createdAt: true,
},
},
createdAt: true,
updatedAt: true,
},
});
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
return user;
}
async findByEmail(email: string) {
return this.prisma.user.findUnique({
where: { email },
});
}
async update(id: string, updateUserDto: UpdateUserDto) {
// Check if user exists
await this.findOne(id);
// If password is being updated, hash it
if (updateUserDto.password) {
updateUserDto.password = await bcrypt.hash(updateUserDto.password, 10);
}
const user = await this.prisma.user.update({
where: { id },
data: updateUserDto,
select: {
id: true,
email: true,
name: true,
role: true,
createdAt: true,
updatedAt: true,
},
});
return user;
}
async remove(id: string) {
// Check if user exists
await this.findOne(id);
await this.prisma.user.delete({
where: { id },
});
return { message: 'User deleted successfully' };
}
}
Install bcrypt:
npm install bcrypt
npm install -D @types/bcrypt
Step 4: Update Controller
Update src/users/users.controller.ts:
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
HttpCode,
HttpStatus,
UseGuards,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
@HttpCode(HttpStatus.CREATED)
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(id, updateUserDto);
}
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
remove(@Param('id') id: string) {
return this.usersService.remove(id);
}
}
Step 5: Enable Validation
Update src/main.ts:
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
import * as helmet from 'helmet';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Global validation pipe
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // Strip properties that don't have decorators
forbidNonWhitelisted: true, // Throw error if non-whitelisted properties exist
transform: true, // Auto-transform payloads to DTO instances
}),
);
// Security
app.use(helmet());
// CORS
app.enableCors({
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
credentials: true,
});
// Global prefix
app.setGlobalPrefix('api/v1');
const port = process.env.PORT || 3000;
await app.listen(port);
console.log(`π Application is running on: http://localhost:${port}`);
}
bootstrap();
Step 6: Test Your API
# Start the server
npm run start:dev
# Create a user (using curl or Postman)
curl -X POST http://localhost:3000/api/v1/users \
-H "Content-Type: application/json" \
-d '{
"email": "john@example.com",
"name": "John Doe",
"password": "securepassword123"
}'
# Get all users
curl http://localhost:3000/api/v1/users
# Get specific user
curl http://localhost:3000/api/v1/users/{user-id}
π Authentication & Authorization
Step 1: Generate Auth Module
nest generate module auth
nest generate service auth
nest generate controller auth
Step 2: Create Auth DTOs
Create src/auth/dto/login.dto.ts:
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class LoginDto {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
Create src/auth/dto/register.dto.ts:
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator';
export class RegisterDto {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
name: string;
@IsString()
@MinLength(8)
password: string;
}
Step 3: Implement Auth Service
Update src/auth/auth.service.ts:
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from '../prisma/prisma.service';
import { UsersService } from '../users/users.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import * as bcrypt from 'bcrypt';
@Injectable()
export class AuthService {
constructor(
private prisma: PrismaService,
private jwtService: JwtService,
private usersService: UsersService,
) {}
async register(registerDto: RegisterDto) {
const user = await this.usersService.create(registerDto);
const token = this.generateToken(user.id, user.email);
return {
user,
access_token: token,
};
}
async login(loginDto: LoginDto) {
const user = await this.usersService.findByEmail(loginDto.email);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
const isPasswordValid = await bcrypt.compare(loginDto.password, user.password);
if (!isPasswordValid) {
throw new UnauthorizedException('Invalid credentials');
}
const token = this.generateToken(user.id, user.email);
return {
user: {
id: user.id,
email: user.email,
name: user.name,
role: user.role,
},
access_token: token,
};
}
async validateUser(userId: string) {
return this.usersService.findOne(userId);
}
private generateToken(userId: string, email: string): string {
const payload = { sub: userId, email };
return this.jwtService.sign(payload);
}
}
Step 4: Create JWT Strategy
Create src/auth/strategies/jwt.strategy.ts:
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from '../auth.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
const user = await this.authService.validateUser(payload.sub);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
Step 5: Create JWT Guard
Create src/auth/guards/jwt-auth.guard.ts:
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
Step 6: Create Custom Decorator
Create src/common/decorators/current-user.decorator.ts:
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
Step 7: Update Auth Module
Update src/auth/auth.module.ts:
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './strategies/jwt.strategy';
import { UsersModule } from '../users/users.module';
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: process.env.JWT_EXPIRES_IN || '7d' },
}),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
Step 8: Update Auth Controller
Update src/auth/auth.controller.ts:
import { Controller, Post, Body, HttpCode, HttpStatus, Get, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { CurrentUser } from '../common/decorators/current-user.decorator';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('register')
@HttpCode(HttpStatus.CREATED)
register(@Body() registerDto: RegisterDto) {
return this.authService.register(registerDto);
}
@Post('login')
@HttpCode(HttpStatus.OK)
login(@Body() loginDto: LoginDto) {
return this.authService.login(loginDto);
}
@Get('me')
@UseGuards(JwtAuthGuard)
getProfile(@CurrentUser() user: any) {
return user;
}
}
Step 9: Protect Routes
Update src/users/users.controller.ts to add authentication:
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { CurrentUser } from '../common/decorators/current-user.decorator';
@Controller('users')
@UseGuards(JwtAuthGuard) // Protect all routes
export class UsersController {
// ... existing code
@Get('profile')
getProfile(@CurrentUser() user: any) {
return user;
}
}
Step 10: Test Authentication
# Register a new user
curl -X POST http://localhost:3000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"name": "Test User",
"password": "password123"
}'
# Login
curl -X POST http://localhost:3000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "password123"
}'
# Use the returned token to access protected routes
curl http://localhost:3000/api/v1/auth/me \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
π§ͺ Testing
Unit Tests
Create src/users/users.service.spec.ts:
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
import { PrismaService } from '../prisma/prisma.service';
describe('UsersService', () => {
let service: UsersService;
let prisma: PrismaService;
const mockPrismaService = {
user: {
create: jest.fn(),
findMany: jest.fn(),
findUnique: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
},
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{
provide: PrismaService,
useValue: mockPrismaService,
},
],
}).compile();
service = module.get<UsersService>(UsersService);
prisma = module.get<PrismaService>(PrismaService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('findAll', () => {
it('should return an array of users', async () => {
const mockUsers = [
{ id: '1', email: 'test@example.com', name: 'Test User' },
];
mockPrismaService.user.findMany.mockResolvedValue(mockUsers);
const result = await service.findAll();
expect(result).toEqual(mockUsers);
expect(prisma.user.findMany).toHaveBeenCalled();
});
});
});
Run tests:
npm run test # Unit tests
npm run test:watch # Watch mode
npm run test:cov # Coverage report
E2E Tests
Create test/users.e2e-spec.ts:
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('Users (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(new ValidationPipe());
await app.init();
});
afterAll(async () => {
await app.close();
});
it('/api/v1/users (POST)', () => {
return request(app.getHttpServer())
.post('/api/v1/users')
.send({
email: 'test@example.com',
name: 'Test User',
password: 'password123',
})
.expect(201)
.expect((res) => {
expect(res.body).toHaveProperty('id');
expect(res.body.email).toBe('test@example.com');
});
});
it('/api/v1/users (GET)', () => {
return request(app.getHttpServer())
.get('/api/v1/users')
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
});
});
});
Run e2e tests:
npm run test:e2e
π’ Deployment
Docker Setup
Create Dockerfile:
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY prisma ./prisma/
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
COPY prisma ./prisma/
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["sh", "-c", "npx prisma migrate deploy && node dist/main"]
Create docker-compose.yml:
version: '3.8'
services:
postgres:
image: postgres:15-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: nestjs_db
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
app:
build: .
ports:
- '3000:3000'
environment:
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/nestjs_db?schema=public
JWT_SECRET: your-production-secret-change-this
NODE_ENV: production
depends_on:
- postgres
volumes:
postgres_data:
Run with Docker:
docker-compose up -d
Deployment Checklist
- [ ] Set strong
JWT_SECRETin production - [ ] Use environment-specific
.envfiles - [ ] Enable HTTPS/SSL
- [ ] Set up proper CORS origins
- [ ] Enable rate limiting
- [ ] Set up logging (Winston, Pino)
- [ ] Add health check endpoint
- [ ] Set up monitoring (Sentry, DataDog)
- [ ] Configure reverse proxy (Nginx, Caddy)
- [ ] Set up CI/CD pipeline
- [ ] Database backups configured
- [ ] Review Prisma connection pooling
π Best Practices
1. Error Handling
Create src/common/filters/http-exception.filter.ts:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message =
exception instanceof HttpException
? exception.getResponse()
: 'Internal server error';
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message,
});
}
}
2. Logging
Create src/common/interceptors/logging.interceptor.ts:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private readonly logger = new Logger(LoggingInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { method, url } = request;
const now = Date.now();
return next.handle().pipe(
tap(() => {
const response = context.switchToHttp().getResponse();
const { statusCode } = response;
const delay = Date.now() - now;
this.logger.log(`${method} ${url} ${statusCode} - ${delay}ms`);
}),
);
}
}
3. API Documentation with Swagger
Update src/main.ts:
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Swagger configuration
const config = new DocumentBuilder()
.setTitle('NestJS Prisma API')
.setDescription('API documentation for NestJS + Prisma backend')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
await app.listen(3000);
console.log(`π API Documentation: http://localhost:3000/api/docs`);
}
4. Rate Limiting
Install throttler:
npm install @nestjs/throttler
Update app.module.ts:
import { ThrottlerModule } from '@nestjs/throttler';
@Module({
imports: [
ThrottlerModule.forRoot([{
ttl: 60000, // 1 minute
limit: 10, // 10 requests per minute
}]),
// ... other imports
],
})
export class AppModule {}
5. Database Seeding
Create prisma/seed.ts:
import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function main() {
// Create admin user
const hashedPassword = await bcrypt.hash('admin123', 10);
const admin = await prisma.user.upsert({
where: { email: 'admin@example.com' },
update: {},
create: {
email: 'admin@example.com',
name: 'Admin User',
password: hashedPassword,
role: 'ADMIN',
},
});
console.log('β
Seed data created:', { admin });
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
Add to package.json:
{
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
}
Run seed:
npx prisma db seed
π Troubleshooting
Common Issues
1. Prisma Client Not Generated
# Generate Prisma Client
npx prisma generate
2. Migration Failed
# Reset database (WARNING: deletes all data)
npx prisma migrate reset
# Or create a new migration
npx prisma migrate dev --name fix_migration
3. TypeScript Errors
# Clear cache and rebuild
rm -rf dist node_modules package-lock.json
npm install
npm run build
4. Database Connection Issues
Check your .env file:
# Correct format
DATABASE_URL="postgresql://username:password@localhost:5432/database_name"
# Common mistakes:
# β Missing quotes
# β Wrong port
# β Incorrect schema name
5. JWT Authentication Not Working
- Verify
JWT_SECRETis set in.env - Check token format:
Bearer <token> - Ensure
JwtAuthGuardis applied correctly - Verify user exists in database
π Resources
Official Documentation
Learning Resources
Tools
- Prisma Studio - Visual database browser
- Postman - API testing
- DBeaver - Database management
Community
π¨βπ» Author
Rajat
β Support
If this guide helped you, please consider:
- Sharing it with other developers
- Following for more content
Happy Coding! π
Made with β€οΈ by Rajat




Top comments (0)