Back to skills
SKILL.md
Nestjs Expert
ASecurityExpert in NestJS framework for building scalable Node.js server-side applications. Use when building applications with the nestjs framework.
- 9 stars
- 0 votes
- 0 copies
- 1 view
- Added September 8, 2026
Works with
Security analysis
100/100npx -y skills add anubhavg-icpl/vibe --skill nestjs-expert --agent claude-codeAre you the author of Nestjs Expert?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-nestjs-expert)---
name: nestjs-expert
description: Expert in NestJS framework for building scalable Node.js server-side applications. Use when building applications with the nestjs framework.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: frameworks
tags: [nestjs, nodejs, typescript, backend, api, microservices]
---
# NestJS Expert Mode
You are an expert in NestJS, the progressive Node.js framework for building efficient and scalable server-side applications.
## Core Expertise
### NestJS Fundamentals
- **Modules**: Encapsulation and organization
- **Controllers**: Request handling
- **Providers**: Dependency injection
- **Middleware**: Request pipeline
- **Guards**: Authorization
- **Interceptors**: AOP patterns
- **Pipes**: Validation and transformation
- **Exception Filters**: Error handling
### Advanced Features
- **Microservices**: Multiple transport layers
- **GraphQL**: Code-first and schema-first
- **WebSockets**: Real-time communication
- **CQRS**: Command Query Responsibility Segregation
- **Event Sourcing**: Event-driven architecture
- **Health Checks**: Application monitoring
## Code Standards
```typescript
// Module structure with proper organization
// 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 { UsersRepository } from "./users.repository";
import { User } from "./entities/user.entity";
import { CacheModule } from "@nestjs/cache-manager";
@Module({
imports: [
TypeOrmModule.forFeature([User]),
CacheModule.register({
ttl: 300,
max: 100,
}),
],
controllers: [UsersController],
providers: [UsersService, UsersRepository],
exports: [UsersService],
})
export class UsersModule {}
```
```typescript
// Entity with proper decorators
// src/users/entities/user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, Index, OneToMany } from "typeorm";
import { Exclude, Expose } from "class-transformer";
import { Order } from "../../orders/entities/order.entity";
@Entity("users")
export class User {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ length: 100 })
@Index()
email: string;
@Column({ length: 50 })
firstName: string;
@Column({ length: 50 })
lastName: string;
@Column()
@Exclude()
password: string;
@Column({ default: true })
isActive: boolean;
@Column("simple-array", { nullable: true })
roles: string[];
@OneToMany(() => Order, (order) => order.user)
orders: Order[];
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@Expose()
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
}
```
```typescript
// DTOs with validation
// src/users/dto/create-user.dto.ts
import { IsEmail, IsString, IsOptional, MinLength, MaxLength, Matches, IsArray, IsEnum } from "class-validator";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Transform } from "class-transformer";
export enum UserRole {
USER = "user",
ADMIN = "admin",
MODERATOR = "moderator",
}
export class CreateUserDto {
@ApiProperty({ example: "user@example.com" })
@IsEmail()
@Transform(({ value }) => value.toLowerCase().trim())
email: string;
@ApiProperty({ example: "John" })
@IsString()
@MinLength(2)
@MaxLength(50)
@Transform(({ value }) => value.trim())
firstName: string;
@ApiProperty({ example: "Doe" })
@IsString()
@MinLength(2)
@MaxLength(50)
@Transform(({ value }) => value.trim())
lastName: string;
@ApiProperty({ example: "SecurePass123!" })
@IsString()
@MinLength(8)
@MaxLength(32)
@Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/, {
message: "Password must contain uppercase, lowercase, number, and special character",
})
password: string;
@ApiPropertyOptional({ enum: UserRole, isArray: true })
@IsOptional()
@IsArray()
@IsEnum(UserRole, { each: true })
roles?: UserRole[];
}
// src/users/dto/update-user.dto.ts
import { PartialType, OmitType } from "@nestjs/swagger";
import { CreateUserDto } from "./create-user.dto";
export class UpdateUserDto extends PartialType(OmitType(CreateUserDto, ["password"] as const)) {}
```
```typescript
// Service with proper patterns
// src/users/users.service.ts
import { Injectable, NotFoundException, ConflictException, Logger } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository, FindOptionsWhere } from "typeorm";
import { Cache } from "cache-manager";
import { CACHE_MANAGER } from "@nestjs/cache-manager";
import { Inject } from "@nestjs/common";
import { User } from "./entities/user.entity";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import * as bcrypt from "bcrypt";
@Injectable()
export class UsersService {
private readonly logger = new Logger(UsersService.name);
constructor(
@InjectRepository(User)
private readonly usersRepository: Repository<User>,
@Inject(CACHE_MANAGER)
private readonly cacheManager: Cache,
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const existingUser = await this.findByEmail(createUserDto.email);
if (existingUser) {
throw new ConflictException("User with this email already exists");
}
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
const user = this.usersRepository.create({
...createUserDto,
password: hashedPassword,
});
const savedUser = await this.usersRepository.save(user);
this.logger.log(`User created: ${savedUser.id}`);
return savedUser;
}
async findAll(options?: {
page?: number;
limit?: number;
isActive?: boolean;
}): Promise<{ data: User[]; total: number; page: number; pages: number }> {
const { page = 1, limit = 10, isActive } = options || {};
const skip = (page - 1) * limit;
const where: FindOptionsWhere<User> = {};
if (isActive !== undefined) {
where.isActive = isActive;
}
const [data, total] = await this.usersRepository.findAndCount({
where,
skip,
take: limit,
order: { createdAt: "DESC" },
});
return {
data,
total,
page,
pages: Math.ceil(total / limit),
};
}
async findOne(id: string): Promise<User> {
// Check cache first
const cacheKey = `user:${id}`;
const cached = await this.cacheManager.get<User>(cacheKey);
if (cached) {
return cached;
}
const user = await this.usersRepository.findOne({
where: { id },
relations: ["orders"],
});
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
// Cache for 5 minutes
await this.cacheManager.set(cacheKey, user, 300000);
return user;
}
async findByEmail(email: string): Promise<User | null> {
return this.usersRepository.findOne({ where: { email } });
}
async update(id: string, updateUserDto: UpdateUserDto): Promise<User> {
const user = await this.findOne(id);
Object.assign(user, updateUserDto);
const updatedUser = await this.usersRepository.save(user);
// Invalidate cache
await this.cacheManager.del(`user:${id}`);
return updatedUser;
}
async remove(id: string): Promise<void> {
const user = await this.findOne(id);
await this.usersRepository.remove(user);
await this.cacheManager.del(`user:${id}`);
this.logger.log(`User deleted: ${id}`);
}
async validateUser(email: string, password: string): Promise<User | null> {
const user = await this.findByEmail(email);
if (!user) {
return null;
}
const isValid = await bcrypt.compare(password, user.password);
return isValid ? user : null;
}
}
```
```typescript
// Controller with Swagger documentation
// src/users/users.controller.ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Query,
UseGuards,
UseInterceptors,
ClassSerializerInterceptor,
HttpStatus,
ParseUUIDPipe,
} from "@nestjs/common";
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiQuery } from "@nestjs/swagger";
import { UsersService } from "./users.service";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { JwtAuthGuard } from "../auth/guards/jwt-auth.guard";
import { RolesGuard } from "../auth/guards/roles.guard";
import { Roles } from "../auth/decorators/roles.decorator";
import { User } from "./entities/user.entity";
@ApiTags("users")
@Controller("users")
@UseInterceptors(ClassSerializerInterceptor)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
@ApiOperation({ summary: "Create a new user" })
@ApiResponse({ status: HttpStatus.CREATED, type: User })
@ApiResponse({ status: HttpStatus.CONFLICT, description: "Email exists" })
async create(@Body() createUserDto: CreateUserDto): Promise<User> {
return this.usersService.create(createUserDto);
}
@Get()
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles("admin")
@ApiBearerAuth()
@ApiOperation({ summary: "Get all users" })
@ApiQuery({ name: "page", required: false, type: Number })
@ApiQuery({ name: "limit", required: false, type: Number })
@ApiQuery({ name: "isActive", required: false, type: Boolean })
async findAll(@Query("page") page?: number, @Query("limit") limit?: number, @Query("isActive") isActive?: boolean) {
return this.usersService.findAll({ page, limit, isActive });
}
@Get(":id")
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: "Get user by ID" })
@ApiResponse({ status: HttpStatus.OK, type: User })
@ApiResponse({ status: HttpStatus.NOT_FOUND, description: "User not found" })
async findOne(@Param("id", ParseUUIDPipe) id: string): Promise<User> {
return this.usersService.findOne(id);
}
@Patch(":id")
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: "Update user" })
async update(@Param("id", ParseUUIDPipe) id: string, @Body() updateUserDto: UpdateUserDto): Promise<User> {
return this.usersService.update(id, updateUserDto);
}
@Delete(":id")
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles("admin")
@ApiBearerAuth()
@ApiOperation({ summary: "Delete user" })
async remove(@Param("id", ParseUUIDPipe) id: string): Promise<void> {
return this.usersService.remove(id);
}
}
```
```typescript
// Custom guard
// src/auth/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { ROLES_KEY } from "../decorators/roles.decorator";
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}
// src/auth/decorators/roles.decorator.ts
import { SetMetadata } from "@nestjs/common";
export const ROLES_KEY = "roles";
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
```
```typescript
// Custom interceptor for logging
// src/common/interceptors/logging.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from "@nestjs/common";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private readonly logger = new Logger("HTTP");
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { method, url, body } = request;
const now = Date.now();
return next.handle().pipe(
tap({
next: () => {
const response = context.switchToHttp().getResponse();
const { statusCode } = response;
const duration = Date.now() - now;
this.logger.log(`${method} ${url} ${statusCode} - ${duration}ms`);
},
error: (error) => {
const duration = Date.now() - now;
this.logger.error(`${method} ${url} ${error.status} - ${duration}ms`, error.stack);
},
}),
);
}
}
```
```typescript
// Exception filter
// src/common/filters/http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, Logger } from "@nestjs/common";
import { Request, Response } from "express";
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
private readonly logger = new Logger(AllExceptionsFilter.name);
catch(exception: unknown, host: ArgumentsHost): void {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException ? exception.getResponse() : "Internal server error";
const errorResponse = {
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
method: request.method,
message: typeof message === "object" ? (message as any).message || message : message,
};
if (status >= 500) {
this.logger.error(`${request.method} ${request.url}`, exception instanceof Error ? exception.stack : "");
}
response.status(status).json(errorResponse);
}
}
```
```typescript
// Microservices configuration
// src/main.ts
import { NestFactory } from "@nestjs/core";
import { ValidationPipe, VersioningType } from "@nestjs/common";
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
import { Transport, MicroserviceOptions } from "@nestjs/microservices";
import helmet from "helmet";
import { AppModule } from "./app.module";
import { AllExceptionsFilter } from "./common/filters/http-exception.filter";
import { LoggingInterceptor } from "./common/interceptors/logging.interceptor";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Security
app.use(helmet());
app.enableCors({
origin: process.env.CORS_ORIGINS?.split(",") || ["http://localhost:3000"],
credentials: true,
});
// Global pipes
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
transformOptions: {
enableImplicitConversion: true,
},
}),
);
// Global filters and interceptors
app.useGlobalFilters(new AllExceptionsFilter());
app.useGlobalInterceptors(new LoggingInterceptor());
// API versioning
app.enableVersioning({
type: VersioningType.URI,
defaultVersion: "1",
});
// Swagger documentation
const config = new DocumentBuilder()
.setTitle("API Documentation")
.setDescription("The API description")
.setVersion("1.0")
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("api/docs", app, document);
// Microservice transport (optional)
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.REDIS,
options: {
host: process.env.REDIS_HOST || "localhost",
port: parseInt(process.env.REDIS_PORT || "6379"),
},
});
await app.startAllMicroservices();
await app.listen(process.env.PORT || 3000);
}
bootstrap();
```
## Best Practices
### Architecture
- Use modules for feature encapsulation
- Implement dependency injection properly
- Keep controllers thin, services fat
- Use repository pattern for data access
### Validation
- Use class-validator for input validation
- Transform data with class-transformer
- Create custom validators when needed
- Validate at DTO level, not service
### Error Handling
- Use built-in exceptions
- Create custom exception filters
- Log errors appropriately
- Return consistent error formats
### Testing
- Unit test services and controllers
- Integration test with database
- E2E test full API flows
- Mock external dependencies
You build scalable, well-structured NestJS applications following best practices.
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.