NestJS Samples

NestJS enterprise Node.js framework examples including modules, controllers, services, and deployment

Key Facts

Category
Web Frameworks
Items
4
Format Families
sample

Sample Overview

NestJS enterprise Node.js framework examples including modules, controllers, services, and deployment This sample set belongs to Web Frameworks and can be used to test related workflows inside Elysia Tools.

💻 NestJS Hello World typescript

🟢 simple

Basic NestJS application setup and Hello World implementation

// NestJS Hello World Examples

// 1. Basic NestJS Application Structure
// src/main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { ValidationPipe } from '@nestjs/common'

async function bootstrap() {
  const app = await NestFactory.create(AppModule)

  // Enable validation globally
  app.useGlobalPipes(new ValidationPipe())

  // Enable CORS
  app.enableCors()

  // Prefix all routes
  app.setGlobalPrefix('api')

  await app.listen(3000)
  console.log('🚀 Application is running on: http://localhost:3000')
}

bootstrap()

// src/app.module.ts
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

// src/app.controller.ts
import { Controller, Get, Param, Query, Post, Body } from '@nestjs/common'
import { AppService } from './app.service'
import { CreateHelloDto } from './dto/create-hello.dto'

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello()
  }

  @Get('hello/:name')
  getHelloName(@Param('name') name: string): string {
    return this.appService.getHelloName(name)
  }

  @Get('greet')
  greet(@Query('name') name: string): object {
    return this.appService.greet(name)
  }

  @Post('hello')
  createHello(@Body() createHelloDto: CreateHelloDto): object {
    return this.appService.createHello(createHelloDto)
  }
}

// src/app.service.ts
import { Injectable } from '@nestjs/common'

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello, World!'
  }

  getHelloName(name: string): string {
    return `Hello, ${name}!`
  }

  greet(name: string): object {
    return {
      message: `Hello, ${name || 'Guest'}!`,
      timestamp: new Date().toISOString(),
      framework: 'NestJS'
    }
  }

  createHello(data: { name: string; message: string }): object {
    return {
      id: Math.random(),
      ...data,
      createdAt: new Date().toISOString()
    }
  }
}

// src/dto/create-hello.dto.ts
import { IsString, IsOptional } from 'class-validator'

export class CreateHelloDto {
  @IsString()
  name: string

  @IsString()
  @IsOptional()
  message?: string
}

// 2. NestJS with Multiple Controllers
// src/users/users.controller.ts
import { Controller, Get, Post, Put, Delete, Param, Body, HttpStatus, HttpException } from '@nestjs/common'
import { UsersService } from './users.service'
import { CreateUserDto, UpdateUserDto } from './dto'

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  async create(@Body() createUserDto: CreateUserDto) {
    try {
      return await this.usersService.create(createUserDto)
    } catch (error) {
      throw new HttpException(error.message, HttpStatus.BAD_REQUEST)
    }
  }

  @Get()
  async findAll() {
    return this.usersService.findAll()
  }

  @Get(':id')
  async findOne(@Param('id') id: string) {
    const user = await this.usersService.findOne(+id)
    if (!user) {
      throw new HttpException('User not found', HttpStatus.NOT_FOUND)
    }
    return user
  }

  @Put(':id')
  async update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    const user = await this.usersService.update(+id, updateUserDto)
    if (!user) {
      throw new HttpException('User not found', HttpStatus.NOT_FOUND)
    }
    return user
  }

  @Delete(':id')
  async remove(@Param('id') id: string) {
    const result = await this.usersService.remove(+id)
    if (!result) {
      throw new HttpException('User not found', HttpStatus.NOT_FOUND)
    }
    return { message: 'User deleted successfully' }
  }
}

// src/users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common'
import { CreateUserDto, UpdateUserDto } from './dto'

export interface User {
  id: number
  name: string
  email: string
  age?: number
  createdAt: Date
  updatedAt: Date
}

@Injectable()
export class UsersService {
  private users: User[] = []
  private nextId = 1

  create(createUserDto: CreateUserDto): User {
    const user: User = {
      id: this.nextId++,
      ...createUserDto,
      createdAt: new Date(),
      updatedAt: new Date()
    }
    this.users.push(user)
    return user
  }

  findAll(): User[] {
    return this.users
  }

  findOne(id: number): User | undefined {
    return this.users.find(user => user.id === id)
  }

  update(id: number, updateUserDto: UpdateUserDto): User | undefined {
    const userIndex = this.users.findIndex(user => user.id === id)
    if (userIndex === -1) {
      return undefined
    }

    this.users[userIndex] = {
      ...this.users[userIndex],
      ...updateUserDto,
      updatedAt: new Date()
    }

    return this.users[userIndex]
  }

  remove(id: number): boolean {
    const userIndex = this.users.findIndex(user => user.id === id)
    if (userIndex === -1) {
      return false
    }

    this.users.splice(userIndex, 1)
    return true
  }
}

// src/users/dto/index.ts
export class CreateUserDto {
  name: string
  email: string
  age?: number
}

export class UpdateUserDto {
  name?: string
  email?: string
  age?: number
}

// 3. NestJS with Modules and Dependencies
// src/users/users.module.ts
import { Module } from '@nestjs/common'
import { UsersController } from './users.controller'
import { UsersService } from './users.service'
import { DatabaseModule } from '../database/database.module'

@Module({
  imports: [DatabaseModule],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

// src/database/database.module.ts
import { Module, Global } from '@nestjs/common'
import { DatabaseService } from './database.service'

@Global()
@Module({
  providers: [DatabaseService],
  exports: [DatabaseService],
})
export class DatabaseModule {}

// src/database/database.service.ts
import { Injectable } from '@nestjs/common'

export interface DatabaseConnection {
  connect(): Promise<void>
  disconnect(): Promise<void>
  query(sql: string, params?: any[]): Promise<any>
}

@Injectable()
export class DatabaseService {
  private connection: DatabaseConnection

  constructor() {
    this.connection = new PostgresConnection()
  }

  async connect() {
    await this.connection.connect()
  }

  async disconnect() {
    await this.connection.disconnect()
  }

  async query(sql: string, params?: any[]) {
    return this.connection.query(sql, params)
  }
}

class PostgresConnection implements DatabaseConnection {
  async connect() {
    console.log('Connecting to PostgreSQL...')
  }

  async disconnect() {
    console.log('Disconnecting from PostgreSQL...')
  }

  async query(sql: string, params?: any[]) {
    console.log(`Query: ${sql}`, params)
    return { rows: [] }
  }
}

// 4. NestJS with Custom Decorators
// src/decorators/user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common'

export const User = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest()
    const user = request.user

    return data ? user?.[data] : user
  },
)

// src/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common'

export const Roles = (...roles: string[]) => SetMetadata('roles', roles)

// Usage in controllers
// src/admin/admin.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common'
import { RolesGuard } from './guards/roles.guard'
import { Roles } from '../decorators/roles.decorator'
import { User } from '../decorators/user.decorator'

@Controller('admin')
@UseGuards(RolesGuard)
@Roles('admin')
export class AdminController {
  @Get('dashboard')
  getDashboard(@User('id') userId: number) {
    return {
      message: 'Admin dashboard',
      userId,
      data: 'Sensitive admin data'
    }
  }

  @Get('users')
  getUsers() {
    return {
      message: 'All users list',
      users: []
    }
  }
}

// 5. NestJS with Guards and Middleware
// src/guards/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest()
    const token = request.headers.authorization?.replace('Bearer ', '')

    // Simple token validation
    if (!token || token !== 'valid-token') {
      return false
    }

    // Attach user to request
    request.user = {
      id: 1,
      name: 'John Doe',
      role: 'user'
    }

    return true
  }
}

// src/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
import { Reflector } from '@nestjs/core'

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler())
    if (!requiredRoles) {
      return true
    }

    const { user } = context.switchToHttp().getRequest()
    return requiredRoles.some(role => user.role === role)
  }
}

// src/middleware/logging.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common'
import { Request, Response, NextFunction } from 'express'

@Injectable()
export class LoggingMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const start = Date.now()
    const { method, url, ip } = req

    console.log(`[${new Date().toISOString()}] ${method} ${url} - ${ip}`)

    res.on('finish', () => {
      const duration = Date.now() - start
      console.log(`[${new Date().toISOString()}] ${method} ${url} - ${res.statusCode} (${duration}ms)`)
    })

    next()
  }
}

// Apply middleware in app module
// src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
import { LoggingMiddleware } from './middleware/logging.middleware'
import { UsersModule } from './users/users.module'
import { AuthModule } from './auth/auth.module'

@Module({
  imports: [UsersModule, AuthModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggingMiddleware)
      .forRoutes('*')
  }
}

// 6. NestJS with Interceptors and Filters
// src/interceptors/transform.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'

export interface Response<T> {
  data: T
  timestamp: string
  success: boolean
}

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
    return next.handle().pipe(
      map(data => ({
        data,
        timestamp: new Date().toISOString(),
        success: true
      }))
    )
  }
}

// src/interceptors/caching.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'
import { Observable, of } from 'rxjs'
import { tap } from 'rxjs/operators'

@Injectable()
export class CachingInterceptor implements NestInterceptor {
  private cache = new Map<string, any>()

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const key = context.switchToHttp().getRequest().url

    if (this.cache.has(key)) {
      return of(this.cache.get(key))
    }

    return next.handle().pipe(
      tap(data => {
        this.cache.set(key, data)
      })
    )
  }
}

// src/filters/http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'
import { Request, Response } from 'express'

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp()
    const response = ctx.getResponse<Response>()
    const request = ctx.getRequest<Request>()

    const status = exception.getStatus()
    const exceptionResponse = exception.getResponse()

    response
      .status(status)
      .json({
        statusCode: status,
        timestamp: new Date().toISOString(),
        path: request.url,
        method: request.method,
        message: typeof exceptionResponse === 'string'
          ? exceptionResponse
          : (exceptionResponse as any).message,
        error: exception.constructor.name
      })
  }
}

// Apply globally in main.ts
// src/main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { ValidationPipe } from '@nestjs/common'
import { TransformInterceptor } from './interceptors/transform.interceptor'

