Skip to content
Back to skills

Dev Nestjs Guide

ASecurity

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".

  • 27 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
ai-agentstypescriptbashnodeexpressdockertestingapidatabasebackend

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 18, 2026

npx -y skills add khalilbenaz/claude-skills-collection --skill dev-nestjs-guide --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dev Nestjs Guide?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dev Nestjs Guide
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/khalilbenaz-dev-nestjs-guide/badge)](https://www.skillsdirectory.com/skills/khalilbenaz-dev-nestjs-guide)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dev-nestjs-guide
description: 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".
---

# 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 :
| Cas | ORM recommandé |
|-----|----------------|
| Relations complexes, legacy DB | TypeORM |
| DX moderne, migrations auto | Prisma |
| Multi-DB, unit-of-work strict | MikroORM |

## 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-pattern | Problème | Correction |
|---|---|---|
| Logique métier dans le controller | Couplage HTTP/métier | Déplacer dans le service |
| `any` sur les corps de requête | Bypass de validation | DTO + `ValidationPipe` obligatoire |
| Module circulaire | Erreur runtime | `forwardRef(() => ModuleB)` ou refactorer |
| Pas de `whitelist: true` | Propriétés injectées silencieuses | Toujours activer |
| `new Service()` manuel | Échappe au DI, non testable | Injecter via constructeur |
| Secrets hardcodés | Fuite de sécurité | `ConfigService` + `.env` validé par Joi |
| N+1 queries | Perf dégradée | `relations` dans `findOne` ou QueryBuilder |
| Pas de graceful shutdown | Requêtes coupées | `app.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.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.