| name | api-validation |
| description | Use when adding or changing API validation in apps/api. |
Generated from ai/registry.json. Do not edit manually.
Canonical skill: ../../../ai/skills/api-validation.md.
Referenced context:
../../../ai/rules/api-validation-rules.md
../../../ai/architecture/api-app.md
../../../ai/examples/good-api-validation.md
This file is compiled from canonical AI knowledge files. Edit canonical files under ai, then run npm run ai:sync.
Compiled AI Skill: api-validation
Canonical Skill: ai/skills/api-validation.md
API Validation
Use this skill when adding or changing API controllers, route params, request DTOs, or validation behavior in apps/api.
Goal
Keep request validation at Nest boundaries while preserving service ownership of authorization, existence checks, and business rules.
Read First
ai/rules/api-validation-rules.md
ai/architecture/api-app.md
ai/examples/good-api-validation.md
Related References
- Use
api-module-architecture when the validation change also restructures controllers, use cases, support services, providers, or module exports.
Workflow
- Inspect nearby controller and DTO patterns before editing.
- Add DTO decorators for request-body shape, format, and normalization.
- Use
UuidParam, the shared wrapper around ParseUUIDPipe, for UUID route params.
- Keep database-aware and tenant-aware validation in services.
- Preserve shared Prisma exception handling for uniqueness and constraint errors.
Expected Output
- Controllers parse and delegate.
- DTOs validate and normalize request bodies.
- Services enforce authorization, ownership, existence, and business invariants.
Verification
- Search controllers for UUID route params and verify IDs use
UuidParam instead of raw string params.
- Check every
@Body() type is a DTO with validation decorators.
- Run
npm --workspace @capture-flag/api run build.
Referenced Context
Reference: ai/rules/api-validation-rules.md
API Validation Rules
Rules for Nest controllers, DTOs, and request validation in apps/api.
Always
- Keep the global Nest
ValidationPipe enabled in src/main.ts.
- Use DTO classes with
class-validator decorators for request bodies.
- Use
class-transformer decorators such as @Transform and @Type for normalization.
- Use
UuidParam, the shared wrapper around ParseUUIDPipe, for every UUID route param before passing it into services.
- Trim user-entered strings with
@Transform before validating.
- Pair optional string identifiers with specific validators such as
@IsUUID() or @IsEmail().
- Use role allowlists with
@IsIn(...) and shared role constants.
- Keep authorization, existence checks, ownership checks, and business rules in services.
Never
- Do not accept raw
string params for IDs backed by Prisma @db.Uuid fields.
- Do not put business rules into DTO decorators when they require database context.
- Do not let controllers perform tenant ownership checks.
- Do not bypass the shared Prisma exception filter for mapped uniqueness and constraint errors.
- Do not accept unvalidated request bodies into services.
Verification
- Search controllers for UUID route params and ensure they use
UuidParam instead of raw string params.
- Check every
@Body() type is a DTO with validation decorators.
- Run
npm --workspace @capture-flag/api run build after API validation changes.
Reference: ai/architecture/api-app.md
API App Architecture
apps/api is a NestJS API backed by Prisma and PostgreSQL.
Request Flow
- Controllers define routes, parse params, receive DTOs, and pass
request.user.id to services.
- Session-only private controllers use
SessionGuard and AuthenticatedRequest.
- API-token-capable management controllers use
AuthenticatedApiGuard, then API token tenant/scope guards.
- API-token-only management routes can use
ApiTokenGuard directly, followed by tenant/scope guards.
- DTO classes validate and normalize request bodies.
- UUID route params use
UuidParam, the shared wrapper around ParseUUIDPipe, in controllers.
- Services own authorization, existence checks, ownership checks, business rules, and Prisma calls.
Module Structure
- API modules use explicit Nest imports, controllers, providers, and exports.
- Controllers stay thin and delegate to facade services or focused use-case services.
- Facade services are module boundaries for controllers or cross-module consumers; they should delegate rather than own business rules.
- Use-case services own operation behavior, tenant checks, Prisma calls, transactions, audit writes, config-state bumps, and no-op behavior.
support services hold concrete reusable responsibilities such as access, audit, input normalization, read models, credentials, config state, references, validation, writers, or initializers.
- Modules export only the current boundary needed by another module, not internal support providers by default.
Persistence
- Prisma schema and migrations define the data model.
- Prisma access is injected through
PrismaService.
- Tenant checks usually resolve parent entities before child reads or writes.
- Constraint and uniqueness errors are mapped through the shared Prisma exception filter.
Public SDK Boundary
- Public SDK config routes are unauthenticated by session.
- SDK keys authenticate public config access through hashed key lookup.
- SDK evaluation context never reaches the API.
- Public config output is a versioned SDK contract, not an internal API DTO.
Public Management Boundary
- Public Management API automation uses API tokens as Bearer credentials on a documented subset of
/api/v1.
- API token routes require tenant checks, scope checks, and normal RBAC checks through the token subject user.
- OpenAPI at
/api/v1/docs is filtered to the supported management subset.
- Management rate limits are currently process-local in memory.
Security Boundary
- API bootstrap calls
applyHttpSecurity() before request handling.
- Helmet, CORS, HTTPS enforcement, trust proxy, and rate limit guards live in API infrastructure, not services.
- Raw session tokens, SDK keys, and API tokens must not be persisted or logged.
Reference: ai/examples/good-api-validation.md
Good API Validation
Source: apps/api/src/feature-flags/feature-flags.controller.ts (sha256: 1949c60371de22bf12e05ef4e366708dc2c7adfb69027c12f50516f741152f07)
Source: apps/api/src/projects/dto/projects.dto.ts (sha256: 8ba57f5fea22889c71de3571a4285309d127ceecfa8b7527b215dbf9e432508b)
Why this is canonical:
- Keeps controllers thin by parsing params and passing authenticated user identity.
- Uses
UuidParam, a small wrapper around ParseUUIDPipe, at controller boundaries.
- Leaves database-aware validation and ownership checks in services.
Canonical controller and colocated DTO patterns from apps/api.
Controller Param Validation
@Get("configs/:configId/feature-flags")
list(@CurrentUserId() userId: string, @UuidParam("configId") configId: string) {
return this.listFeatureFlags.execute({ userId, configId });
}
Controllers parse UUID params and pass authenticated user identity to use-case services.
DTO Normalization
export class CreateProjectDto {
@ApiProperty({ maxLength: 120, minLength: 1 })
@Transform(({ value }) => trimString(value))
@IsString()
@MinLength(1)
@MaxLength(120)
name!: string;
@ApiPropertyOptional({ maxLength: 80, minLength: 1 })
@Transform(({ value }) => trimString(value))
@IsOptional()
@IsString()
@MinLength(1)
@MaxLength(80)
slug?: string;
}
DTOs normalize strings before validation and keep database-aware rules in services.