async function bootstrap() {
  const app = await NestFactory.create(AppModule)

  app.useGlobalPipes(new ValidationPipe())
  app.useGlobalFilters(new HttpExceptionFilter())
  app.useGlobalInterceptors(new TransformInterceptor())

  await app.listen(3000)
}

bootstrap()

// 7. NestJS with WebSocket Support
// src/websocket/websocket.gateway.ts
import {
  WebSocketGateway,
  SubscribeMessage,
  WebSocketServer,
  OnGatewayInit,
  OnGatewayConnection,
  OnGatewayDisconnect,
  ConnectedSocket,
  MessageBody,
} from '@nestjs/websockets'
import { Server } from 'socket.io'

export interface ClientData {
  id: string
  name: string
  room?: string
}

export interface MessageData {
  text: string
  sender: string
  timestamp: string
  room?: string
}

@WebSocketGateway({ cors: { origin: '*' } })
export class WebSocketGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer() server: Server
  private clients: Map<string, ClientData> = new Map()

  afterInit(server: Server) {
    console.log('WebSocket Gateway initialized')
  }

  handleConnection(client: any) {
    console.log(`Client connected: ${client.id}`)
    this.clients.set(client.id, {
      id: client.id,
      name: `User${client.id.substr(-4)}`
    })

    client.emit('connected', {
      message: 'Connected to chat server',
      clientId: client.id
    })
  }

  handleDisconnect(client: any) {
    console.log(`Client disconnected: ${client.id}`)
    const clientData = this.clients.get(client.id)
    this.clients.delete(client.id)

    if (clientData) {
      client.broadcast.emit('userLeft', {
        message: `${clientData.name} left the chat`,
        user: clientData
      })
    }
  }

  @SubscribeMessage('joinRoom')
  handleJoinRoom(
    @MessageBody() data: { room: string },
    @ConnectedSocket() client: any,
  ) {
    const clientData = this.clients.get(client.id)
    if (clientData) {
      clientData.room = data.room
      client.join(data.room)

      client.emit('joinedRoom', {
        room: data.room,
        message: `Joined room: ${data.room}`
      })

      client.to(data.room).emit('userJoinedRoom', {
        message: `${clientData.name} joined the room`,
        user: clientData
      })
    }
  }

  @SubscribeMessage('sendMessage')
  handleMessage(
    @MessageBody() data: { text: string; room?: string },
    @ConnectedSocket() client: any,
  ) {
    const clientData = this.clients.get(client.id)
    if (!clientData) return

    const message: MessageData = {
      text: data.text,
      sender: clientData.name,
      timestamp: new Date().toISOString(),
      room: data.room
    }

    if (data.room) {
      this.server.to(data.room).emit('newMessage', message)
    } else {
      client.broadcast.emit('newMessage', message)
    }

    client.emit('messageSent', message)
  }

  @SubscribeMessage('getUsers')
  handleGetUsers(@ConnectedSocket() client: any) {
    const users = Array.from(this.clients.values())
    client.emit('usersList', users)
  }
}

// src/websocket/websocket.module.ts
import { Module } from '@nestjs/common'
import { WebSocketGateway } from './websocket.gateway'

@Module({
  providers: [WebSocketGateway],
  exports: [WebSocketGateway],
})
export class WebSocketModule {}

// 8. NestJS Configuration and Environment
// src/config/configuration.ts
export default () => ({
  port: parseInt(process.env.PORT, 10) || 3000,
  database: {
    host: process.env.DATABASE_HOST || 'localhost',
    port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
    username: process.env.DATABASE_USERNAME || 'postgres',
    password: process.env.DATABASE_PASSWORD || 'password',
    database: process.env.DATABASE_NAME || 'nestjs_db'
  },
  jwt: {
    secret: process.env.JWT_SECRET || 'default-secret',
    expiresIn: process.env.JWT_EXPIRES_IN || '1h'
  },
  redis: {
    host: process.env.REDIS_HOST || 'localhost',
    port: parseInt(process.env.REDIS_PORT, 10) || 6379,
    password: process.env.REDIS_PASSWORD || undefined
  }
})

// src/config/config.module.ts
import { Module } from '@nestjs/common'
import { ConfigModule, ConfigService } from '@nestjs/config'
import configuration from './configuration'

@Module({
  imports: [ConfigModule.forRoot({
    load: [configuration],
    isGlobal: true
  })],
  providers: [ConfigService],
  exports: [ConfigService],
})
export class AppConfigModule {}

// 9. NestJS Testing
// src/users/users.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing'
import { UsersService } from './users.service'
import { CreateUserDto } from './dto'

describe('UsersService', () => {
  let service: UsersService

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UsersService],
    }).compile()

    service = module.get<UsersService>(UsersService)
  })

  it('should be defined', () => {
    expect(service).toBeDefined()
  })

  describe('create', () => {
    it('should create a user successfully', () => {
      const createUserDto: CreateUserDto = {
        name: 'John Doe',
        email: '[email protected]',
        age: 30
      }

      const user = service.create(createUserDto)

      expect(user).toHaveProperty('id')
      expect(user.name).toBe(createUserDto.name)
      expect(user.email).toBe(createUserDto.email)
      expect(user.age).toBe(createUserDto.age)
      expect(user).toHaveProperty('createdAt')
      expect(user).toHaveProperty('updatedAt')
    })

    it('should auto-increment user ID', () => {
      const user1 = service.create({ name: 'User 1', email: '[email protected]' })
      const user2 = service.create({ name: 'User 2', email: '[email protected]' })

      expect(user2.id).toBe(user1.id + 1)
    })
  })

  describe('findAll', () => {
    it('should return empty array when no users exist', () => {
      const users = service.findAll()
      expect(users).toEqual([])
    })

    it('should return all users', () => {
      const user1 = service.create({ name: 'User 1', email: '[email protected]' })
      const user2 = service.create({ name: 'User 2', email: '[email protected]' })

      const users = service.findAll()
      expect(users).toHaveLength(2)
      expect(users).toContainEqual(user1)
      expect(users).toContainEqual(user2)
    })
  })
})

// src/users/users.controller.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing'
import { INestApplication } from '@nestjs/common'
import * as request from 'supertest'
import { AppModule } from '../app.module'

describe('UsersController (e2e)', () => {
  let app: INestApplication

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile()

    app = moduleFixture.createNestApplication()
    await app.init()
  })

  describe('/users (POST)', () => {
    it('should create a new user', () => {
      const createUserDto = {
        name: 'John Doe',
        email: '[email protected]'
      }

      return request(app.getHttpServer())
        .post('/api/users')
        .send(createUserDto)
        .expect(201)
        .expect((res) => {
          expect(res.body.name).toBe(createUserDto.name)
          expect(res.body.email).toBe(createUserDto.email)
          expect(res.body).toHaveProperty('id')
          expect(res.body).toHaveProperty('createdAt')
        })
    })

    it('should return 400 for invalid data', () => {
      const invalidUserDto = {
        name: '',
        email: 'invalid-email'
      }

      return request(app.getHttpServer())
        .post('/api/users')
        .send(invalidUserDto)
        .expect(400)
    })
  })

  describe('/users (GET)', () => {
    it('should return array of users', () => {
      return request(app.getHttpServer())
        .get('/api/users')
        .expect(200)
        .expect((res) => {
          expect(Array.isArray(res.body)).toBe(true)
        })
    })
  })
})

// 10. Package.json Configuration
{
  "name": "nestjs-app",
  "version": "0.0.1",
  "description": "NestJS application example",
  "author": "",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/platform-express": "^10.0.0",
    "@nestjs/websockets": "^10.0.0",
    "@nestjs/config": "^3.0.0",
    "@nestjs/platform-socket.io": "^10.0.0",
    "class-validator": "^0.14.0",
    "class-transformer": "^0.5.1",
    "reflect-metadata": "^0.1.13",
    "rxjs": "^7.8.1",
    "socket.io": "^4.7.2"
  },
  "devDependencies": {
    "@nestjs/cli": "^10.0.0",
    "@nestjs/schematics": "^10.0.0",
    "@nestjs/testing": "^10.0.0",
    "@types/express": "^4.17.17",
    "@types/jest": "^29.5.2",
    "@types/node": "^20.3.1",
    "@types/supertest": "^2.0.12",
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "@typescript-eslint/parser": "^6.0.0",
    "eslint": "^8.42.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-prettier": "^5.0.0",
    "jest": "^29.5.0",
    "prettier": "^3.0.0",
    "source-map-support": "^0.5.21",
    "supertest": "^6.3.3",
    "ts-jest": "^29.1.0",
    "ts-loader": "^9.4.3",
    "ts-node": "^10.9.1",
    "tsconfig-paths": "^4.2.0",
    "typescript": "^5.1.3"
  },
  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "rootDir": "src",
    "testRegex": ".*\.spec\.ts$",
    "transform": {
      "^.+\.(t|j)s$": "ts-jest"
    },
    "collectCoverageFrom": [
      "**/*.(t|j)s"
    ],
    "coverageDirectory": "../coverage",
    "testEnvironment": "node"
  }
}

💻 NestJS Modules and Services typescript

🟡 intermediate

Module architecture and service patterns in NestJS applications

// NestJS Modules and Services Examples

// 1. Basic Module Structure
// src/core/core.module.ts
import { Module } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { LoggerModule } from '@nestjs/logger'
import { DatabaseModule } from './database/database.module'

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: '.env'
    }),
    LoggerModule.forRoot({
      isGlobal: true
    }),
    DatabaseModule
  ],
  exports: [DatabaseModule]
})
export class CoreModule {}

// src/core/database/database.module.ts
import { Module } from '@nestjs/common'
import { DatabaseService } from './database.service'

@Module({
  providers: [DatabaseService],
  exports: [DatabaseService]
})
export class DatabaseModule {}

// src/core/database/database.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'

@Injectable()
export class DatabaseService implements OnModuleInit {
  private connection: any

  constructor(private configService: ConfigService) {}

  async onModuleInit() {
    await this.connect()
  }

  private async connect() {
    console.log('Connecting to database...')
    // Database connection logic here
  }

