AgentSkillsCN

nestjs-master

全面掌握 NestJS 框架,结合 Drizzle ORM、TypeORM、Prisma、BullMQ 队列,以及 40 条最佳实践准则。适用于 NestJS 应用程序、API、身份验证、数据库集成、队列处理、微服务、测试,以及架构决策。

SKILL.md
--- frontmatter
name: nestjs-master
description: Comprehensive NestJS framework mastery with Drizzle ORM, TypeORM, Prisma, BullMQ queues, and 40 best practice rules. Use for NestJS applications, APIs, authentication, database integration, queue processing, microservices, testing, and architectural decisions.

NestJS Master Skill

Authoritative guide for building production-grade NestJS applications. Consolidates architecture patterns, database integration, authentication, queues, and testing best practices.

ORM Selection (CRITICAL)

ALWAYS detect and use the project's existing ORM. Never switch ORMs mid-project.

Detection MethodORM
drizzle.config.ts or drizzle-orm in package.jsonDrizzle
prisma/schema.prisma or @prisma/client in package.jsonPrisma
typeorm in package.json or ormconfig.jsonTypeORM
bash
# Quick detection commands
grep -l "drizzle" package.json   # Drizzle
ls prisma/schema.prisma 2>/dev/null  # Prisma
grep -l "typeorm" package.json   # TypeORM

If no ORM exists yet, ask the user which they prefer before proceeding.

See references/database.md for patterns specific to each ORM.

Core Architecture

Module Organization

typescript
// Feature module pattern - one module per domain
@Module({
  imports: [
    DrizzleModule,
    ConfigModule.forFeature(userConfig),
  ],
  controllers: [UserController],
  providers: [UserService, UserRepository],
  exports: [UserService], // Only export what other modules need
})
export class UserModule {}

Dependency Injection Patterns

typescript
// Constructor injection (preferred)
@Injectable()
export class UserService {
  constructor(
    private readonly userRepository: UserRepository,
    private readonly configService: ConfigService,
  ) {}
}

// Custom providers for flexibility
const providers = [
  {
    provide: 'DATABASE_CONNECTION',
    useFactory: async (config: ConfigService) => {
      return drizzle(config.get('DATABASE_URL'));
    },
    inject: [ConfigService],
  },
];

Controller Best Practices

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

  @Get(':id')
  @ApiOperation({ summary: 'Get user by ID' })
  async findOne(
    @Param('id', ParseUUIDPipe) id: string,
  ): Promise<UserResponseDto> {
    const user = await this.userService.findById(id);
    if (!user) throw new NotFoundException(`User ${id} not found`);
    return plainToInstance(UserResponseDto, user);
  }

  @Post()
  @HttpCode(HttpStatus.CREATED)
  async create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
    return this.userService.create(dto);
  }
}

Database Integration

All three ORMs are fully supported. Use whichever the project already has.

Quick Comparison

FeatureDrizzlePrismaTypeORM
Type SafetyExcellent (schema-first)Excellent (generated)Good (decorators)
Query BuilderSQL-like, composableFluent APIQueryBuilder
Migrationsdrizzle-kitprisma migrateBuilt-in CLI
PerformanceLightweight, fastGood (query engine)Good
Learning CurveLow (SQL knowledge helps)LowMedium

Repository Pattern (ORM-Agnostic Interface)

typescript
// Define interface - implementation varies by ORM
export interface IUserRepository {
  findById(id: string): Promise<User | null>;
  findByEmail(email: string): Promise<User | null>;
  create(data: CreateUserDto): Promise<User>;
  update(id: string, data: UpdateUserDto): Promise<User | null>;
  delete(id: string): Promise<boolean>;
}

// Inject by token for flexibility
@Injectable()
export class UserService {
  constructor(
    @Inject('USER_REPOSITORY') 
    private readonly userRepository: IUserRepository,
  ) {}
}

ORM-Specific Implementations

See references/database.md for complete patterns:

  • Drizzle: Schema definition, module setup, queries, transactions
  • Prisma: Schema, PrismaService, queries, transactions
  • TypeORM: Entities, repositories, QueryBuilder, transactions

Transaction Pattern (All ORMs)

typescript
// Drizzle
await this.db.transaction(async (tx) => { /* use tx */ });

// Prisma  
await this.prisma.$transaction(async (tx) => { /* use tx */ });

// TypeORM
await this.dataSource.transaction(async (manager) => { /* use manager */ });

Authentication & Authorization

JWT Strategy

