Claude Code Plugins

Community-maintained marketplace

Feedback

Node.js/TypeScript backend framework with dependency injection and modular architecture

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name NestJS Framework
version 1.0.0
framework_versions [object Object]
compatible_agents [object Object]
description Node.js/TypeScript backend framework with dependency injection and modular architecture
frameworks nestjs
languages typescript, javascript
category backend
updated Wed Oct 22 2025 00:00:00 GMT+0000 (Coordinated Universal Time)

NestJS Framework Skill

Quick Reference

When to Use: Building scalable Node.js/TypeScript backend applications with modular architecture

Core Strengths: Dependency injection, modular design, enterprise patterns, comprehensive testing

Target Coverage: Services ≥80%, Controllers ≥70%, E2E ≥60%, Overall ≥75%

Essential Patterns

Module Architecture

// users/users.module.ts
@Module({
  imports: [TypeOrmModule.forFeature([User]), AuthModule],
  controllers: [UserController],
  providers: [
    UserService,
    UserRepository,
    { provide: 'USER_REPOSITORY', useClass: UserRepository },
  ],
  exports: [UserService],
})
export class UsersModule {}

Key Principles:

  • Clear module boundaries and responsibilities
  • Export only what other modules need
  • Import shared modules (AuthModule, DatabaseModule)
  • Use token-based providers for abstraction

Dependency Injection

// users/services/user.service.ts
@Injectable()
export class UserService {
  constructor(
    @Inject('USER_REPOSITORY') private readonly userRepository: UserRepository,
    private readonly hashingService: HashingService,
    private readonly eventEmitter: EventEmitter2,
  ) {}

  async createUser(dto: CreateUserDto): Promise<User> {
    const hashedPassword = await this.hashingService.hash(dto.password);
    const user = await this.userRepository.create({
      ...dto,
      password: hashedPassword,
    });
    this.eventEmitter.emit('user.created', user);
    return user;
  }
}

Best Practices:

  • Use constructor injection for all dependencies
  • Inject interfaces/tokens, not concrete implementations
  • Keep services focused on single responsibility
  • Emit events for cross-cutting concerns

DTO Validation

// users/dto/create-user.dto.ts
export class CreateUserDto {
  @ApiProperty({ example: 'john@example.com' })
  @IsEmail({}, { message: 'Invalid email format' })
  email: string;

  @ApiProperty({ example: 'StrongP@ss123', minLength: 8 })
  @IsString()
  @MinLength(8)
  @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/, {
    message: 'Password must contain uppercase, lowercase, number, symbol'
  })
  password: string;

  @ApiProperty({ example: 'John Doe', required: false })
  @IsOptional()
  @MaxLength(100)
  name?: string;
}

Validation Rules:

  • All inputs validated with class-validator decorators
  • API documentation via @ApiProperty
  • Custom error messages for user clarity
  • Optional fields with @IsOptional()

Repository Pattern

// users/repositories/user.repository.ts
@Injectable()
export class UserRepository {
  constructor(
    @InjectRepository(User) private readonly repository: Repository<User>
  ) {}

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

  async findById(id: number): Promise<User> {
    const user = await this.repository.findOne({ where: { id } });
    if (!user) throw new NotFoundException('User not found');
    return user;
  }

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

Repository Guidelines:

  • Encapsulate all database operations
  • Throw domain-specific exceptions
  • Use TypeORM query builder for complex queries
  • Keep repositories focused on data access only

Controller Best Practices

// users/controllers/user.controller.ts
@ApiTags('users')
@Controller('users')
@UseInterceptors(ClassSerializerInterceptor)
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post()
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Create new user' })
  @ApiResponse({ status: 201, type: UserResponseDto })
  @ApiResponse({ status: 400, description: 'Validation failed' })
  async create(
    @Body(ValidationPipe) dto: CreateUserDto
  ): Promise<UserResponseDto> {
    const user = await this.userService.create(dto);
    return plainToInstance(UserResponseDto, user);
  }

  @Get(':id')
  @UseGuards(JwtAuthGuard)
  @ApiParam({ name: 'id', type: 'number' })
  async findOne(
    @Param('id', ParseIntPipe) id: number
  ): Promise<UserResponseDto> {
    const user = await this.userService.findById(id);
    return plainToInstance(UserResponseDto, user);
  }
}

Controller Checklist:

  • @ApiTags for logical grouping
  • @ApiOperation for endpoint description
  • @ApiResponse for status codes + types
  • ValidationPipe for DTO validation
  • Guards for authentication/authorization
  • Transform responses with DTOs

Authentication & Authorization

// auth/guards/jwt-auth.guard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  handleRequest(err, user, info) {
    if (err || !user) {
      throw new UnauthorizedException('Invalid or expired token');
    }
    return user;
  }
}

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

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

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

// Usage in controller
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(Role.ADMIN)
@Delete(':id')
async delete(@Param('id') id: number): Promise<void> {
  await this.userService.delete(id);
}

Auth Patterns:

  • JWT strategy with Passport.js
  • Role-based access control with custom decorators
  • Guard composition for complex rules
  • Secure password hashing (bcrypt, argon2)

Exception Handling

// common/filters/http-exception.filter.ts
@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(),
      path: ctx.getRequest().url,
      message: typeof exceptionResponse === 'string'
        ? exceptionResponse
        : (exceptionResponse as any).message,
    });
  }
}

// Usage in main.ts
app.useGlobalFilters(new HttpExceptionFilter());