  async query(sql: string, params?: any[]) {
    // Query execution logic here
    return { rows: [] }
  }

  async close() {
    // Connection cleanup logic here
  }
}

// 2. Feature Module Architecture
// src/users/users.module.ts
import { Module } from '@nestjs/common'
import { TypeOrmModule } from '@nestjs/typeorm'
import { UsersController } from './users.controller'
import { UsersService } from './users.service'
import { User } from './entities/user.entity'
import { UserProfileModule } from './user-profile/user-profile.module'

@Module({
  imports: [
    TypeOrmModule.forFeature([User]),
    UserProfileModule
  ],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService]
})
export class UsersModule {}

// src/users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { User } from './entities/user.entity'
import { CreateUserDto } from './dto/create-user.dto'
import { UpdateUserDto } from './dto/update-user.dto'
import { UserProfileService } from '../user-profile/user-profile.service'

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    private userProfileService: UserProfileService
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = this.userRepository.create(createUserDto)
    const savedUser = await this.userRepository.save(user)

    // Create user profile
    await this.userProfileService.create({
      userId: savedUser.id,
      bio: createUserDto.bio || ''
    })

    return savedUser
  }

  async findAll(): Promise<User[]> {
    return this.userRepository.find({
      relations: ['profile']
    })
  }

  async findOne(id: number): Promise<User> {
    const user = await this.userRepository.findOne({
      where: { id },
      relations: ['profile']
    })

    if (!user) {
      throw new NotFoundException(`User with ID ${id} not found`)
    }

    return user
  }

  async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.findOne(id)
    Object.assign(user, updateUserDto)
    return this.userRepository.save(user)
  }

  async remove(id: number): Promise<void> {
    const user = await this.findOne(id)
    await this.userRepository.remove(user)
  }

  // Custom query methods
  async findByEmail(email: string): Promise<User | null> {
    return this.userRepository.findOne({
      where: { email },
      relations: ['profile']
    })
  }

  async findActiveUsers(): Promise<User[]> {
    return this.userRepository.find({
      where: { isActive: true },
      relations: ['profile']
    })
  }
}

// 3. Service Abstraction with Interfaces
// src/users/interfaces/user-service.interface.ts
import { User } from '../entities/user.entity'
import { CreateUserDto } from '../dto/create-user.dto'
import { UpdateUserDto } from '../dto/update-user.dto'

export interface IUsersService {
  create(createUserDto: CreateUserDto): Promise<User>
  findAll(): Promise<User[]>
  findOne(id: number): Promise<User>
  update(id: number, updateUserDto: UpdateUserDto): Promise<User>
  remove(id: number): Promise<void>
  findByEmail(email: string): Promise<User | null>
  findActiveUsers(): Promise<User[]>
}

// src/users/users.service.ts (implementing interface)
import { Injectable, NotFoundException } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { User } from './entities/user.entity'
import { CreateUserDto, UpdateUserDto } from './dto'
import { IUsersService } from './interfaces/user-service.interface'

@Injectable()
export class UsersService implements IUsersService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    // Implementation
  }

  async findAll(): Promise<User[]> {
    return this.userRepository.find()
  }

  async findOne(id: number): Promise<User> {
    const user = await this.userRepository.findOne({ where: { id } })
    if (!user) {
      throw new NotFoundException(`User with ID ${id} not found`)
    }
    return user
  }

  async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.findOne(id)
    Object.assign(user, updateUserDto)
    return this.userRepository.save(user)
  }

  async remove(id: number): Promise<void> {
    const user = await this.findOne(id)
    await this.userRepository.remove(user)
  }

  async findByEmail(email: string): Promise<User | null> {
    return this.userRepository.findOne({ where: { email } })
  }

  async findActiveUsers(): Promise<User[]> {
    return this.userRepository.find({ where: { isActive: true } })
  }
}

// 4. Provider Registration and Custom Providers
// src/cache/cache.module.ts
import { Module } from '@nestjs/common'
import { CacheService } from './cache.service'
import { REDIS_PROVIDER } from './cache.constants'

// Custom provider factory
export const redisProvider = {
  provide: REDIS_PROVIDER,
  useFactory: async () => {
    const Redis = require('ioredis')
    const redis = new Redis({
      host: process.env.REDIS_HOST || 'localhost',
      port: parseInt(process.env.REDIS_PORT) || 6379,
      password: process.env.REDIS_PASSWORD
    })

    await redis.ping()
    return redis
  }
}

@Module({
  providers: [
    CacheService,
    redisProvider
  ],
  exports: [CacheService]
})
export class CacheModule {}

// src/cache/cache.service.ts
import { Injectable, Inject } from '@nestjs/common'
import { REDIS_PROVIDER } from './cache.constants'

@Injectable()
export class CacheService {
  constructor(@Inject(REDIS_PROVIDER) private readonly redis: any) {}

  async get(key: string): Promise<string | null> {
    return this.redis.get(key)
  }

  async set(key: string, value: string, ttl?: number): Promise<void> {
    if (ttl) {
      await this.redis.setex(key, ttl, value)
    } else {
      await this.redis.set(key, value)
    }
  }

  async del(key: string): Promise<void> {
    await this.redis.del(key)
  }

  async exists(key: string): Promise<boolean> {
    const result = await this.redis.exists(key)
    return result === 1
  }

  async clear(): Promise<void> {
    await this.redis.flushall()
  }
}

// 5. Async Provider Registration
// src/async-connection/async-connection.module.ts
import { Module } from '@nestjs/common'

export const ASYNC_CONNECTION_PROVIDER = 'ASYNC_CONNECTION_PROVIDER'

export const asyncConnectionProvider = {
  provide: ASYNC_CONNECTION_PROVIDER,
  useFactory: async (configService: ConfigService) => {
    // Simulate async connection setup
    await new Promise(resolve => setTimeout(resolve, 1000))

    return {
      connect: () => console.log('Connected asynchronously'),
      disconnect: () => console.log('Disconnected asynchronously')
    }
  },
  inject: [ConfigService]
}

@Module({
  providers: [asyncConnectionProvider],
  exports: [ASYNC_CONNECTION_PROVIDER]
})
export class AsyncConnectionModule {}

// 6. Circular Dependency Resolution
// src/auth/auth.service.ts
import { Injectable, forwardRef, Inject } from '@nestjs/common'
import { UsersService } from '../users/users.service'

@Injectable()
export class AuthService {
  constructor(
    @Inject(forwardRef(() => UsersService))
    private usersService: UsersService
  ) {}

  async validateUser(email: string, password: string) {
    const user = await this.usersService.findByEmail(email)
    if (!user) {
      return null
    }

    // Password validation logic here
    const isValid = await this.validatePassword(password, user.password)
    return isValid ? user : null
  }

  private async validatePassword(password: string, hashedPassword: string) {
    // Password validation logic
    return password === hashedPassword // Simplified
  }

  async login(user: any) {
    return {
      access_token: 'jwt-token',
      user
    }
  }
}

// src/users/users.service.ts (updated to avoid circular dependency)
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { User } from './entities/user.entity'

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>
  ) {}

  async findByEmail(email: string): Promise<User | null> {
    return this.userRepository.findOne({ where: { email } })
  }

  // Other methods...
}

// 7. Service Decorators and Scopes
// src/scoped/scoped.service.ts
import { Injectable, Scope } from '@nestjs/common'

@Injectable({ scope: Scope.REQUEST })
export class ScopedService {
  private requestId: string

  constructor() {
    this.requestId = Math.random().toString(36).substr(2, 9)
  }

  getRequestId(): string {
    return this.requestId
  }

  processData(data: any): any {
    return {
      requestId: this.requestId,
      processed: true,
      data,
      timestamp: new Date().toISOString()
    }
  }
}

// src/singleton/singleton.service.ts
import { Injectable } from '@nestjs/common'

@Injectable()
export class SingletonService {
  private static instanceCount = 0
  private instanceId: number

  constructor() {
    SingletonService.instanceCount++
    this.instanceId = SingletonService.instanceCount
  }

  getInstanceId(): number {
    return this.instanceId
  }

  getTotalInstances(): number {
    return SingletonService.instanceCount
  }
}

// 8. Service with Repository Pattern
// src/users/repositories/user.repository.ts
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { User } from '../entities/user.entity'

export interface IUserRepository {
  create(userData: Partial<User>): Promise<User>
  findById(id: number): Promise<User | null>
  findByEmail(email: string): Promise<User | null>
  findAll(): Promise<User[]>
  update(id: number, userData: Partial<User>): Promise<User>
  delete(id: number): Promise<void>
  findActiveUsers(): Promise<User[]>
}

@Injectable()
export class UserRepository implements IUserRepository {
  constructor(
    @InjectRepository(User)
    private readonly repository: Repository<User>
  ) {}

  async create(userData: Partial<User>): Promise<User> {
    const user = this.repository.create(userData)
    return this.repository.save(user)
  }

  async findById(id: number): Promise<User | null> {
    return this.repository.findOne({ where: { id } })
  }

  async findByEmail(email: string): Promise<User | null> {
    return this.repository.findOne({ where: { email } })
  }

  async findAll(): Promise<User[]> {
    return this.repository.find()
  }

  async update(id: number, userData: Partial<User>): Promise<User> {
    await this.repository.update(id, userData)
    return this.findById(id)
  }

  async delete(id: number): Promise<void> {
    await this.repository.delete(id)
  }

  async findActiveUsers(): Promise<User[]> {
    return this.repository.find({ where: { isActive: true } })
  }
}

// src/users/users.service.ts (using repository)
import { Injectable } from '@nestjs/common'
import { CreateUserDto, UpdateUserDto } from './dto'
import { UserRepository, IUserRepository } from './repositories/user.repository'

@Injectable()
export class UsersService {
  constructor(private readonly userRepository: IUserRepository) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    return this.userRepository.create(createUserDto)
  }

  async findAll(): Promise<User[]> {
    return this.userRepository.findAll()
  }

  async findOne(id: number): Promise<User> {
    const user = await this.userRepository.findById(id)
    if (!user) {
      throw new NotFoundException(`User with ID ${id} not found`)
    }
    return user
  }

  async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
    return this.userRepository.update(id, updateUserDto)
  }

  async remove(id: number): Promise<void> {
    await this.userRepository.delete(id)
  }

  async findByEmail(email: string): Promise<User | null> {
    return this.userRepository.findByEmail(email)
  }

  async findActiveUsers(): Promise<User[]> {
    return this.userRepository.findActiveUsers()
  }
}