typescript
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly configService: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.getOrThrow('JWT_SECRET'),
    });
  }

  async validate(payload: JwtPayload): Promise<AuthUser> {
    return {
      id: payload.sub,
      email: payload.email,
      roles: payload.roles,
    };
  }
}

Role-Based Access Control

typescript
// roles.decorator.ts
export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);

// roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!requiredRoles) return true;

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

// Usage
@Get('admin')
@Roles(Role.ADMIN)
@UseGuards(JwtAuthGuard, RolesGuard)
async adminOnly() {}

Validation & Exception Handling

DTO Validation

typescript
// Always use class-validator with whitelist
app.useGlobalPipes(new ValidationPipe({
  whitelist: true,           // Strip non-whitelisted properties
  forbidNonWhitelisted: true, // Throw on non-whitelisted
  transform: true,           // Auto-transform to DTO types
  transformOptions: {
    enableImplicitConversion: true,
  },
}));

// Example DTO
export class CreateUserDto {
  @IsEmail()
  @Transform(({ value }) => value?.toLowerCase())
  email: string;

  @IsString()
  @MinLength(8)
  @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, {
    message: 'Password must contain uppercase, lowercase, and number',
  })
  password: string;

  @IsOptional()
  @IsString()
  @MaxLength(100)
  name?: string;
}

Global Exception Filter

typescript
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly logger: Logger) {}

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const { status, message } = this.getErrorDetails(exception);

    this.logger.error(`${request.method} ${request.url}`, {
      status,
      message,
      stack: exception instanceof Error ? exception.stack : undefined,
    });

    response.status(status).json({
      statusCode: status,
      message,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }

  private getErrorDetails(exception: unknown) {
    if (exception instanceof HttpException) {
      return {
        status: exception.getStatus(),
        message: exception.message,
      };
    }
    return {
      status: HttpStatus.INTERNAL_SERVER_ERROR,
      message: 'Internal server error',
    };
  }
}

Quick Reference: 40 Best Practice Rules

IDRuleCategory
arch-01One module per domain/featureArchitecture
arch-02Keep controllers thin, logic in servicesArchitecture
arch-03Use repository pattern for data accessArchitecture
arch-04Avoid circular dependencies with forwardRefArchitecture
arch-05Export only what other modules needArchitecture
di-01Prefer constructor injectionDependency Injection
di-02Use custom providers for complex setupDependency Injection
di-03Scope providers appropriately (default, request, transient)Dependency Injection
di-04Use injection tokens for non-class dependenciesDependency Injection
error-01Use built-in HTTP exceptionsError Handling
error-02Implement global exception filterError Handling
error-03Log errors with context (request ID, user)Error Handling
error-04Never expose internal errors to clientsError Handling
security-01Always validate input (see validation skill)Security
security-02Use whitelist: true in ValidationPipeSecurity
security-03Implement rate limiting on public endpointsSecurity
security-04Hash passwords with bcrypt (cost 12+)Security
security-05Use helmet for HTTP headersSecurity
security-06Validate JWT with proper expirationSecurity
perf-01Use pagination for list endpointsPerformance
perf-02Implement caching with CacheModulePerformance
perf-03Use database indexes for frequent queriesPerformance
perf-04Lazy load modules when possiblePerformance
perf-05Use connection poolingPerformance
test-01Unit test services with mocked dependenciesTesting
test-02Use TestingModule.compile() for integrationTesting
test-03E2E test with supertest and test databaseTesting
test-04Mock external services in testsTesting
test-05Test guards and interceptors in isolationTesting
db-01Use project's existing ORM consistentlyDatabase
db-02Define schema with proper constraintsDatabase
db-03Use transactions for multi-step operationsDatabase
db-04Implement soft deletes where appropriateDatabase
db-05Use migrations for schema changesDatabase
api-01Version APIs (/v1/, /v2/)API Design
api-02Use proper HTTP methods and status codesAPI Design
api-03Document with OpenAPI/SwaggerAPI Design
api-04Implement consistent response formatAPI Design
micro-01Use message patterns for microservicesMicroservices
devops-01Use ConfigService for environment variablesDevOps

References

For detailed patterns and examples, see:

When to Use This Skill

Load this skill when:

  • Building new NestJS applications or features
  • Integrating databases (Drizzle, Prisma, or TypeORM - match existing)
  • Implementing authentication/authorization
  • Setting up queue processing with BullMQ
  • Writing tests for NestJS components
  • Making architectural decisions
  • Debugging dependency injection issues