MCP HubMCP Hub
スキル一覧に戻る

NestJS Framework

FortiumPartners
更新日 Today
80 閲覧
5
1
5
GitHubで表示
その他general

について

このスキルは、開発者がNestJSを使用してスケーラブルなNode.js/TypeScriptバックエンドアプリケーションを構築できるようにします。依存性の注入、モジュール式アーキテクチャ、および構造化された開発のためのエンタープライズパターンを提供します。包括的なテストと保守可能なコード構成が求められるバックエンドサービスを作成する際にご利用ください。

クイックインストール

Claude Code

推奨
プラグインコマンド推奨
/plugin add https://github.com/FortiumPartners/ai-mesh
Git クローン代替
git clone https://github.com/FortiumPartners/ai-mesh.git ~/.claude/skills/NestJS Framework

このコマンドをClaude Codeにコピー&ペーストしてスキルをインストールします

ドキュメント

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: '[email protected]' })
  @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: '[email protected]', 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: '[email protected]', 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

GitHub リポジトリ

FortiumPartners/ai-mesh
パス: skills/nestjs-framework

関連スキル

algorithmic-art

メタ

This Claude Skill creates original algorithmic art using p5.js with seeded randomness and interactive parameters. It generates .md files for algorithmic philosophies, plus .html and .js files for interactive generative art implementations. Use it when developers need to create flow fields, particle systems, or other computational art while avoiding copyright issues.

スキルを見る

subagent-driven-development

開発

This skill executes implementation plans by dispatching a fresh subagent for each independent task, with code review between tasks. It enables fast iteration while maintaining quality gates through this review process. Use it when working on mostly independent tasks within the same session to ensure continuous progress with built-in quality checks.

スキルを見る

executing-plans

デザイン

Use the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.

スキルを見る

cost-optimization

その他

This Claude Skill helps developers optimize cloud costs through resource rightsizing, tagging strategies, and spending analysis. It provides a framework for reducing cloud expenses and implementing cost governance across AWS, Azure, and GCP. Use it when you need to analyze infrastructure costs, right-size resources, or meet budget constraints.

スキルを見る