// 9. Service with Factory Pattern
// src/factories/notification.factory.ts
import { Injectable } from '@nestjs/common'

export interface NotificationProvider {
  send(message: string, recipient: string): Promise<boolean>
}

@Injectable()
export class EmailNotificationProvider implements NotificationProvider {
  async send(message: string, recipient: string): Promise<boolean> {
    console.log(`Sending email to ${recipient}: ${message}`)
    return true
  }
}

@Injectable()
export class SMSNotificationProvider implements NotificationProvider {
  async send(message: string, recipient: string): Promise<boolean> {
    console.log(`Sending SMS to ${recipient}: ${message}`)
    return true
  }
}

@Injectable()
export class PushNotificationProvider implements NotificationProvider {
  async send(message: string, recipient: string): Promise<boolean> {
    console.log(`Sending push notification to ${recipient}: ${message}`)
    return true
  }
}

export class NotificationFactory {
  static createProvider(type: 'email' | 'sms' | 'push'): NotificationProvider {
    switch (type) {
      case 'email':
        return new EmailNotificationProvider()
      case 'sms':
        return new SMSNotificationProvider()
      case 'push':
        return new PushNotificationProvider()
      default:
        throw new Error(`Unknown notification provider: ${type}`)
    }
  }
}

// src/notification/notification.service.ts
import { Injectable } from '@nestjs/common'
import { NotificationFactory, NotificationProvider } from '../factories/notification.factory'

@Injectable()
export class NotificationService {
  private providers: Map<string, NotificationProvider> = new Map()

  constructor() {
    // Register default providers
    this.providers.set('email', NotificationFactory.createProvider('email'))
    this.providers.set('sms', NotificationFactory.createProvider('sms'))
    this.providers.set('push', NotificationFactory.createProvider('push'))
  }

  async sendNotification(
    type: 'email' | 'sms' | 'push',
    message: string,
    recipient: string
  ): Promise<boolean> {
    const provider = this.providers.get(type)
    if (!provider) {
      throw new Error(`Notification provider '${type}' not found`)
    }

    return provider.send(message, recipient)
  }

  registerProvider(type: string, provider: NotificationProvider): void {
    this.providers.set(type, provider)
  }

  async sendMultiChannelNotification(
    message: string,
    recipient: string,
    channels: string[] = ['email', 'sms']
  ): Promise<boolean[]> {
    const results = await Promise.all(
      channels.map(async (channel) => {
        try {
          return await this.sendNotification(channel as any, message, recipient)
        } catch (error) {
          console.error(`Failed to send ${channel} notification:`, error)
          return false
        }
      })
    )

    return results
  }
}

// 10. Service with Event Pattern
// src/events/events.module.ts
import { Module } from '@nestjs/common'
import { EventEmitterModule } from '@nestjs/event-emitter'
import { UserEventsService } from './services/user-events.service'
import { UserCreatedListener } from './listeners/user-created.listener'

@Module({
  imports: [EventEmitterModule.forRoot()],
  providers: [UserEventsService, UserCreatedListener],
  exports: [UserEventsService]
})
export class EventsModule {}

// src/events/services/user-events.service.ts
import { Injectable } from '@nestjs/common'
import { EventEmitter2 } from '@nestjs/event-emitter'

export interface UserCreatedEvent {
  userId: number
  email: string
  name: string
}

export interface UserDeletedEvent {
  userId: number
  deletedAt: Date
}

@Injectable()
export class UserEventsService {
  constructor(private eventEmitter: EventEmitter2) {}

  emitUserCreated(event: UserCreatedEvent) {
    this.eventEmitter.emit('user.created', event)
  }

  emitUserDeleted(event: UserDeletedEvent) {
    this.eventEmitter.emit('user.deleted', event)
  }

  emitUserUpdated(userId: number, changes: any) {
    this.eventEmitter.emit('user.updated', {
      userId,
      changes,
      updatedAt: new Date()
    })
  }
}

// src/events/listeners/user-created.listener.ts
import { Injectable, Logger } from '@nestjs/common'
import { OnEvent } from '@nestjs/event-emitter'
import { UserCreatedEvent } from '../services/user-events.service'
import { NotificationService } from '../notification/notification.service'
import { EmailService } from '../email/email.service'

@Injectable()
export class UserCreatedListener {
  private readonly logger = new Logger(UserCreatedListener.name)

  constructor(
    private readonly notificationService: NotificationService,
    private readonly emailService: EmailService
  ) {}

  @OnEvent('user.created')
  async handleUserCreatedEvent(event: UserCreatedEvent) {
    this.logger.log(`Handling user created event for user ID: ${event.userId}`)

    // Send welcome email
    await this.emailService.sendWelcomeEmail(event.email, event.name)

    // Send welcome notification
    await this.notificationService.sendNotification(
      'push',
      `Welcome ${event.name}! Your account has been created successfully.`,
      `user_${event.userId}`
    )

    // Log analytics
    await this.logUserCreation(event)
  }

  private async logUserCreation(event: UserCreatedEvent) {
    // Analytics logging logic
    this.logger.log(`User analytics logged for ID: ${event.userId}`)
  }
}

💻 NestJS Controllers and DTOs typescript

🟡 intermediate

Controller patterns, DTOs, validation, and routing in NestJS

// NestJS Controllers and DTOs Examples

// 1. Basic Controller Structure
// src/controllers/app.controller.ts
import { Controller, Get, Post, Put, Delete, Param, Query, Body, HttpStatus, HttpCode } from '@nestjs/common'
import { AppService } from '../services/app.service'
import { CreateDto, UpdateDto, QueryDto } from '../dto'

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getRoot(): string {
    return this.appService.getRoot()
  }

  @Get('health')
  @HttpCode(HttpStatus.OK)
  getHealthCheck() {
    return {
      status: 'OK',
      timestamp: new Date().toISOString(),
      uptime: process.uptime()
    }
  }
}

// 2. RESTful Controller with Full CRUD Operations
// src/products/products.controller.ts
import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Patch,
  Param,
  Query,
  Body,
  Headers,
  Ip,
  Session,
  UseGuards,
  UseInterceptors,
  UseFilters,
  HttpStatus,
  NotFoundException,
  BadRequestException
} from '@nestjs/common'
import { ProductsService } from '../services/products.service'
import { CreateProductDto, UpdateProductDto, ProductQueryDto } from '../dto'
import { JwtAuthGuard } from '../guards/jwt-auth.guard'
import { LoggingInterceptor } from '../interceptors/logging.interceptor'
import { HttpExceptionFilter } from '../filters/http-exception.filter'
import { CacheInterceptor } from '@nestjs/cache-manager'
import { CacheKey } from '../decorators/cache-key.decorator'

@Controller('products')
@UseGuards(JwtAuthGuard)
@UseInterceptors(LoggingInterceptor, CacheInterceptor)
@UseFilters(HttpExceptionFilter)
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

  @Post()
  async create(@Body() createProductDto: CreateProductDto) {
    try {
      const product = await this.productsService.create(createProductDto)
      return {
        success: true,
        data: product,
        message: 'Product created successfully'
      }
    } catch (error) {
      throw new BadRequestException(error.message)
    }
  }

  @Get()
  @CacheKey('products')
  async findAll(@Query() query: ProductQueryDto) {
    const { page = 1, limit = 10, search, category, sortBy = 'createdAt', sortOrder = 'desc' } = query

    const result = await this.productsService.findAll({
      page: +page,
      limit: +limit,
      search,
      category,
      sortBy,
      sortOrder
    })

    return {
      success: true,
      data: result.products,
      pagination: {
        page: result.page,
        limit: result.limit,
        total: result.total,
        totalPages: Math.ceil(result.total / limit)
      }
    }
  }

  @Get(':id')
  async findOne(
    @Param('id', ParseIntPipe) id: number,
    @Headers('accept-language') acceptLanguage: string,
    @Ip() ip: string
  ) {
    const product = await this.productsService.findOne(id)

    if (!product) {
      throw new NotFoundException(`Product with ID ${id} not found`)
    }

    // Log access
    console.log(`Product ${id} accessed from ${ip} with language: ${acceptLanguage}`)

    return {
      success: true,
      data: product
    }
  }

  @Put(':id')
  async update(
    @Param('id', ParseIntPipe) id: number,
    @Body() updateProductDto: UpdateProductDto
  ) {
    const product = await this.productsService.update(id, updateProductDto)

    if (!product) {
      throw new NotFoundException(`Product with ID ${id} not found`)
    }

    return {
      success: true,
      data: product,
      message: 'Product updated successfully'
    }
  }

  @Patch(':id')
  async partialUpdate(
    @Param('id', ParseIntPipe) id: number,
    @Body() partialUpdateDto: Partial<UpdateProductDto>
  ) {
    const product = await this.productsService.partialUpdate(id, partialUpdateDto)

    if (!product) {
      throw new NotFoundException(`Product with ID ${id} not found`)
    }

    return {
      success: true,
      data: product,
      message: 'Product partially updated successfully'
    }
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  async remove(@Param('id', ParseIntPipe) id: number) {
    const result = await this.productsService.remove(id)

    if (!result) {
      throw new NotFoundException(`Product with ID ${id} not found`)
    }
  }

  @Post(':id/like')
  async likeProduct(
    @Param('id', ParseIntPipe) id: number,
    @Session() session: Record<string, any>
  ) {
    const userId = session.userId || 'anonymous'
    const result = await this.productsService.likeProduct(id, userId)

    return {
      success: true,
      data: result,
      message: 'Product liked successfully'
    }
  }

  @Get(':id/reviews')
  async getProductReviews(
    @Param('id', ParseIntPipe) id: number,
    @Query('page', ParseIntPipe) page: number = 1,
    @Query('limit', ParseIntPipe) limit: number = 10
  ) {
    const reviews = await this.productsService.getProductReviews(id, page, limit)

    return {
      success: true,
      data: reviews,
      pagination: {
        page,
        limit,
        total: reviews.length
      }
    }
  }
}

