Implementing Request Header-Based Version Detection in NestJS — Using an Interceptor
- 778Words
- 4Minutes
- 14 Sep, 2024
In a recent project, we needed to add version control functionality to the API to ensure that different versions of the client could handle interface changes while prompting older clients to update when necessary. This is a common requirement in mobile development, where the client typically sends the current version number via the HTTP request header, and the server returns the corresponding response data based on that version number.
The issue we encountered was that in some cases, when the client version was too outdated, we wanted to attach an update prompt to the normal return data. This would allow the interface request side to handle this logic uniformly without changing the original business logic. To address this, we could use an interceptor or middleware in NestJS.
This article takes the interceptor as an example to guide you through the implementation. For middleware implementation, refer to this article: “Implementing Request Header-Based Version Detection in NestJS — Using Middleware”
Requirement Analysis
The requirement is simple: the client version number is carried in the header of each request, and the server checks this version number. If the client’s version is lower than the latest version required by the server, an update prompt needs to be included in the response; if the version is valid, the data is returned normally without modification.
How to Implement It
In NestJS, we can capture both requests and responses via an interceptor. In the interceptor, we can:
- Obtain the version information from the request header.
- Determine if an update is required by a simple version number comparison.
- Modify the response data by adding an extra field if an update is needed.
1. Creating a Version Detection Interceptor
Interceptors are essential tools in NestJS for intercepting requests and responses. In this scenario, we need to create an interceptor responsible for version detection and processing before the response data is sent back to the client.
1import {2 CallHandler,3 ExecutionContext,4 Injectable,5 NestInterceptor,6} from "@nestjs/common";7import { Observable } from "rxjs";8import { map } from "rxjs/operators";9
10@Injectable()11export class VersionInterceptor implements NestInterceptor {12 private readonly latestVersion = "2.0.0"; // Set the latest version13
14 intercept(context: ExecutionContext, next: CallHandler): Observable<any> {15 const request = context.switchToHttp().getRequest();16 const version = request.headers["version"]; // Get the version from the header17
18 return next.handle().pipe(19 map((data) => {20 if (version && this.needsUpdate(version)) {21 // Add extra fields if an update is needed22 return {23 ...data,24 updateAvailable: true,25 updateUrl: "xxxx", // Update URL26 latestVersion: this.latestVersion,27 message: "A new version is available, please update.",28 };29 }30 // Return the original response data if no update is needed31 return data;32 }),33 );34 }35
36 // Version comparison logic37 private needsUpdate(clientVersion: string): boolean {38 return clientVersion < this.latestVersion;39 }40}
In this interceptor, we use request.headers['version']
to get the version number sent by the client and call the needsUpdate
method to compare versions. If the client version is outdated, we insert an additional updateAvailable
field into the returned data to inform the user that a new version is available.
2. Applying the Interceptor
There are two ways to apply this interceptor in NestJS: local or global.
Applying the Interceptor Locally
If the version detection function is needed only on certain routes, you can apply the interceptor in the corresponding controller.
1import { Controller, Get, UseInterceptors } from "@nestjs/common";2import { VersionInterceptor } from "./version.interceptor";3
4@Controller("api")5export class AppController {6 @Get("data")7 @UseInterceptors(VersionInterceptor)8 getData() {9 return {10 data: "Here is your data",11 };12 }13}
Applying the Interceptor Globally
If you want all API endpoints to support version detection, you can apply the interceptor globally via main.ts
.
1import { NestFactory } from "@nestjs/core";2import { AppModule } from "./app.module";3import { VersionInterceptor } from "./version.interceptor";4
5async function bootstrap() {6 const app = await NestFactory.create(AppModule);7
8 // Apply the interceptor globally9 app.useGlobalInterceptors(new VersionInterceptor());10
11 await app.listen(3000);12}13bootstrap();
3. Testing the API
After setting up the interceptor, we can test the API using curl
or Postman. The client sends the version information via the request header, and the server returns the corresponding response based on the version number.
Example Request
1curl -X GET http://localhost:3000/api/data -H "version: 1.0.0"
Example Response (When an update is needed)
1{2 "data": "Here is your data",3 "updateAvailable": true,4 "latestVersion": "2.0.0",5 "message": "A new version is available, please update."6}
Example Response (When no update is needed)
1{2 "data": "Here is your data"3}
Conclusion
Interceptors are a highly efficient and flexible tool for handling version detection. They allow us to dynamically modify response data without altering the controller logic, returning different prompt information based on the client’s version. This approach is not only suitable for version control but also for other similar scenarios, such as globally formatting response content or adding extra metadata.
This method helps maintain API compatibility while ensuring that old versions of the client receive update prompts in time, improving the user experience.