AI Skill Report Card
Building NestJS APIs
NestJS API Development
Quick Start15 / 15
TypeScript// app.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule } from '@nestjs/config'; import { UsersModule } from './users/users.module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), TypeOrmModule.forRoot({ type: 'postgres', host: process.env.DB_HOST, port: +process.env.DB_PORT, username: process.env.DB_USER, password: process.env.DB_PASS, database: process.env.DB_NAME, autoLoadEntities: true, synchronize: process.env.NODE_ENV !== 'production', }), UsersModule, ], }) export class AppModule {}
Recommendation▾
Reduce verbosity in workflow explanations - Claude understands NestJS basics and doesn't need every decorator explained
Workflow14 / 15
Progress:
- Set up module structure with proper separation of concerns
- Define entities with TypeORM decorators
- Create DTOs with class-validator
- Implement services with dependency injection
- Build controllers with proper HTTP methods
- Add authentication guards and middleware
- Configure database connections and migrations
- Implement error handling and logging
Step 1: Module Architecture
TypeScript// users/users.module.ts @Module({ imports: [TypeOrmModule.forFeature([User])], controllers: [UsersController], providers: [UsersService], exports: [UsersService], }) export class UsersModule {}
Step 2: Entity Definition
TypeScript// users/entities/user.entity.ts @Entity() export class User { @PrimaryGeneratedColumn('uuid') id: string; @Column({ unique: true }) email: string; @Column() @Exclude() password: string; @CreateDateColumn() createdAt: Date; }
Step 3: DTO Validation
TypeScript// users/dto/create-user.dto.ts export class CreateUserDto { @IsEmail() @IsNotEmpty() email: string; @IsString() @MinLength(8) @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/) password: string; }
Step 4: Service Implementation
TypeScript// users/users.service.ts @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository<User>, ) {} async create(createUserDto: CreateUserDto): Promise<User> { const hashedPassword = await bcrypt.hash(createUserDto.password, 10); const user = this.usersRepository.create({ ...createUserDto, password: hashedPassword, }); return this.usersRepository.save(user); } }
Step 5: Controller Setup
TypeScript// users/users.controller.ts @Controller('users') @UseGuards(JwtAuthGuard) export class UsersController { constructor(private readonly usersService: UsersService) {} @Post() @UseInterceptors(ClassSerializerInterceptor) async create(@Body() createUserDto: CreateUserDto) { return this.usersService.create(createUserDto); } }
Recommendation▾
Add more concrete input/output examples showing actual API requests and responses, not just code snippets
Examples18 / 20
Example 1: JWT Authentication Guard Input: Need to protect routes with JWT authentication Output:
TypeScript@Injectable() export class JwtAuthGuard extends AuthGuard('jwt') { canActivate(context: ExecutionContext) { return super.canActivate(context); } handleRequest(err, user, info) { if (err || !user) { throw err || new UnauthorizedException(); } return user; } }
Example 2: Global Exception Filter Input: Handle all unhandled exceptions consistently Output:
TypeScript@Catch() export class AllExceptionsFilter implements ExceptionFilter { catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; response.status(status).json({ statusCode: status, timestamp: new Date().toISOString(), path: request.url, message: exception instanceof HttpException ? exception.getResponse() : 'Internal server error', }); } }
Recommendation▾
Include a complete working example with database setup and authentication flow rather than scattered code pieces
Best Practices
- Module Organization: One module per business domain, keep modules focused and cohesive
- Dependency Injection: Use constructor injection, avoid circular dependencies
- DTOs: Always validate input with class-validator, separate DTOs for create/update operations
- Error Handling: Use built-in HTTP exceptions, implement global exception filters
- Security: Hash passwords with bcrypt, use JWT for stateless auth, implement rate limiting
- Database: Use transactions for multi-step operations, implement soft deletes where needed
- Testing: Write unit tests for services, integration tests for controllers
- Configuration: Use ConfigModule for environment variables, validate config on startup
Common Pitfalls
- Don't use
anytype - leverage TypeScript's type system fully - Don't expose entities directly - always use DTOs for API responses
- Don't forget to validate input - use ValidationPipe globally
- Don't ignore database migrations - use TypeORM migrations in production
- Don't store secrets in code - use environment variables and config validation
- Don't create god modules - keep modules small and focused
- Don't bypass guards - apply authentication/authorization consistently
- Don't ignore error logging - implement structured logging with context