// 3. Controller with Custom Decorators
// src/decorators/user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common'

export const User = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest()
    const user = request.user

    return data ? user?.[data] : user
  },
)

export const IpAddress = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest()
    return request.ip || request.headers['x-forwarded-for'] || request.connection.remoteAddress
  },
)

// src/decorators/pagination.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common'

export interface Pagination {
  page: number
  limit: number
  offset: number
}

export const Pagination = createParamDecorator(
  (defaultLimit: number = 10, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest()
    const page = parseInt(request.query.page) || 1
    const limit = parseInt(request.query.limit) || defaultLimit

    return {
      page,
      limit,
      offset: (page - 1) * limit
    }
  },
)

// src/controllers/user.controller.ts
import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common'
import { UserService } from '../services/user.service'
import { CreateUserDto, UpdateUserDto } from '../dto'
import { User, IpAddress, Pagination } from '../decorators'

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get('profile')
  getUserProfile(@User() user: any) {
    return {
      success: true,
      data: user
    }
  }

  @Get('activity')
  getUserActivity(@User('id') userId: number, @Pagination(20) pagination: Pagination) {
    return this.userService.getUserActivity(userId, pagination)
  }

  @Post()
  async createUser(
    @Body() createUserDto: CreateUserDto,
    @IpAddress() ip: string
  ) {
    const user = await this.userService.create({
      ...createUserDto,
      registrationIp: ip
    })

    return {
      success: true,
      data: user,
      message: 'User created successfully'
    }
  }
}

// 4. DTOs with Validation
// src/dto/create-product.dto.ts
import {
  IsString,
  IsNumber,
  IsOptional,
  IsPositive,
  IsEmail,
  IsUrl,
  IsArray,
  IsEnum,
  MinLength,
  MaxLength,
  Matches,
  IsDateString
} from 'class-validator'

export enum ProductStatus {
  ACTIVE = 'active',
  INACTIVE = 'inactive',
  DRAFT = 'draft'
}

export class CreateProductDto {
  @IsString()
  @MinLength(3, { message: 'Name must be at least 3 characters long' })
  @MaxLength(100, { message: 'Name must not exceed 100 characters' })
  name: string;

  @IsString()
  @MinLength(10, { message: 'Description must be at least 10 characters long' })
  description: string;

  @IsNumber()
  @IsPositive({ message: 'Price must be a positive number' })
  price: number;

  @IsOptional()
  @IsNumber()
  @IsPositive({ message: 'Discount must be a positive number' })
  discount?: number;

  @IsOptional()
  @IsString()
  @IsEnum(ProductStatus, { message: 'Invalid status value' })
  status?: ProductStatus;

  @IsOptional()
  @IsArray()
  @IsString({ each: true, message: 'Each tag must be a string' })
  tags?: string[];

  @IsOptional()
  @IsUrl({}, { message: 'Image URL must be a valid URL' })
  imageUrl?: string;

  @IsOptional()
  @IsString()
  category?: string;

  @IsOptional()
  @IsNumber()
  @IsPositive({ message: 'Stock must be a positive number' })
  stock?: number;

  @IsOptional()
  @IsString()
  @Matches(/^[a-zA-Z0-9-]+$/, { message: 'SKU can only contain letters, numbers, and hyphens' })
  sku?: string;

  @IsOptional()
  @IsDateString({}, { message: 'Launch date must be a valid ISO date string' })
  launchDate?: string;
}

// src/dto/update-product.dto.ts
import { PartialType } from '@nestjs/swagger'
import { CreateProductDto } from './create-product.dto'

export class UpdateProductDto extends PartialType(CreateProductDto) {}

// src/dto/product-query.dto.ts
import {
  IsOptional,
  IsString,
  IsNumber,
  IsEnum,
  IsPositive,
  Min,
  Max
} from 'class-validator'
import { Transform } from 'class-transformer'
import { ProductStatus } from './create-product.dto'

export class ProductQueryDto {
  @IsOptional()
  @Transform(({ value }) => parseInt(value))
  @IsNumber()
  @IsPositive()
  @Min(1)
  page?: number = 1;

  @IsOptional()
  @Transform(({ value }) => parseInt(value))
  @IsNumber()
  @IsPositive()
  @Max(100)
  limit?: number = 10;

  @IsOptional()
  @IsString()
  search?: string;

  @IsOptional()
  @IsString()
  category?: string;

  @IsOptional()
  @IsEnum(ProductStatus)
  status?: ProductStatus;

  @IsOptional()
  @IsEnum(['createdAt', 'updatedAt', 'price', 'name'])
  sortBy?: string = 'createdAt';

  @IsOptional()
  @IsEnum(['asc', 'desc'])
  sortOrder?: 'asc' | 'desc' = 'desc';

  @IsOptional()
  @Transform(({ value }) => value === 'true')
  includeDeleted?: boolean;

  @IsOptional()
  @Transform(({ value }) => parseFloat(value))
  @IsNumber()
  @IsPositive()
  minPrice?: number;

  @IsOptional()
  @Transform(({ value }) => parseFloat(value))
  @IsNumber()
  @IsPositive()
  maxPrice?: number;
}

// 5. Nested DTOs for Complex Objects
// src/dto/create-order.dto.ts
import {
  IsString,
  IsEmail,
  IsArray,
  IsNumber,
  IsPositive,
  ValidateNested,
  ArrayNotEmpty
} from 'class-validator'
import { Type } from 'class-transformer'

export class OrderItemDto {
  @IsNumber()
  @IsPositive()
  productId: number;

  @IsNumber()
  @IsPositive()
  quantity: number;

  @IsOptional()
  @IsNumber()
  @IsPositive()
  unitPrice?: number;
}

export class ShippingAddressDto {
  @IsString()
  street: string;

  @IsString()
  city: string;

  @IsString()
  state: string;

  @IsString()
  @Matches(/^[0-9]{5}(-[0-9]{4})?$/, { message: 'Invalid postal code format' })
  postalCode: string;

  @IsString()
  country: string;

  @IsOptional()
  @IsString()
  addressLine2?: string;
}

export class CreateOrderDto {
  @IsEmail({}, { message: 'Invalid email format' })
  customerEmail: string;

  @IsString()
  customerName: string;

  @IsArray()
  @ArrayNotEmpty({ message: 'Order must contain at least one item' })
  @ValidateNested({ each: true })
  @Type(() => OrderItemDto)
  items: OrderItemDto[];

  @ValidateNested()
  @Type(() => ShippingAddressDto)
  shippingAddress: ShippingAddressDto;

  @IsOptional()
  @IsString()
  @IsEnum(['standard', 'express', 'overnight'])
  shippingMethod?: string;

  @IsOptional()
  @IsString()
  notes?: string;
}

// 6. DTOs with Class Transformer
// src/dto/user.dto.ts
import {
  IsString,
  IsEmail,
  IsOptional,
  IsBoolean,
  IsDateString,
  IsEnum
} from 'class-validator'
import { Transform, Expose } from 'class-transformer'
import { UserRole } from '../enums/user-role.enum'

export class CreateUserDto {
  @IsString()
  @Transform(({ value }) => value?.trim())
  firstName: string;

  @IsString()
  @Transform(({ value }) => value?.trim())
  lastName: string;

  @IsEmail({}, { message: 'Invalid email format' })
  @Transform(({ value }) => value?.toLowerCase().trim())
  email: string;

  @IsString()
  @MinLength(8, { message: 'Password must be at least 8 characters long' })
  @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/, {
    message: 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character'
  })
  password: string;

  @IsOptional()
  @IsDateString()
  birthDate?: string;

  @IsOptional()
  @IsEnum(UserRole)
  role?: UserRole = UserRole.USER;

  @IsOptional()
  @IsBoolean()
  @Transform(({ value }) => value === 'true')
  isActive?: boolean = true;

  @IsOptional()
  @IsString()
  @Transform(({ value }) => value?.trim())
  phone?: string;

  @IsOptional()
  @Transform(({ value }) => value?.split(',').map((tag: string) => tag.trim()).filter(Boolean))
  tags?: string[];
}

export class UserResponseDto {
  @Expose()
  id: number;

  @Expose()
  email: string;

  @Expose()
  firstName: string;

  @Expose()
  lastName: string;

  @Expose()
  fullName: string;

  @Expose()
  role: UserRole;

  @Expose()
  isActive: boolean;

  @Expose()
  birthDate: string;

  @Expose()
  phone: string;

  @Expose()
  tags: string[];

  @Expose()
  createdAt: string;

  @Expose()
  updatedAt: string;

  @Expose()
  lastLoginAt?: string;
}

// src/dto/user-search.dto.ts
import { IsOptional, IsString, IsBoolean, IsEnum } from 'class-validator'
import { Transform } from 'class-transformer'

export enum SortField {
  NAME = 'name',
  EMAIL = 'email',
  CREATED_AT = 'createdAt',
  LAST_LOGIN = 'lastLoginAt'
}

export enum SortOrder {
  ASC = 'asc',
  DESC = 'desc'
}

export class UserSearchDto {
  @IsOptional()
  @IsString()
  @Transform(({ value }) => value?.trim())
  search?: string;

  @IsOptional()
  @IsString()
  @Transform(({ value }) => value?.trim())
  email?: string;

  @IsOptional()
  @IsString()
  @Transform(({ value }) => value?.trim())
  name?: string;

  @IsOptional()
  @IsBoolean()
  @Transform(({ value }) => value === 'true')
  isActive?: boolean;

  @IsOptional()
  @IsEnum(SortField)
  sortBy?: SortField = SortField.CREATED_AT;

  @IsOptional()
  @IsEnum(SortOrder)
  sortOrder?: SortOrder = SortOrder.DESC;

  @IsOptional()
  @Transform(({ value }) => parseInt(value))
  @IsNumber()
  page?: number = 1;

  @IsOptional()
  @Transform(({ value }) => parseInt(value))
  @IsNumber()
  limit?: number = 10;

