REST Best Practices, Serialization & Versioning
REST Best Practices, Serialization & Versioning
Building a production-ready NestJS API means going beyond making things work — you must design resources correctly, return the right HTTP status codes, protect sensitive data from leaking into responses, and evolve your API without breaking existing clients. This lesson covers four pillars: RESTful resource design, status codes, serialization with ClassSerializerInterceptor, and API versioning.
RESTful resource design
Good REST design uses nouns, not verbs, in URL paths, and relies on HTTP methods to express actions:
GET /users— list all usersGET /users/:id— fetch one userPOST /users— create a userPATCH /users/:id— partial update (prefer over PUT for partial changes)DELETE /users/:id— remove a user
Nest's @nestjs/common provides @Get, @Post, @Patch, @Put, @Delete decorators to map handlers to these verbs cleanly.
HTTP status codes
Returning the correct status code is part of the API contract. NestJS defaults to 200 for most handlers and 201 for @Post. Override with @HttpCode():
Common codes to know: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable Entity.
Serialization with ClassSerializerInterceptor
When you return a plain object, every field is sent to the client — including passwords, internal flags, and other sensitive data. NestJS ships ClassSerializerInterceptor (from @nestjs/common) to fix this. It uses class-transformer decorators to control exactly what is exposed.
Install the dependency: npm install class-transformer class-validator
Enable the interceptor globally in main.ts (or per-controller / per-handler with @UseInterceptors):
In the controller, return a new UserEntity(rawUser) (not a plain object) so the interceptor can apply the decorators:
@Expose() in whitelist mode. If you add excludeExtraneousValues: true to @SerializeOptions(), only fields marked @Expose() are included. This is safer than blacklisting — you opt-in each field rather than remembering to exclude every sensitive one.
ClassSerializerInterceptor only transforms instances of a class with class-transformer metadata. If you return a raw Prisma/TypeORM entity or a plain {}, no transformation occurs and all fields are exposed.
API versioning
Versioning lets you introduce breaking changes without breaking existing clients. NestJS supports four strategies; the two most common are URI versioning (/v1/users) and header versioning (Accept-Version: 1). Enable in main.ts:
Then decorate controllers or individual handlers:
app.enableVersioning() call — there is no need to build it manually.
Summary
Production REST APIs in NestJS rest on four pillars: resource-oriented URLs with correct HTTP verbs, precise status codes (using @HttpCode), serialization (ClassSerializerInterceptor + @Exclude/@Expose to stop sensitive fields from leaking), and versioning (URI or header, enabled with one call). Together these make your API predictable, secure, and evolvable.