Implementing Header-based Version Detection in NestJS — Using Middleware

In the previous article “Implementing Header-based Version Detection in NestJS — Using Interceptor”, we introduced how to implement version detection using interceptors.

In this article, we will implement the same requirement using middleware as an example.

Requirement Analysis

The requirement is simple: include the client version number in the header of each request, and the server checks this version. If the client’s version is lower than the required latest version, an update notification field should be added to the response; if the version is acceptable, return the data normally without modification.

How to Implement

In NestJS, we can capture requests and modify responses using middleware. In the middleware, we can:

  1. Retrieve version information from the request header.
  2. Check if the version needs to be updated by a simple version comparison.
  3. Modify response data. If an update is required, add an extra field to the returned data.

1. Creating the Version Detection Middleware

Middleware in NestJS is a tool that allows intervention in the request lifecycle. In this scenario, we need to create middleware responsible for version detection and handling the response data before it is sent back to the client.

1
import { Injectable, NestMiddleware } from "@nestjs/common";
2
import { Request, Response, NextFunction } from "express";
3
4
@Injectable()
5
export class VersionMiddleware implements NestMiddleware {
6
private readonly latestVersion = "2.0.0"; // Set the latest version
7
8
use(req: Request, res: Response, next: NextFunction) {
9
const version = req.headers["version"] as string; // Get version from header
10
11
res.on("finish", () => {
12
if (version && this.needsUpdate(version)) {
13
// If an update is needed, modify the response data
14
const originalSend = res.send;
15
res.send = (body: any) => {
16
let modifiedBody = body;
17
try {
18
modifiedBody = JSON.parse(body);
19
} catch (err) {}
20
21
const updateInfo = {
22
updateAvailable: true,
23
updateUrl: "xxxx", // Update URL
24
latestVersion: this.latestVersion,
25
message: "A new version is available, please update.",
26
};
27
28
const updatedBody = { ...modifiedBody, ...updateInfo };
29
return originalSend.call(res, JSON.stringify(updatedBody));
30
};
31
}
32
});
33
34
next();
35
}
36
37
// Version comparison logic
38
private needsUpdate(clientVersion: string): boolean {
39
return clientVersion < this.latestVersion;
40
}
41
}

In this middleware, we get the client’s version via req.headers['version'] and call the needsUpdate method to compare the version. If the client version is outdated, we modify the data before the response is sent, inserting the updateAvailable field to inform the user that a new version is available.

2. Applying the Middleware

In NestJS, we can register middleware in the main.ts or app.module.ts file of the application.

Registering in app.module.ts

1
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
2
import { AppController } from "./app.controller";
3
import { VersionMiddleware } from "./version.middleware";
4
5
@Module({
6
controllers: [AppController],
7
})
8
export class AppModule implements NestModule {
9
configure(consumer: MiddlewareConsumer) {
10
consumer.apply(VersionMiddleware).forRoutes("*"); // Apply middleware to all routes
11
}
12
}

3. Testing the API

After setting up the middleware, we can test the API via curl or Postman. The client sends the version information via the request header, and the server responds accordingly based on the version.

Request Example

Terminal window
1
curl -X GET http://localhost:3000/api/data -H "version: 1.0.0"

Response Example (When 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
}

Response Example (When no update is needed)

1
{
2
"data": "Here is your data"
3
}

Conclusion

Middleware provides a flexible and efficient way to handle version detection without modifying business logic. Through this method, we can add version control functionality to all API requests, ensuring that outdated client versions receive timely update notifications, improving user experience. This method can also be applied to other scenarios requiring processing between request and response, such as logging or authorization checks.