  @IsOptional()
  @Transform(({ value }) => value?.split(',').map((tag: string) => tag.trim()))
  tags?: string[];
}

// 7. Controller with Response Transformation
// src/controllers/api/v1/users.controller.ts
import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  HttpCode,
  HttpStatus,
  UseGuards,
  UseInterceptors
} from '@nestjs/common'
import { UserService } from '../../../services/user.service'
import { CreateUserDto, UpdateUserDto, UserResponseDto, UserSearchDto } from '../../../dto'
import { JwtAuthGuard } from '../../../guards/jwt-auth.guard'
import { TransformInterceptor } from '../../../interceptors/transform.interceptor'
import { Serialize } from '../../../decorators/serialize.decorator'

@Controller('api/v1/users')
@UseGuards(JwtAuthGuard)
@UseInterceptors(TransformInterceptor)
export class ApiV1UsersController {
  constructor(private readonly userService: UserService) {}

  @Post()
  @Serialize(UserResponseDto)
  async create(@Body() createUserDto: CreateUserDto) {
    const user = await this.userService.create(createUserDto)
    return {
      success: true,
      data: user,
      message: 'User created successfully'
    }
  }

  @Get()
  @Serialize(UserResponseDto)
  async findAll(@Query() query: UserSearchDto) {
    const { users, pagination } = await this.userService.findAll(query)
    return {
      success: true,
      data: users,
      pagination
    }
  }

  @Get(':id')
  @Serialize(UserResponseDto)
  async findOne(@Param('id') id: string) {
    const user = await this.userService.findOne(+id)
    return {
      success: true,
      data: user
    }
  }

  @Put(':id')
  @Serialize(UserResponseDto)
  async update(
    @Param('id') id: string,
    @Body() updateUserDto: UpdateUserDto
  ) {
    const user = await this.userService.update(+id, updateUserDto)
    return {
      success: true,
      data: user,
      message: 'User updated successfully'
    }
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  async remove(@Param('id') id: string) {
    await this.userService.remove(+id)
  }
}

// 8. Controller with File Upload
// src/controllers/upload.controller.ts
import {
  Controller,
  Post,
  UseInterceptors,
  UploadedFile,
  UploadedFiles,
  Body,
  HttpCode,
  HttpStatus,
  ParseFilePipe,
  MaxFileSizeValidator,
  FileTypeValidator,
  ParseFilePipeBuilder
} from '@nestjs/common'
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'
import { UploadService } from '../services/upload.service'
import { CreateUploadDto } from '../dto/upload.dto'

@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @Post('single')
  @UseInterceptors(FileInterceptor('file'))
  async uploadSingle(
    @UploadedFile(
      new ParseFilePipe({
        validators: [
          new MaxFileSizeValidator({ maxSize: 5 * 1024 * 1024 }), // 5MB
          new FileTypeValidator({ fileType: /(jpeg|jpg|png|gif)$/i })
        ]
      })
    )
    file: Express.Multer.File,
    @Body() uploadDto: CreateUploadDto
  ) {
    const result = await this.uploadService.uploadFile(file, uploadDto)
    return {
      success: true,
      data: result,
      message: 'File uploaded successfully'
    }
  }

  @Post('multiple')
  @UseInterceptors(FilesInterceptor('files', 10))
  async uploadMultiple(
    @UploadedFiles(
      new ParseFilePipeBuilder()
        .addFileTypeValidator({
          fileType: /(jpeg|jpg|png|gif|pdf|doc|docx)$/i,
        })
        .addMaxSizeValidator({
          maxSize: 10 * 1024 * 1024, // 10MB per file
          maxFileSize: 50 * 1024 * 1024, // 50MB total
        })
        .build({
          errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY
        })
    )
    files: Express.Multer.File[],
    @Body() uploadDto: CreateUploadDto
  ) {
    const results = await this.uploadService.uploadMultipleFiles(files, uploadDto)
    return {
      success: true,
      data: results,
      message: `${files.length} files uploaded successfully`
    }
  }

  @Post('image')
  @UseInterceptors(FileInterceptor('image'))
  async uploadImage(
    @UploadedFile(
      new ParseFilePipe({
        validators: [
          new MaxFileSizeValidator({ maxSize: 2 * 1024 * 1024 }), // 2MB
          new FileTypeValidator({ fileType: /(jpeg|jpg|png|webp)$/i })
        ]
      })
    )
    image: Express.Multer.File
  ) {
    const result = await this.uploadService.processImage(image)
    return {
      success: true,
      data: result,
      message: 'Image uploaded and processed successfully'
    }
  }
}

// 9. Controller with Swagger Documentation
// src/controllers/products.controller.ts
import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  Query,
  HttpStatus
} from '@nestjs/common'
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiParam,
  ApiQuery,
  ApiBody
} from '@nestjs/swagger'
import { ProductsService } from '../services/products.service'
import { CreateProductDto, UpdateProductDto, ProductQueryDto } from '../dto'
import { Product } from '../entities/product.entity'

@ApiTags('products')
@Controller('products')
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

  @Post()
  @ApiOperation({ summary: 'Create a new product' })
  @ApiResponse({ status: 201, description: 'Product created successfully', type: Product })
  @ApiResponse({ status: 400, description: 'Bad request - Invalid input data' })
  @ApiBody({ type: CreateProductDto })
  async create(@Body() createProductDto: CreateProductDto): Promise<Product> {
    return this.productsService.create(createProductDto)
  }

  @Get()
  @ApiOperation({ summary: 'Get all products with pagination and filtering' })
  @ApiResponse({ status: 200, description: 'List of products', type: [Product] })
  @ApiQuery({ name: 'page', required: false, type: Number, description: 'Page number' })
  @ApiQuery({ name: 'limit', required: false, type: Number, description: 'Number of items per page' })
  @ApiQuery({ name: 'search', required: false, type: String, description: 'Search term' })
  @ApiQuery({ name: 'category', required: false, type: String, description: 'Product category' })
  async findAll(@Query() query: ProductQueryDto) {
    return this.productsService.findAll(query)
  }

  @Get(':id')
  @ApiOperation({ summary: 'Get a product by ID' })
  @ApiResponse({ status: 200, description: 'Product found', type: Product })
  @ApiResponse({ status: 404, description: 'Product not found' })
  @ApiParam({ name: 'id', description: 'Product ID' })
  async findOne(@Param('id') id: string): Promise<Product> {
    return this.productsService.findOne(+id)
  }

  @Put(':id')
  @ApiOperation({ summary: 'Update a product by ID' })
  @ApiResponse({ status: 200, description: 'Product updated successfully', type: Product })
  @ApiResponse({ status: 404, description: 'Product not found' })
  @ApiResponse({ status: 400, description: 'Bad request - Invalid input data' })
  @ApiParam({ name: 'id', description: 'Product ID' })
  @ApiBody({ type: UpdateProductDto })
  async update(
    @Param('id') id: string,
    @Body() updateProductDto: UpdateProductDto
  ): Promise<Product> {
    return this.productsService.update(+id, updateProductDto)
  }

  @Delete(':id')
  @ApiOperation({ summary: 'Delete a product by ID' })
  @ApiResponse({ status: 204, description: 'Product deleted successfully' })
  @ApiResponse({ status: 404, description: 'Product not found' })
  @ApiParam({ name: 'id', description: 'Product ID' })
  @HttpCode(HttpStatus.NO_CONTENT)
  async remove(@Param('id') id: string): Promise<void> {
    return this.productsService.remove(+id)
  }
}

💻 NestJS Authentication and Testing typescript

🔴 complex

Authentication strategies, guards, and comprehensive testing patterns

// NestJS Authentication and Testing Examples

// 1. JWT Authentication Module
// src/auth/auth.module.ts
import { Module } from '@nestjs/common'
import { JwtModule } from '@nestjs/jwt'
import { PassportModule } from '@nestjs/passport'
import { ConfigModule, ConfigService } from '@nestjs/config'
import { AuthService } from './auth.service'
import { AuthController } from './auth.controller'
import { JwtStrategy } from './strategies/jwt.strategy'
import { LocalStrategy } from './strategies/local.strategy'
import { UsersModule } from '../users/users.module'

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        secret: configService.get<string>('JWT_SECRET'),
        signOptions: {
          expiresIn: configService.get<string>('JWT_EXPIRES_IN', '1h')
        }
      }),
      inject: [ConfigService]
    })
  ],
  controllers: [AuthController],
  providers: [AuthService, JwtStrategy, LocalStrategy],
  exports: [AuthService]
})
export class AuthModule {}

// src/auth/auth.service.ts
import { Injectable, UnauthorizedException, BadRequestException } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import { UsersService } from '../users/users.service'
import { CreateUserDto, LoginDto } from '../dto'
import * as bcrypt from 'bcrypt'
import { User } from '../entities/user.entity'

@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService
  ) {}

  async validateUser(email: string, password: string): Promise<User | null> {
    const user = await this.usersService.findByEmail(email)
    if (!user) {
      throw new UnauthorizedException('Invalid credentials')
    }

    const isPasswordValid = await bcrypt.compare(password, user.password)
    if (!isPasswordValid) {
      throw new UnauthorizedException('Invalid credentials')
    }

    return user
  }

  async login(loginDto: LoginDto) {
    const { email, password } = loginDto
    const user = await this.validateUser(email, password)

    if (!user.isActive) {
      throw new UnauthorizedException('Account is deactivated')
    }

    const payload = {
      sub: user.id,
      email: user.email,
      role: user.role,
      name: user.fullName
    }

    const access_token = this.jwtService.sign(payload)

    // Update last login
    await this.usersService.updateLastLogin(user.id)

    return {
      access_token,
      user: {
        id: user.id,
        email: user.email,
        fullName: user.fullName,
        role: user.role
      }
    }
  }

  async register(createUserDto: CreateUserDto) {
    // Check if user already exists
    const existingUser = await this.usersService.findByEmail(createUserDto.email)
    if (existingUser) {
      throw new BadRequestException('Email already registered')
    }

    // Hash password
    const salt = await bcrypt.genSalt(10)
    const hashedPassword = await bcrypt.hash(createUserDto.password, salt)

    // Create user
    const user = await this.usersService.create({
      ...createUserDto,
      password: hashedPassword
    })

    // Generate JWT token
    const payload = {
      sub: user.id,
      email: user.email,
      role: user.role,
      name: user.fullName
    }

    const access_token = this.jwtService.sign(payload)

    return {
      access_token,
      user: {
        id: user.id,
        email: user.email,
        fullName: user.fullName,
        role: user.role
      }
    }
  }

  async refreshToken(user: any) {
    const payload = {
      sub: user.id,
      email: user.email,
      role: user.role,
      name: user.name
    }

    const access_token = this.jwtService.sign(payload)

    return {
      access_token
    }
  }

  async changePassword(userId: number, currentPassword: string, newPassword: string) {
    const user = await this.usersService.findOne(userId)
    if (!user) {
      throw new UnauthorizedException('User not found')
    }

    const isCurrentPasswordValid = await bcrypt.compare(currentPassword, user.password)
    if (!isCurrentPasswordValid) {
      throw new UnauthorizedException('Current password is incorrect')
    }

    const salt = await bcrypt.genSalt(10)
    const hashedNewPassword = await bcrypt.hash(newPassword, salt)

    await this.usersService.update(userId, { password: hashedNewPassword })

    return { message: 'Password changed successfully' }
  }
}