Error Strategy:

  • Global exception filter for consistency
  • Domain-specific exceptions (UserNotFoundException)
  • Include correlation IDs for debugging
  • Log errors with appropriate severity

Testing

// users/services/user.service.spec.ts
describe('UserService', () => {
  let service: UserService;
  let repository: MockType<UserRepository>;
  let hashingService: MockType<HashingService>;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: 'USER_REPOSITORY',
          useFactory: mockRepository,
        },
        {
          provide: HashingService,
          useFactory: mockHashingService,
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    repository = module.get('USER_REPOSITORY');
    hashingService = module.get(HashingService);
  });

  describe('createUser', () => {
    it('should hash password and create user', async () => {
      const dto = { email: 'test@test.com', password: 'Pass123!' };
      const hashedPassword = 'hashed_password';

      hashingService.hash.mockResolvedValue(hashedPassword);
      repository.findByEmail.mockResolvedValue(null);
      repository.create.mockResolvedValue({ id: 1, ...dto, password: hashedPassword });

      const result = await service.createUser(dto);

      expect(hashingService.hash).toHaveBeenCalledWith(dto.password);
      expect(repository.create).toHaveBeenCalledWith({
        ...dto,
        password: hashedPassword,
      });
      expect(result.id).toBe(1);
    });

    it('should throw ConflictException if user exists', async () => {
      const dto = { email: 'test@test.com', password: 'Pass123!' };
      repository.findByEmail.mockResolvedValue({ id: 1 });

      await expect(service.createUser(dto)).rejects.toThrow(ConflictException);
    });
  });
});

Test Strategy:

  • Unit tests for services (≥80% coverage)
  • Integration tests for repositories
  • E2E tests for critical workflows (≥60% coverage)
  • Mock external dependencies
  • Test error paths and edge cases

Common Anti-Patterns

❌ Don't: Tight Coupling

// Bad: Direct dependency on implementation
@Injectable()
export class UserService {
  async createUser(email: string, password: string) {
    const hashed = await bcrypt.hash(password, 10); // Tight coupling!
    // ...
  }
}

✅ Do: Abstraction

// Good: Depend on abstraction
@Injectable()
export class UserService {
  constructor(private readonly hashingService: HashingService) {}

  async createUser(dto: CreateUserDto) {
    const hashed = await this.hashingService.hash(dto.password);
    // ...
  }
}

❌ Don't: No Input Validation

// Bad: No validation, any type
@Post()
async create(@Body() body: any) {
  return this.service.create(body);
}

✅ Do: DTO Validation

// Good: Strong typing + validation
@Post()
async create(@Body(ValidationPipe) dto: CreateUserDto): Promise<UserResponseDto> {
  return this.service.create(dto);
}

❌ Don't: Expose Sensitive Data

// Bad: Returns password field!
@Get(':id')
async findOne(@Param('id') id: string) {
  return this.userService.findOne(id); // Contains password
}

✅ Do: Transform Responses

// Good: Use response DTO with @Exclude()
@Get(':id')
@UseInterceptors(ClassSerializerInterceptor)
async findOne(@Param('id', ParseIntPipe) id: number): Promise<UserResponseDto> {
  const user = await this.userService.findById(id);
  return plainToInstance(UserResponseDto, user); // Excludes password
}

Performance Patterns

Caching

@Injectable()
export class UserService {
  constructor(
    @Inject(CACHE_MANAGER) private cacheManager: Cache,
    private userRepository: UserRepository,
  ) {}

  async findById(id: number): Promise<User> {
    const cacheKey = `user:${id}`;
    const cached = await this.cacheManager.get<User>(cacheKey);
    if (cached) return cached;

    const user = await this.userRepository.findById(id);
    await this.cacheManager.set(cacheKey, user, 3600); // 1 hour TTL
    return user;
  }
}

Background Jobs

// users/processors/email.processor.ts
@Processor('email')
export class EmailProcessor {
  @Process('welcome')
  async sendWelcomeEmail(job: Job<{ email: string; name: string }>) {
    const { email, name } = job.data;
    await this.emailService.sendWelcome(email, name);
  }
}

// In service
await this.emailQueue.add('welcome', { email: user.email, name: user.name });

Query Optimization

// Bad: N+1 query problem
const users = await this.repository.find();
for (const user of users) {
  user.orders = await this.orderRepository.findByUserId(user.id);
}

// Good: Use relations
const users = await this.repository.find({
  relations: ['orders'],
});

Integration Checklist

  • Module properly structured with clear boundaries
  • All dependencies injected via constructor
  • DTOs with class-validator decorators
  • Repository pattern for data access
  • Guards for authentication/authorization
  • Exception filters for consistent errors
  • OpenAPI/Swagger documentation
  • Unit tests ≥80% coverage
  • Integration tests for API endpoints
  • E2E tests for critical paths
  • Caching for frequently accessed data
  • Background jobs for async operations
  • Health check endpoint

Quick Commands

# Generate resources
nest g module users
nest g controller users
nest g service users

# Generate complete CRUD
nest g resource users

# Run tests
npm run test           # Unit tests
npm run test:e2e       # E2E tests
npm run test:cov       # Coverage report

# Build and run
npm run build
npm run start:dev      # Watch mode
npm run start:prod     # Production

See Also

  • REFERENCE.md - Comprehensive guide with microservices, GraphQL, advanced patterns
  • templates/ - Code generation templates
  • examples/ - Real-world implementation examples