🦁

【Next.js】Middleware を宣言的に書く

2022/07/25に公開

はじめに

作成したライブラリの宣伝であり、こちらの記事の続編となります。
https://zenn.dev/kj455/articles/486ddbfe3520be

背景

↑の記事でも書いた通り、 Next.js v12.2 から Middleware が安定版としてサポートされるとともに、 Nested Middleware が廃止されました。

Nested Middleware とは

Nested Middleware というのは、 pages ディレクトリに _middleware.ts を置いておくと、表示されるページのパスと同階層以上の _middleware.ts が実行されるという便利なものでした。

たとえば、以下のようなページ階層になっていて、 /foo/bar ページを表示する場合は、 ①,② の2つの middleware が発火します。

pages
├── index.tsx
├── _middleware.ts (①)
├── foo
│   ├── index.tsx
│   ├── [id].tsx
│   ├── _middleware.ts (②)

v12.2 以降ではこれらが1つの middleware.ts に統合されることになります。
つまり、今までの「パスベースで実行する middleware を切り替える」処理を自前で実装し1ファイルに収める必要があります。愚直にやるならば大量の if 文によって構成された手続き型のスクリプトになり、可読性が著しく低下していくことは想像に難くありません。
もしページ構造が複雑で多くの middleware ファイルを pages に配置していた場合、 v12.2 へのマイグレーション作業は容易なものではないでしょう。

解決策

この移行を助ける & 複雑な middleware を簡単に構成するためのライブラリがこちらです。
https://github.com/kj455/next-compose-middleware

  • パスと実行したいmiddleware 関数の対応を記述する(宣言的)
  • middleware 関数は既存のものとほぼ同じで良い(引数を追加するだけ)
  • 複数 middleware の直列実行( middleware を関心毎に分離可能になる)
  • 早期 return のサポート
/**
 * pages             // middleware の実行順序
 *├── index.tsx      // root1 → root2
 *├── foo
 *│   ├── index.tsx  // root1 → root2 → foo
 *│   ├── [id].tsx   // root1 → root2 → foo → fooId
 */
export async function middleware(req: NextRequest) {
  return composeMiddleware(req, NextResponse.next(), {
    scripts: [root1, root2],
    '/foo': {
      scripts: [fooMiddleware],
      '/[id]': [fooIdMiddleware]
    }
  });
}

詳細はレポジトリをご覧ください。

終わりに

このライブラリによって Next v12.2~ 移行や巨大 middleware によって苦しむ人が少しでも減ることを祈っています。
気が向いた方はレポジトリへのスターよろしくお願いします⭐️

https://github.com/kj455/next-compose-middleware

Discussion