「エージェントには入っている版の公式ドキュメントを読ませたから、古い知識で書かれる心配はない」。Next.js 16 の middleware から proxy への移行で、この前提がそのまま崩れる箇所がある。
公式ドキュメントは、proxy の matcher を単体テストする関数として unstable_doesProxyMatch を載せている。Web 版にも、node_modules/next/dist/docs/ に同梱された版にも同じ名前がある。この記事のために Next.js 16.4.2 でそのとおり書いてみると、次のように落ちた。
TypeError: unstable_doesProxyMatch is not a function
error TS2305: Module '"next/experimental/testing/server"' has no exported member 'unstable_doesProxyMatch'.
1 行目が vitest、2 行目が tsc の出力だ。原因はどちらも同じ 1 行の import で、tsc は import の行、vitest は呼び出した行で止まる。16.5.0-canary.16 の型定義にもこの関数は無い。ドキュメントどおりなのに落ちる。真面目に読むエージェントほど、存在しない関数を import する(勉強熱心ゆえの事故)。
ドキュメントは proxy、コードには middleware の名前が残っている
なぜドキュメントにある関数が無いのか? ドキュメントの上では名前が proxy に揃っているが、実際は周りの名前が古いまま残っている。16.4.2 で確かめた範囲では次のとおりだ。
| 場所 | ドキュメントの名前 | 16.4.2 の実際の名前 |
|---|---|---|
| matcher を確かめるテスト関数 | unstable_doesProxyMatch | unstable_doesMiddlewareMatch |
NextResponse.next() が付けるヘッダ | 記載なし(内部名) | x-middleware-next |
ドキュメントに合わせて middleware を一括で proxy に置き換えると、テスト関数の import が壊れる。内部名の x-middleware-next も一括置換に巻き込まれ、通過を確かめるテストが落ちる。16.4.2 で通る形は次のとおりで、静的ファイルに proxy が走らないことを確かめる例だ。
import { unstable_doesMiddlewareMatch } from 'next/experimental/testing/server';
import { config } from './proxy';
expect(unstable_doesMiddlewareMatch({ config, url: '/_next/static/chunk.js' })).toBe(false);
同梱ドキュメントは学習データより新しいが、コードと一致しているとは限らない。名前が変わったばかりの API ほど、ドキュメントだけでは確かめ先として足りない。最後に頼れるのは、入っている版の型定義だ。
改名で静かに変わること
公式の移行ガイドによれば proxy は Node.js 固定で、Edge を使い続けたいなら middleware のままにする。一方、runtime 指定なしの middleware は今も Edge で動く(16.4.2 で build して確認)。同梱の middleware のリファレンスも「only the file and export names have changed」と書く。名前だけの変更のはずが、指定なしなら実行環境も変わる。移行コマンドが runtime を消すことも、ガイドには書かれていない。
| 16.4.2 での状態 | 結果 |
|---|---|
runtime: 'experimental-edge' 付きの middleware に移行コマンドを流す | build は通る。runtime の行は無言で消えている |
proxy.ts に runtime: 'edge' を書く | build エラー: Proxy does not support Edge runtime |
差分には、runtime を書いていれば 1 行の削除が残るだけで、指定なしなら何も残らない(レビューで指を差せる行がほとんど無い)。Cookie 認証を AI に書かせた記事の社内ダッシュボードも runtime 指定なしの Edge で動いていた。署名の計算は node:crypto を使わず Web Crypto(crypto.subtle)で書いていた。移行の変更で足した認証のテストは、vitest の Node.js 環境で通っている。
エージェントへの指示
肝は最初の項目で、ドキュメントの名前を信じる前に型定義で実在を確かめさせる。
middleware.ts を Next.js 16 の proxy.ts に移行して。commit はまだしないで。
- ドキュメントにある関数名・型名・設定名は、使う前に node_modules/next の型定義(.d.ts)で実在を確かめる。
見つからなければドキュメントの名前で書かず、見つかった名前と一緒に報告する
- x-middleware-next などのヘッダ名は置換しない(型定義には無く、node_modules/next/dist の実装にだけある)
- 移行前に今の runtime を報告する(指定なしなら Edge)。Edge でしか成り立たない処理があれば止まって相談する
- 認証・リダイレクトのテストを先に middleware に対して通し、移行後は期待値を変えずに通す。変えてよいのは import 先と関数名だけ
- npx @next/codemod@canary middleware-to-proxy . で改名し、消えた行を差分から報告する
返ってきた報告で人が見るのは、「ドキュメントと違う名前だった」という行と、消えた行だ。どちらも差分を眺めるだけでは気づきにくい。
まとめ
- 公式ドキュメントの
unstable_doesProxyMatchは、16.4.2 と 16.5.0-canary.16 には無い。実在するのはunstable_doesMiddlewareMatch - コードには middleware の名前が残っていて、内部名の
x-middleware-nextも同じ。一括置換は壊す - エージェントには、ドキュメントの名前を型定義で確かめさせ、違ったら報告させる
ドキュメントを読ませるのは出発点で、ゴールではない(読んだ本人が一番信じ込む)。フレームワークの更新をエージェントに任せる手順まで外から整えたい場合は、AI開発支援で相談を受け付けている。



