Modules, Controllers & Services
Modules, Controllers & Services NestJS Architecture NestJS organizes code into Modules. Each module groups related Controllers (handle HTTP) and Providers/Servi…
Modules, Controllers & Services
NestJS Architecture
NestJS organizes code into Modules. Each module groups related Controllers (handle HTTP) and Providers/Services (business logic). The root AppModule bootstraps the app.
// app.module.ts
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
@Module({
imports: [UsersModule, AuthModule],
})
export class AppModule {}
// users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
imports: [],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService], // make available to other modules
})
export class UsersModule {}Controllers
import {
Controller, Get, Post, Put, Patch, Delete,
Body, Param, Query, Req, Res, HttpCode, HttpStatus,
UseGuards, UseInterceptors, ParseIntPipe, ParseUUIDPipe,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
@Controller('users') // base route: /users
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get() // GET /users
findAll(@Query('page') page = 1, @Query('limit') limit = 10) {
return this.usersService.findAll({ page, limit });
}
@Get(':id') // GET /users/:id
findOne(@Param('id', ParseUUIDPipe) id: string) {
return this.usersService.findOne(id);
}
@Post() // POST /users
@HttpCode(HttpStatus.CREATED)
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Patch(':id') // PATCH /users/:id
update(@Param('id') id: string, @Body() updateDto: Partial<CreateUserDto>) {
return this.usersService.update(id, updateDto);
}
@Delete(':id') // DELETE /users/:id
@HttpCode(HttpStatus.NO_CONTENT)
remove(@Param('id') id: string) {
return this.usersService.remove(id);
}
}Services (Providers)
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';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly usersRepo: Repository<User>,
) {}
async findAll(pagination: { page: number; limit: number }) {
const { page, limit } = pagination;
return this.usersRepo.findAndCount({
skip: (page - 1) * limit,
take: limit,
order: { createdAt: 'DESC' },
});
}
async findOne(id: string): Promise<User> {
const user = await this.usersRepo.findOneBy({ id });
if (!user) throw new NotFoundException(`User ${id} not found`);
return user;
}
async create(dto: CreateUserDto): Promise<User> {
const user = this.usersRepo.create(dto);
return this.usersRepo.save(user);
}
async update(id: string, dto: Partial<CreateUserDto>): Promise<User> {
await this.findOne(id); // throws if not found
await this.usersRepo.update(id, dto);
return this.findOne(id);
}
async remove(id: string): Promise<void> {
await this.findOne(id);
await this.usersRepo.delete(id);
}
}DTOs & Validation
// dto/create-user.dto.ts
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
import { Exclude, Expose } from 'class-transformer';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(2)
name: string;
@IsString()
@MinLength(8)
password: string;
@IsOptional()
@IsString()
bio?: string;
}
// main.ts — enable global validation pipe
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // strip unknown properties
forbidNonWhitelisted: true,
transform: true, // auto-convert types
}),
);
await app.listen(3000);
}
bootstrap();Exception Filters
// Built-in HTTP exceptions
import {
BadRequestException, // 400
UnauthorizedException, // 401
ForbiddenException, // 403
NotFoundException, // 404
ConflictException, // 409
InternalServerErrorException, // 500
} from '@nestjs/common';
throw new NotFoundException('User not found');
throw new ConflictException({ message: 'Email already exists', field: 'email' });
// Custom exception filter
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { 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 status = exception.getStatus();
const exceptionResponse = exception.getResponse();
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
message: typeof exceptionResponse === 'object'
? exceptionResponse
: { message: exceptionResponse },
});
}
}