// src/auth/strategies/jwt.strategy.ts
import { Injectable } from '@nestjs/common'
import { PassportStrategy } from '@nestjs/passport'
import { ExtractJwt, Strategy } from 'passport-jwt'
import { ConfigService } from '@nestjs/config'

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private configService: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get<string>('JWT_SECRET'),
    })
  }

  async validate(payload: any) {
    return {
      id: payload.sub,
      email: payload.email,
      role: payload.role,
      name: payload.name
    }
  }
}

// src/auth/strategies/local.strategy.ts
import { Injectable } from '@nestjs/common'
import { PassportStrategy } from '@nestjs/passport'
import { Strategy } from 'passport-local'
import { AuthService } from '../auth.service'

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({
      usernameField: 'email'
    })
  }

  async validate(email: string, password: string): Promise<any> {
    return await this.authService.validateUser(email, password)
  }
}

// 2. Authentication Guards
// src/guards/jwt-auth.guard.ts
import { Injectable } from '@nestjs/common'
import { AuthGuard } from '@nestjs/passport'

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

// src/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { ROLES_KEY } from '../decorators/roles.decorator'
import { UserRole } from '../enums/user-role.enum'

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<UserRole[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ])

    if (!requiredRoles) {
      return true
    }

    const { user } = context.switchToHttp().getRequest()
    return requiredRoles.some((role) => user.role === role)
  }
}

// src/guards/api-key.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'

@Injectable()
export class ApiKeyGuard implements CanActivate {
  constructor(private configService: ConfigService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest()
    const apiKey = request.headers['x-api-key'] as string

    const validApiKeys = this.configService.get<string>('API_KEYS')?.split(',') || []

    return validApiKeys.includes(apiKey)
  }
}

// src/guards/throttler.guard.ts
import { Injectable, CanActivate, ExecutionContext, HttpException, HttpStatus } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { Request } from 'express'

interface RateLimit {
  windowMs: number
  max: number
}

@Injectable()
export class ThrottlerGuard implements CanActivate {
  private requests = new Map<string, { count: number; resetTime: number }>()

  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest<Request>()
    const rateLimit = this.reflector.get<RateLimit>('throttle', context.getHandler()) || {
      windowMs: 60000, // 1 minute
      max: 100 // 100 requests per minute
    }

    const key = this.getRequestKey(request)
    const now = Date.now()
    const record = this.requests.get(key)

    if (!record || now > record.resetTime) {
      this.requests.set(key, { count: 1, resetTime: now + rateLimit.windowMs })
      return true
    }

    if (record.count >= rateLimit.max) {
      throw new HttpException('Too Many Requests', HttpStatus.TOO_MANY_REQUESTS)
    }

    record.count++
    return true
  }

  private getRequestKey(request: Request): string {
    const ip = request.ip || request.headers['x-forwarded-for'] || request.connection.remoteAddress
    return `${ip}:${request.path}`
  }
}

// 3. Custom Decorators
// src/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common'
import { UserRole } from '../enums/user-role.enum'

export const ROLES_KEY = 'roles'
export const Roles = (...roles: UserRole[]) => SetMetadata(ROLES_KEY, roles)

// src/decorators/throttle.decorator.ts
import { SetMetadata } from '@nestjs/common'

export const THROTTLE_KEY = 'throttle'
export const Throttle = (windowMs: number, max: number) =>
  SetMetadata(THROTTLE_KEY, { windowMs, max })

// src/decorators/public.decorator.ts
import { SetMetadata } from '@nestjs/common'

export const IS_PUBLIC_KEY = 'isPublic'
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true)

// src/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
  },
)

// 4. Authentication Controller
// src/auth/auth.controller.ts
import {
  Controller,
  Post,
  UseGuards,
  Request,
  Body,
  HttpCode,
  HttpStatus,
  Get
} from '@nestjs/common'
import { AuthService } from './auth.service'
import { LocalAuthGuard } from './guards/local-auth.guard'
import { JwtAuthGuard } from './guards/jwt-auth.guard'
import { CreateUserDto, LoginDto } from '../dto'
import { CurrentUser } from '../decorators/current-user.decorator'
import { Throttle } from '../decorators/throttle.decorator'

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('login')
  @UseGuards(LocalAuthGuard)
  @Throttle(60000, 5) // 5 requests per minute
  @HttpCode(HttpStatus.OK)
  async login(@Request() req, @Body() loginDto: LoginDto) {
    return this.authService.login(loginDto)
  }

  @Post('register')
  @Throttle(300000, 3) // 3 requests per 5 minutes
  async register(@Body() createUserDto: CreateUserDto) {
    return this.authService.register(createUserDto)
  }

  @Post('refresh')
  @UseGuards(JwtAuthGuard)
  async refresh(@CurrentUser() user: any) {
    return this.authService.refreshToken(user)
  }

  @Post('change-password')
  @UseGuards(JwtAuthGuard)
  @Throttle(300000, 3) // 3 requests per 5 minutes
  async changePassword(
    @CurrentUser() user: any,
    @Body() body: { currentPassword: string; newPassword: string }
  ) {
    return this.authService.changePassword(user.id, body.currentPassword, body.newPassword)
  }

  @Get('profile')
  @UseGuards(JwtAuthGuard)
  async getProfile(@CurrentUser() user: any) {
    return {
      success: true,
      data: user
    }
  }
}

// 5. Unit Testing
// src/auth/auth.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing'
import { AuthService } from './auth.service'
import { UsersService } from '../users/users.service'
import { JwtService } from '@nestjs/jwt'
import { UnauthorizedException, BadRequestException } from '@nestjs/common'
import { CreateUserDto, LoginDto } from '../dto'
import { User } from '../entities/user.entity'
import * as bcrypt from 'bcrypt'

jest.mock('bcrypt')

describe('AuthService', () => {
  let service: AuthService
  let usersService: UsersService
  let jwtService: JwtService

  const mockUser: User = {
    id: 1,
    email: '[email protected]',
    fullName: 'Test User',
    password: 'hashedPassword',
    role: 'user',
    isActive: true,
    createdAt: new Date(),
    updatedAt: new Date()
  }

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AuthService,
        {
          provide: UsersService,
          useValue: {
            findByEmail: jest.fn(),
            create: jest.fn(),
            findOne: jest.fn(),
            updateLastLogin: jest.fn(),
            update: jest.fn()
          }
        },
        {
          provide: JwtService,
          useValue: {
            sign: jest.fn()
          }
        }
      ]
    }).compile()

    service = module.get<AuthService>(AuthService)
    usersService = module.get<UsersService>(UsersService)
    jwtService = module.get<JwtService>(JwtService)

    (bcrypt.compare as jest.Mock).mockResolvedValue(true)
    (bcrypt.hash as jest.Mock).mockResolvedValue('hashedPassword')
    (bcrypt.genSalt as jest.Mock).mockResolvedValue('salt')
  })

  describe('validateUser', () => {
    it('should return user when credentials are valid', async () => {
      usersService.findByEmail.mockResolvedValue(mockUser)

      const result = await service.validateUser('[email protected]', 'password')

      expect(result).toEqual(mockUser)
      expect(usersService.findByEmail).toHaveBeenCalledWith('[email protected]')
      expect(bcrypt.compare).toHaveBeenCalledWith('password', 'hashedPassword')
    })

    it('should throw UnauthorizedException when user does not exist', async () => {
      usersService.findByEmail.mockResolvedValue(null)

      await expect(service.validateUser('[email protected]', 'password'))
        .rejects.toThrow(UnauthorizedException)
    })

    it('should throw UnauthorizedException when password is invalid', async () => {
      usersService.findByEmail.mockResolvedValue(mockUser)
      ;(bcrypt.compare as jest.Mock).mockResolvedValue(false)

      await expect(service.validateUser('[email protected]', 'wrongpassword'))
        .rejects.toThrow(UnauthorizedException)
    })
  })

  describe('login', () => {
    it('should return access token and user when credentials are valid', async () => {
      const loginDto: LoginDto = { email: '[email protected]', password: 'password' }
      const expectedPayload = { sub: 1, email: '[email protected]', role: 'user', name: 'Test User' }

      usersService.findByEmail.mockResolvedValue(mockUser)
      jwtService.sign.mockReturnValue('jwt-token')

      const result = await service.login(loginDto)

      expect(result).toEqual({
        access_token: 'jwt-token',
        user: {
          id: 1,
          email: '[email protected]',
          fullName: 'Test User',
          role: 'user'
        }
      })
      expect(jwtService.sign).toHaveBeenCalledWith(expectedPayload)
    })

    it('should throw UnauthorizedException when account is deactivated', async () => {
      const deactivatedUser = { ...mockUser, isActive: false }
      const loginDto: LoginDto = { email: '[email protected]', password: 'password' }

      usersService.findByEmail.mockResolvedValue(deactivatedUser)

      await expect(service.login(loginDto))
        .rejects.toThrow(UnauthorizedException)
    })
  })

  describe('register', () => {
    it('should return access token and user when registration is successful', async () => {
      const createUserDto: CreateUserDto = {
        email: '[email protected]',
        password: 'password123',
        fullName: 'New User'
      }

      usersService.findByEmail.mockResolvedValue(null)
      usersService.create.mockResolvedValue({ id: 2, ...createUserDto })
      jwtService.sign.mockReturnValue('new-jwt-token')

      const result = await service.register(createUserDto)

      expect(result).toEqual({
        access_token: 'new-jwt-token',
        user: {
          id: 2,
          email: '[email protected]',
          fullName: 'New User',
          role: 'user'
        }
      })
    })

    it('should throw BadRequestException when email already exists', async () => {
      const createUserDto: CreateUserDto = {
        email: '[email protected]',
        password: 'password123',
        fullName: 'Existing User'
      }

      usersService.findByEmail.mockResolvedValue(mockUser)

      await expect(service.register(createUserDto))
        .rejects.toThrow(BadRequestException)
    })
  })
})

