khalilbenaz/claude-skills-collection/dev-nestjs-guide

Claude Code and agent skill by khalilbenaz/claude-skills-collection

Trust tier 2
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Développement backend NestJS avec modules, controllers, providers, pipes, guards et interceptors. Se déclenche avec "NestJS", "Nest.js", "module NestJS", "controller NestJS", "decorator NestJS". Also triggers on "NestJS module", "Nest guards and interceptors", "dependency injection in Nest".

Install

npx vskill@latest install khalilbenaz/claude-skills-collection/dev-nestjs-guide

SKILL.md

Guide NestJS

1. Bootstrap du projet

bash
npm i -g @nestjs/cli
nest new my-api --strict # TypeScript strict activé d'emblée
cd my-api
nest g resource users # CRUD complet : module + controller + service + dto + entity

Choix d'ORM :

CasORM recommandé
Relations complexes, legacy DBTypeORM
DX moderne, migrations autoPrisma
Multi-DB, unit-of-work strictMikroORM

2. Structure modulaire

src/
app.module.ts ← racine
core/ ← CoreModule (logger, config, DB)
shared/ ← SharedModule (guards, pipes, interceptors réutilisables)
users/
users.module.ts
users.controller.ts
users.service.ts
dto/
create-user.dto.ts
update-user.dto.ts
entities/
user.entity.ts

Règle : un feature module déclare et exporte ce dont les autres ont besoin. Ne rien exporter par défaut.

typescript
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService], // seulement si d'autres modules en ont besoin
})
export class UsersModule {}

3. Controllers et DTOs

typescript
@ApiTags('users')
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
@HttpCode(HttpStatus.CREATED)
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {
return this.usersService.findOneOrFail(id);
}
}

DTO avec validation :

typescript
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
}

Active le pipe global dans main.ts :

typescript
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // strip propriétés non décorées
forbidNonWhitelisted: true,
transform: true, // cast automatique (string → number, etc.)
}),
);

4. Services et injection de dépendances

typescript
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly repo: Repository<User>,
private readonly configService: ConfigService,
) {}
async findOneOrFail(id: string): Promise<User> {
const user = await this.repo.findOne({ where: { id } });
if (!user) throw new NotFoundException(`User ${id} not found`);
return user;
}
}

Custom provider (ex : client externe) :

typescript
{
provide: 'PAYMENT_CLIENT',
useFactory: (cfg: ConfigService) =>
new PaymentClient(cfg.get('PAYMENT_API_KEY')),
inject: [ConfigService],
}

5. Guards et authentification JWT

typescript
// auth.guard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
// roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(ctx: ExecutionContext): boolean {
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
ctx.getHandler(),
ctx.getClass(),
]);
if (!roles) return true;
const { user } = ctx.switchToHttp().getRequest();
return roles.some((r) => user.roles?.includes(r));
}
}
// usage
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Delete(':id')
remove(@Param('id', ParseUUIDPipe) id: string) { ... }

Config Passport JWT dans JwtStrategy :

typescript
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(cfg: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: cfg.get('JWT_SECRET'),
});
}
validate(payload: JwtPayload) { return payload; }
}

6. Interceptors et gestion des erreurs

Interceptor de logging/transform :

typescript
@Injectable()
export class TransformInterceptor<T>
implements NestInterceptor<T, { data: T; timestamp: string }>
{
intercept(ctx: ExecutionContext, next: CallHandler) {
return next.handle().pipe(
map((data) => ({ data, timestamp: new Date().toISOString() })),
);
}
}

Exception filter global :

typescript
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
ctx.getResponse().status(status).json({
statusCode: status,
message: exception instanceof HttpException
? exception.message
: 'Internal server error',
timestamp: new Date().toISOString(),
});
}
}

Enregistrement dans main.ts :

typescript
app.useGlobalFilters(new AllExceptionsFilter());
app.useGlobalInterceptors(new TransformInterceptor());

7. Configuration et variables d'environnement

typescript
// app.module.ts
ConfigModule.forRoot({
isGlobal: true,
validationSchema: Joi.object({
NODE_ENV: Joi.string().valid('development', 'production', 'test').required(),
DATABASE_URL: Joi.string().required(),
JWT_SECRET: Joi.string().min(32).required(),
}),
}),

8. Tests

typescript
// users.service.spec.ts
describe('UsersService', () => {
let service: UsersService;
let repo: jest.Mocked<Repository<User>>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
UsersService,
{ provide: getRepositoryToken(User), useValue: createMock<Repository<User>>() },
],
}).compile();
service = module.get(UsersService);
repo = module.get(getRepositoryToken(User));
});
it('throws NotFoundException when user not found', async () => {
repo.findOne.mockResolvedValue(null);
await expect(service.findOneOrFail('abc')).rejects.toThrow(NotFoundException);
});
});

Test e2e :

bash
npm run test:e2e # utilise supertest + app.init()

9. Anti-patterns et pièges

Anti-patternProblèmeCorrection
Logique métier dans le controllerCouplage HTTP/métierDéplacer dans le service
any sur les corps de requêteBypass de validationDTO + ValidationPipe obligatoire
Module circulaireErreur runtimeforwardRef(() => ModuleB) ou refactorer
Pas de whitelist: truePropriétés injectées silencieusesToujours activer
new Service() manuelÉchappe au DI, non testableInjecter via constructeur
Secrets hardcodésFuite de sécuritéConfigService + .env validé par Joi
N+1 queriesPerf dégradéerelations dans findOne ou QueryBuilder
Pas de graceful shutdownRequêtes coupéesapp.enableShutdownHooks()

10. Bonnes pratiques 2026

  • Versioning API : app.enableVersioning({ type: VersioningType.URI }) + @Version('2') sur le controller.
  • Rate limiting : @nestjs/throttler global + override par route avec @Throttle().
  • Swagger automatique : @nestjs/swagger + PluginOptions dans nest-cli.json pour inférer les types sans décorateurs redondants.
  • Health checks : @nestjs/terminus — exposer /health avec checks DB, Redis, mémoire.
  • Logging structuré : remplacer le logger par défaut par pino via nestjs-pino (JSON en prod, pretty en dev).
  • Sécurité : helmet(), CORS strict, express-rate-limit, et jamais app.enableCors() sans origin explicite.
  • Docker : image multi-stage, node:20-alpine, copier uniquement dist/ + node_modules pruned.

Communication Rules — MANDATORY

  • Ultra-concise. No filler, no preamble, no pleasantries.
  • Never say "happy to help", "sure!", "great question", "let me", or similar.
  • Tool first, talk second. Act before explaining.
  • Result first. Lead with outcome, not process.
  • Stop when done. No summary, no recap, no trailing commentary.
  • No politeness wrappers. Direct and blunt.
  • Minimum words. If one word works, do not use ten.
  • No unsolicited explanations.
  • No emoji unless asked.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
2
7-day trend score
5

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 28 Apr 2026, 6 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/khalilbenaz/claude-skills-collection/dev-nestjs-guide/badge)](https://verified-skill.com/skills/khalilbenaz/claude-skills-collection/dev-nestjs-guide)

Something wrong with this skill or its page? Tell us.