本文へ移動

ミドルウェア ​

ミドルウェアはリクエストをラップし、レスポンスを返します。ミドルウェア内では、リクエストを変更したり、next を 0 回以上呼び出したりできます。

ミドルウェア内では、next は fetch と同じように動作し、Request を受け取って Promise<Response> を返します。リクエストがネットワークに届く前にほかのミドルウェアを経由するかどうかは、呼び出す側からは分かりません。

ts
import { 
createClient
, type
Middleware
} from "./api.ts";
const
timing
:
Middleware
= async (
request
,
next
, {
operation
}) => {
const
start
=
performance
.
now
();
try { return await
next
(
request
);
} finally { const
name
=
operation
.
operationId
??
operation
.
path
;
console
.
log
(
name
,
performance
.
now
() -
start
);
} }; const
retry
:
Middleware
= async (
request
,
next
) => {
for (let
attempt
= 0; ;
attempt
++) {
// リクエスト本体は 1 回しか送信できないため、コピーを送信する。 const
response
= await
next
(
request
.
clone
());
if (
response
.
status
< 500 ||
attempt
=== 2) return
response
;
} }; const
api
=
createClient
({
middleware
: [
timing
,
retry
] });

ミドルウェアチェーンの動作 ​

クライアントのミドルウェア呼び出しのミドルウェアapi.GET()timingretrycachefetchネットワークnext(request)Response を返す最も外側next を再び呼べる途中で返せる本体のデコード{ ok, status, data, response }DecodeError はここで発生
api.GET() のリクエストが、createClient の [timing, retry] と呼び出しごとの [cache] を経由する順序。各ミドルウェアは 1 回だけ呼び出され、await next(request) より前のコードが行きに、後のコードが帰りに実行されます。強調されている経路は、cache が next を呼び出さずに Response を返す場合を示しています。
  • 最初のミドルウェアが最も外側になります。個別の呼び出しに渡したミドルウェア(api.GET("/pets", { middleware: [...] }))は、クライアントのミドルウェアの内側で実行されます。
  • next はリクエストを次のミドルウェアに渡し、最後のミドルウェアでは fetch に渡します。ミドルウェアが next を呼び出さずに Response を返すと、リクエストはネットワーク経由で送信されません。この仕組みはキャッシュやモックに役立ちます。
  • fetch 自体が発生させるエラーは、next から発生します。
  • レスポンス本体の読み取りとデコードは、ミドルウェアチェーンが終了した後に行われます。そのため、ミドルウェアではレスポンスを調整でき、レスポンス本体の読み取り中に発生したエラーは DecodeError も含めて next から発生しません。

操作の情報 ​

第 3 引数の { operation } には、OpenAPI ドキュメントに記述された操作の情報が格納されています。

フィールド内容
method呼び出しで使われたとおりの HTTP メソッド
path/pets/{petId} などのパステンプレート
operationIdoperationId(ドキュメントに記述があるとき)
security適用されるセキュリティ要件(ドキュメントでの記述順)

with によるクライアント単位のミドルウェア ​

api.with(...middleware) は、渡したミドルウェアを元のクライアントのミドルウェアの内側で実行する新しいクライアントを返します。なお、api 自体は変更されません。

ts
const 
tenant
= (
id
: string):
Middleware
=> (
request
,
next
) => {
request
.
headers
.
set
("x-tenant-id",
id
);
return
next
(
request
);
}; const
acme
=
api
.
with
(
tenant
("acme"));
await
acme
.
GET
("/pets");

共有しているクライアントを直接変更する代わりに with を使うと、サーバー上で扱うテナントやユーザーのトークンなど、リクエストごとの状態がほかのリクエストに漏れるのを防げます。