Aller au contenu

Conventions API

L’API SIM est versionnée par URI, validée avec class-validator, et retourne une enveloppe d’erreur uniforme. Les contrôleurs restent minces ; l’orchestration vit dans les services et le SQL dans les repositories.

Les contrôleurs déclarent à la fois le chemin du module et la version :

@Controller({ path: 'residence', version: '1' })

Toutes les routes protégées sont préfixées par /api/v1. /metrics est exclu du préfixe api et est public.

Les contrôleurs sont responsables de :

  • routage et mapping des méthodes HTTP
  • décorateurs Swagger (@ApiTags, @ApiBearerAuth, @ApiProperty sur les DTO)
  • décorateurs d’authentification / autorisation (@Public, @RequirePermissions, @CurrentUser)
  • extraction des DTO et paramètres, puis appel des services

Les DTO sont des classes, importées en valeur dans les contrôleurs pour que emitDecoratorMetadata fonctionne.

export class CreerClientDto {
@IsString() @IsNotEmpty()
nom: string;
@IsOptional() @IsString() @Matches(DATE_PATTERN)
date_naissance?: string | null;
}

Pipe de validation global :

app.useGlobalPipes(
new ValidationPipe({ transform: true, whitelist: true }),
);

Les unions d’enums générées sont en type seulement, donc la validation runtime utilise des tableaux @IsIn écrits à la main.

Décorateur Rôle
@Public() Ignore le JWT sur cette route
@RequirePermissions('MODULE.ACTION') Codes de permission requis
@CurrentUser() Injecte request.user en AuthUser
@Backpressure() Marque la route pour le contrôle de profondeur de file
  • ignore @Public()
  • extrait le jeton Bearer
  • vérifie signature et expiration
  • contrôle la denylist jti Redis
  • résout permissions, idClient, idCaisse, idActiviteScope
  • attache AuthUser à request.user
  • lit les métadonnées @RequirePermissions
  • autorise si vide
  • sinon vérifie que toutes les permissions listées sont dans user.permissions
  • rejette les routes @Backpressure() avec 503 quand les jobs waiting + active dépassent BACKPRESSURE_QUEUE_DEPTH (défaut 100 ; 0 désactive)

AllExceptionsFilter retourne une forme JSON uniforme :

{
"success": false,
"statusCode": 400,
"code": "DATA_ERROR",
"message": "...",
"details": [...],
"requestId": "...",
"path": "/api/v1/client/clients",
"timestamp": "..."
}

Mappages SQLSTATE PostgreSQL :

SQLSTATE Statut HTTP code
23505 409 CONFLICT
23503 422 FK_VIOLATION
23502 / 23514 400 DATA_ERROR
22P02 400 DATA_ERROR

pino logue en JSON. requestId vient de l’en-tête x-request-id ou d’un randomUUID(). La réponse renvoie x-request-id. Les champs sensibles sont rédigés : authorization, cookie, *_token, mot_de_passe.

@Post()
@RequirePermissions('CLIENT.CREER')
async creer(
@Body() dto: CreerClientDto,
@CurrentUser() user: AuthUser,
) {
return this.service.creer(dto, user.id);
}

Pour les endpoints de liste, les DTO de requête étendent ListQueryDto :

export class ListerClientsDto extends ListQueryDto {
@IsOptional() @IsString()
type_client?: string;
@IsOptional() @IsString()
recherche?: string;
}