NestJSでリクエストヘッダーに基づくバージョン検出の実装——インターセプターを使用

最近のプロジェクトでは、APIにバージョン管理機能を追加して、異なるバージョンのクライアントがインターフェースの変更に対応できるようにし、必要に応じて旧バージョンのクライアントに更新を促す機能が求められました。このような要求はモバイル開発でよく見られ、通常クライアントはHTTPリクエストのヘッダーを介して現在のバージョン番号を送信し、サーバーはバージョン番号に応じて適切なレスポンスデータを返します。

私たちが直面した問題は、クライアントのバージョンが古すぎる場合、通常の返却データに更新通知を追加して、インターフェースリクエスト側でこのロジックを統一的に処理できるようにしたいという点でした。このロジックを実装するために、NestJSでインターセプターまたはミドルウェアを使用することが考えられます。

この記事では、インターセプターを使用した実装例を紹介します。ミドルウェアの実装については、こちらの記事を参照してください:《NestJSでリクエストヘッダーに基づくバージョン検出の実装——ミドルウェアを使用》

要件分析

要件は非常にシンプルです:各リクエストのヘッダーにクライアントのバージョン番号を含め、サーバーがそのバージョン番号を基に検出を行います。クライアントのバージョンがサーバーで要求される最新バージョンよりも低い場合は、レスポンスに更新通知フィールドを追加します。バージョンが要件を満たしている場合は、データを変更せずに通常のレスポンスを返します。

実装方法

NestJSでは、インターセプターを使用してリクエストとレスポンスをキャプチャできます。このインターセプターを使用して、次のようなことが可能です:

  1. リクエストヘッダーからバージョン情報を取得
  2. バージョンの更新が必要かどうかを判断。単純なバージョン番号の比較で実現。
  3. レスポンスデータを変更。更新が必要な場合は、返却データに追加のフィールドを挿入。

1. バージョン検出インターセプターの作成

インターセプターは、NestJSでリクエストとレスポンスをインターセプトするための重要なツールです。このシナリオでは、レスポンスデータがクライアントに返される前にバージョン検出と処理を行うインターセプターを作成します。

1
import {
2
CallHandler,
3
ExecutionContext,
4
Injectable,
5
NestInterceptor,
6
} from "@nestjs/common";
7
import { Observable } from "rxjs";
8
import { map } from "rxjs/operators";
9
10
@Injectable()
11
export class VersionInterceptor implements NestInterceptor {
12
private readonly latestVersion = "2.0.0"; // 最新バージョンを設定
13
14
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
15
const request = context.switchToHttp().getRequest();
16
const version = request.headers["version"]; // ヘッダーからバージョン番号を取得
17
18
return next.handle().pipe(
19
map((data) => {
20
if (version && this.needsUpdate(version)) {
21
// 更新が必要な場合は、追加フィールドを挿入
22
return {
23
...data,
24
updateAvailable: true,
25
updateUrl: "xxxx", // 更新URL
26
latestVersion: this.latestVersion,
27
message: "新しいバージョンがあります。更新してください。",
28
};
29
}
30
// 更新が不要な場合は、元のレスポンスデータを返す
31
return data;
32
}),
33
);
34
}
35
36
// バージョン比較ロジック
37
private needsUpdate(clientVersion: string): boolean {
38
return clientVersion < this.latestVersion;
39
}
40
}

このインターセプターでは、 request.headers['version'] でクライアントから送られたバージョン番号を取得し、 needsUpdate メソッドを使用してバージョンの比較を行います。クライアントのバージョンが古い場合、返却データに updateAvailable フィールドを挿入し、新しいバージョンが利用可能であることをユーザーに通知します。

2. インターセプターの適用

NestJSでは、インターセプターをローカルまたはグローバルに適用する方法が2つあります。

ローカルにインターセプターを適用

特定のルートでのみバージョン検出機能を使用したい場合は、該当するコントローラーでインターセプターを適用します。

1
import { Controller, Get, UseInterceptors } from "@nestjs/common";
2
import { VersionInterceptor } from "./version.interceptor";
3
4
@Controller("api")
5
export class AppController {
6
@Get("data")
7
@UseInterceptors(VersionInterceptor)
8
getData() {
9
return {
10
data: "Here is your data",
11
};
12
}
13
}

グローバルにインターセプターを適用

すべてのAPIエンドポイントでバージョン検出を有効にしたい場合は、 main.ts でインターセプターをグローバルに適用します。

1
import { NestFactory } from "@nestjs/core";
2
import { AppModule } from "./app.module";
3
import { VersionInterceptor } from "./version.interceptor";
4
5
async function bootstrap() {
6
const app = await NestFactory.create(AppModule);
7
8
// グローバルにインターセプターを使用
9
app.useGlobalInterceptors(new VersionInterceptor());
10
11
await app.listen(3000);
12
}
13
bootstrap();

3. インターフェースのテスト

インターセプターの設定が完了したら、 curl や Postman を使用してインターフェースをテストできます。クライアントはリクエストヘッダーを介してバージョン情報を送信し、サーバーはバージョン番号に基づいて適切なレスポンスを返します。

リクエストの例

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

レスポンスの例(更新が必要な場合)

1
{
2
"data": "Here is your data",
3
"updateAvailable": true,
4
"latestVersion": "2.0.0",
5
"message": "新しいバージョンがあります。更新してください。"
6
}

レスポンスの例(更新が不要な場合)

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

まとめ

インターセプターは、バージョンチェックを処理するための非常に効率的かつ柔軟なツールです。コントローラのロジックを変更せずに、動的に応答データを変更し、クライアントのバージョンに応じた異なるメッセージを返すことができます。この方法は、バージョン管理だけでなく、例えば、応答内容のグローバルなフォーマットや追加のメタデータを付加するなど、他の同様のシナリオにも適しています。

この方法により、APIの互換性を維持しつつ、古いバージョンのクライアントが更新通知を迅速に受け取ることができ、ユーザーエクスペリエンスが向上します。