API Versioning Strategies in NestJS
When building APIs, change is inevitable. New features, updated endpoints, and deprecated functionality all require evolving your API…
API Versioning Strategies in NestJS
Photo by HempCrew on Unsplash
When building APIs, change is inevitable. New features, updated endpoints, and deprecated functionality all require evolving your API without breaking existing clients. That’s where API versioning steps in — a crucial practice to keep your APIs robust, maintainable, and backward-compatible.
If you’re a developer building RESTful or GraphQL APIs with NestJS, or just someone exploring best practices in API design, this article is for you. We’ll walk through practical versioning strategies in NestJS, backed by real-world examples and handy code snippets. Let’s keep your APIs evolving smoothly!
Why Version Your API?
Imagine you deployed your awesome API v1, and your clients depend on it heavily. What happens when you need to add new features or change endpoint behaviors? Without versioning, updates might break existing integrations, causing frustration and downtime.
API versioning lets you:
- Make non-breaking changes confidently
- Support multiple client versions simultaneously
- Communicate clearly about API lifecycle and deprecations
NestJS, with its modular architecture and decorators, offers flexible ways to manage API versions elegantly. Let’s dive into some common strategies and how to implement them.
1. URI Versioning: The Classic Approach
What is It?
URI versioning involves embedding the API version directly into the request URL path, like /api/v1/users and /api/v2/users. This is the most explicit and widely supported method to version your API.
Why Use It?
- Easy to understand and implement
- Clear separation between API versions
- Works well with browser clients and simple HTTP tools
Real-World Example
Suppose you have a user retrieval endpoint that evolves from v1 to v2 with added fields.
How to Implement in NestJS
NestJS has built-in support for URI versioning. Here’s how you can set it up for a controller:
import { Controller, Get, Version } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
@Version('1')
getUsersV1() {
return [{ id: 1, name: 'Alice' }]; // Simple user object
}
@Get()
@Version('2')
getUsersV2() {
return [
{ id: 1, name: 'Alice', email: 'alice@example.com' }, // Added email field in v2
];
}
}
You then tell your NestJS app to use URI versioning in your main bootstrap file:
import { VersioningType } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.URI,
});
await app.listen(3000);
}
bootstrap();
With this setup:
- Request GET /v1/users returns the simpler user data
- Request GET /v2/users returns the richer user data with email
Pro Tip
Keep your route definitions constant (@Controller(‘users’) and @Get()) but differentiate versions with @Version(). NestJS handles routing magic behind the scenes.
2. Header Versioning: When You Want to Keep URLs Clean
What is It?
Some prefer to keep URLs clean and instead specify the API version in HTTP headers — for example, using a custom header like X-API-Version: 1 or the standard Accept header with versioned media types.
Why Use It?
- URLs remain clean and consistent
- Flexible for clients that can customize headers
- Often preferred in enterprise settings or APIs consumed by complex clients
How to Implement in NestJS
NestJS supports header-based versioning through configuration.
Example using a custom header:
import { VersioningType } from '@nestjs/common';
// Enable header versioning with a custom header name
app.enableVersioning({
type: VersioningType.HEADER,
header: 'X-API-Version',
});
Controller example stays the same as before:
@Controller('users')
export class UsersController {
@Get()
@Version('1')
getUsersV1() {
return [{ id: 1, name: 'Alice' }];
}
@Get()
@Version('2')
getUsersV2() {
return [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
];
}
}
Now clients include the version in request headers:
GET /users X-API-Version: 1
or
GET /users X-API-Version: 2
Quick Tip
If you prefer using the Accept header, e.g., Accept: application/vnd.myapp.v1+json, you can implement a custom versioning strategy by extending NestJS’s built-in versioning mechanism.
3. Media Type Versioning (a.k.a Content Negotiation)
What is It?
This method uses the Accept HTTP header with custom media types indicating the version, like:
Accept: application/vnd.myapp.v1+json
Why Choose It?
- Ideal if you want versioning embedded in content negotiation
- Supports sophisticated client-server contracts
- Cleaner URLs and header-based control
How to Implement It?
NestJS does not provide direct media type versioning out of the box, but you can create a custom versioning strategy:
import {
VersioningStrategy,
IncomingRequest,
RequestMethod,
} from '@nestjs/common';
export class MediaTypeVersioningStrategy implements VersioningStrategy {
extractVersion(request: IncomingRequest): string | undefined {
const acceptHeader = request.headers['accept'];
if (!acceptHeader) return undefined;
const versionMatch = acceptHeader.match(/application\/vnd\.myapp\.v(\d+)\+json/);
if (versionMatch) {
return versionMatch[1];
}
return undefined;
}
}
Then enable it:
app.enableVersioning({
type: VersioningType.CUSTOM,
strategy: new MediaTypeVersioningStrategy(),
});
The controllers remain the same with the @Version decorator.
4. Attribute and Query Parameter Versioning — The Quick and Dirty Ways
Sometimes teams prefer putting the version as a query parameter, like /users?version=1, or as a custom attribute. While not as clean or robust as URI or header versioning, they’re useful for quick experiments or early projects.
NestJS supports query parameter versioning out of the box:
app.enableVersioning({
type: VersioningType.QUERY,
query: 'version',
});
Clients then request:
GET /users?version=1
And your controllers use the same @Version decorators.
Conclusion: Pick What Fits Your Needs
API versioning is essential for building scalable, maintainable services. NestJS makes versioning accessible through multiple strategies:
- URI versioning is simple and explicit — great for most use cases.
- Header versioning keeps URLs clean and works well when your clients support custom headers.
- Media type versioning is sophisticated and integrates well with content negotiation, though requires a bit more work.
- Query parameter versioning is quick to add but less elegant.
Next Steps
- Try adding versioning to your existing NestJS project.
- Experiment with different strategies to see which fits your clients and deployment style.
- Explore advanced topics like deprecating old API versions or automated version-based documentation with Swagger.
If you’re excited about building future-proof APIs, versioning is your best friend. Happy coding!
메타데이터
- post_id
- f1f0576c92fa
- slug
- api-versioning-strategies-in-nestjs-f1f0576c92fa
- url
- https://medium.com/@jradzik4/api-versioning-strategies-in-nestjs-f1f0576c92fa
- canonical_url
- https://medium.com/@jradzik4/api-versioning-strategies-in-nestjs-f1f0576c92fa
- author_url
- https://medium.com/@jradzik4
- status
- ok
- fetched_at
- 2026-06-09 14:34:10