// 6. E2E Testing
// test/auth.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing'
import { INestApplication } from '@nestjs/common'
import * as request from 'supertest'
import { AppModule } from '../src/app.module'
import { getRepositoryToken } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { User } from '../src/users/entities/user.entity'

describe('Authentication (e2e)', () => {
  let app: INestApplication
  let userRepository: Repository<User>

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile()

    app = moduleFixture.createNestApplication()
    userRepository = moduleFixture.get<Repository<User>>(getRepositoryToken(User))

    await app.init()
  })

  afterEach(async () => {
    await userRepository.clear()
    await app.close()
  })

  describe('/auth/register', () => {
    it('should register a new user successfully', () => {
      const createUserDto = {
        email: '[email protected]',
        password: 'password123',
        fullName: 'Test User'
      }

      return request(app.getHttpServer())
        .post('/auth/register')
        .send(createUserDto)
        .expect(201)
        .expect((res) => {
          expect(res.body.access_token).toBeDefined()
          expect(res.body.user.email).toBe(createUserDto.email)
          expect(res.body.user.fullName).toBe(createUserDto.fullName)
        })
    })

    it('should return 400 when email already exists', () => {
      const createUserDto = {
        email: '[email protected]',
        password: 'password123',
        fullName: 'Duplicate User'
      }

      // Create first user
      request(app.getHttpServer())
        .post('/auth/register')
        .send(createUserDto)
        .expect(201)

      // Try to create second user with same email
      return request(app.getHttpServer())
        .post('/auth/register')
        .send(createUserDto)
        .expect(400)
    })
  })

  describe('/auth/login', () => {
    beforeEach(async () => {
      // Create a user for login testing
      const createUserDto = {
        email: '[email protected]',
        password: 'password123',
        fullName: 'Login Test User'
      }

      await request(app.getHttpServer())
        .post('/auth/register')
        .send(createUserDto)
        .expect(201)
    })

    it('should login successfully with valid credentials', () => {
      const loginDto = {
        email: '[email protected]',
        password: 'password123'
      }

      return request(app.getHttpServer())
        .post('/auth/login')
        .send(loginDto)
        .expect(200)
        .expect((res) => {
          expect(res.body.access_token).toBeDefined()
          expect(res.body.user.email).toBe(loginDto.email)
        })
    })

    it('should return 401 with invalid credentials', () => {
      const loginDto = {
        email: '[email protected]',
        password: 'wrongpassword'
      }

      return request(app.getHttpServer())
        .post('/auth/login')
        .send(loginDto)
        .expect(401)
    })
  })

  describe('/auth/profile', () => {
    let accessToken: string

    beforeEach(async () => {
      // Register and login to get access token
      const createUserDto = {
        email: '[email protected]',
        password: 'password123',
        fullName: 'Profile Test User'
      }

      const registerResponse = await request(app.getHttpServer())
        .post('/auth/register')
        .send(createUserDto)

      accessToken = registerResponse.body.access_token
    })

    it('should return user profile with valid token', () => {
      return request(app.getHttpServer())
        .get('/auth/profile')
        .set('Authorization', `Bearer ${accessToken}`)
        .expect(200)
        .expect((res) => {
          expect(res.body.success).toBe(true)
          expect(res.body.data.email).toBe('[email protected]')
        })
    })

    it('should return 401 without token', () => {
      return request(app.getHttpServer())
        .get('/auth/profile')
        .expect(401)
    })

    it('should return 401 with invalid token', () => {
      return request(app.getHttpServer())
        .get('/auth/profile')
        .set('Authorization', 'Bearer invalid-token')
        .expect(401)
    })
  })
})

// 7. Integration Testing with Test Database
// test/auth.integration.spec.ts
import { Test, TestingModule } from '@nestjs/testing'
import { INestApplication } from '@nestjs/common'
import * as request from 'supertest'
import { AppModule } from '../src/app.module'
import { TypeOrmModule } from '@nestjs/typeorm'
import { User } from '../src/users/entities/user.entity'
import { getRepositoryToken } from '@nestjs/typeorm'

describe('Authentication Integration', () => {
  let app: INestApplication
  let userRepository: Repository<User>

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [
        AppModule,
        TypeOrmModule.forRoot({
          type: 'sqlite',
          database: ':memory:',
          entities: [User],
          synchronize: true,
          logging: false
        })
      ]
    }).compile()

    app = moduleFixture.createNestApplication()
    userRepository = moduleFixture.get<Repository<User>>(getRepositoryToken(User))

    await app.init()
  })

  afterAll(async () => {
    await app.close()
  })

  describe('Complete Authentication Flow', () => {
    it('should handle complete user registration and login flow', async () => {
      const userDto = {
        email: '[email protected]',
        password: 'StrongPassword123!',
        fullName: 'Integration Test User'
      }

      // Step 1: Register user
      const registerResponse = await request(app.getHttpServer())
        .post('/auth/register')
        .send(userDto)
        .expect(201)

      const { access_token, user } = registerResponse.body

      // Step 2: Verify registration response
      expect(access_token).toBeDefined()
      expect(user.email).toBe(userDto.email)
      expect(user.fullName).toBe(userDto.fullName)

      // Step 3: Login with registered credentials
      const loginResponse = await request(app.getHttpServer())
        .post('/auth/login')
        .send({
          email: userDto.email,
          password: userDto.password
        })
        .expect(200)

      // Step 4: Verify login response
      expect(loginResponse.body.access_token).toBeDefined()
      expect(loginResponse.body.user.email).toBe(userDto.email)

      // Step 5: Access protected route
      const profileResponse = await request(app.getHttpServer())
        .get('/auth/profile')
        .set('Authorization', `Bearer ${access_token}`)
        .expect(200)

      expect(profileResponse.body.data.email).toBe(userDto.email)

      // Step 6: Verify user exists in database
      const dbUser = await userRepository.findOne({
        where: { email: userDto.email }
      })

      expect(dbUser).toBeDefined()
      expect(dbUser.fullName).toBe(userDto.fullName)
      expect(dbUser.password).not.toBe(userDto.password) // Password should be hashed
    })

    it('should handle password change', async () => {
      // Register user
      const userDto = {
        email: '[email protected]',
        password: 'OriginalPassword123!',
        fullName: 'Password Test User'
      }

      const registerResponse = await request(app.getHttpServer())
        .post('/auth/register')
        .send(userDto)
        .expect(201)

      const { access_token } = registerResponse.body

      // Change password
      const changePasswordResponse = await request(app.getHttpServer())
        .post('/auth/change-password')
        .set('Authorization', `Bearer ${access_token}`)
        .send({
          currentPassword: 'OriginalPassword123!',
          newPassword: 'NewPassword456!'
        })
        .expect(200)

      // Try login with old password (should fail)
      await request(app.getHttpServer())
        .post('/auth/login')
        .send({
          email: userDto.email,
          password: 'OriginalPassword123!'
        })
        .expect(401)

      // Try login with new password (should succeed)
      const loginResponse = await request(app.getHttpServer())
        .post('/auth/login')
        .send({
          email: userDto.email,
          password: 'NewPassword456!'
        })
        .expect(200)

      expect(loginResponse.body.access_token).toBeDefined()
    })
  })
})

// 8. Mock Testing with Jest Mocks
// src/auth/auth.service.mock.spec.ts
import { Test, TestingModule } from '@nestjs/testing'
import { AuthService } from './auth.service'
import { UsersService } from '../users/users.service'
import { JwtService } from '@nestjs/jwt'

describe('AuthService (Mocked)', () => {
  let service: AuthService
  let mockUsersService: Partial<UsersService>
  let mockJwtService: Partial<JwtService>

  beforeEach(async () => {
    mockUsersService = {
      findByEmail: jest.fn(),
      create: jest.fn(),
      findOne: jest.fn(),
      updateLastLogin: jest.fn(),
      update: jest.fn()
    }

    mockJwtService = {
      sign: jest.fn()
    }

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AuthService,
        {
          provide: UsersService,
          useValue: mockUsersService
        },
        {
          provide: JwtService,
          useValue: mockJwtService
        }
      ]
    }).compile()

    service = module.get<AuthService>(AuthService)
  })

  describe('login', () => {
    it('should handle successful login with mocked dependencies', async () => {
      const mockUser = {
        id: 1,
        email: '[email protected]',
        password: 'hashedPassword',
        isActive: true,
        role: 'user'
      }

      mockUsersService.findByEmail!.mockResolvedValue(mockUser)
      mockJwtService.sign!.mockReturnValue('mock-jwt-token')

      const result = await service.login({
        email: '[email protected]',
        password: 'password'
      })

      expect(mockUsersService.findByEmail).toHaveBeenCalledWith('[email protected]')
      expect(mockJwtService.sign).toHaveBeenCalledWith({
        sub: 1,
        email: '[email protected]',
        role: 'user'
      })
      expect(result).toEqual({
        access_token: 'mock-jwt-token',
        user: {
          id: 1,
          email: '[email protected]',
          fullName: mockUser.fullName,
          role: 'user'
        }
      })
    })
  })
})