Back

React Router のミドルウェア入門ガイド

React Router のミドルウェア入門ガイド

はじめに

保護されたルートごとに認証をチェックしたり、アプリケーション全体でリクエストをログに記録したり、React Router で親ルートと子ルート間でデータを共有したりする必要がある場合、同じコードを何度も書いた経験があるのではないでしょうか。React Router のミドルウェアは、ローダーやアクションに到達する前にリクエストをインターセプトして処理することで、このような繰り返しの問題を解決します。

このガイドでは、v8_middleware フラグの有効化から新しいミドルウェア API を使用した最初の認証ミドルウェアの作成まで、React Router バージョン 7.9 以降でミドルウェアを実装するために必要なすべてを網羅しています。

重要なポイント

  • React Router 7 では、認証やログ記録などの横断的関心事を処理するためのミドルウェアが導入されました
  • ミドルウェアはネストされたチェーン内で順次実行され、ルート間での効率的なデータ共有を可能にします
  • 新しいコンテキスト API は、名前の競合なしに型安全なデータ共有を提供します
  • サーバーとクライアントの両方のミドルウェアがサポートされており、それぞれ異なる実行動作を持ちます

React Router のミドルウェアとは?

React Router 7 のミドルウェアは、ルートハンドラーの実行前後にコードを実行する機能です。各ミドルウェアがリクエストを検査し、コンテキストにデータを追加したり、リダイレクトでリクエストをショートサーキットしたりできる処理パイプラインと考えてください。

React Router 6 のローダーの並列実行とは異なり、ミドルウェアはネストされたチェーン内で順次実行されます。親ミドルウェアは下りで子ミドルウェアより先に実行され、レスポンスを生成した後は逆順で上りに実行されます。

親ミドルウェア → 子ミドルウェア → ルートハンドラー → 子ミドルウェア → 親ミドルウェア

この順次実行により、親ルートから認証されたユーザーデータをすべての子ルートに、冗長なデータベース呼び出しなしで共有するなど、以前は不可能だったパターンが可能になります。

プロジェクトでミドルウェアを有効にする

ミドルウェアの使用を開始するには、まず React Router 7.9.0 以降(またはミドルウェアフラグが有効な 7.3.0 以降)を実行していることを確認してください。次に、設定ファイルで機能フラグを有効にします:

// react-router.config.ts
import type { Config } from "@react-router/dev/config";

export default {
  future: {
    v8_middleware: true,
  },
} satisfies Config;

このフラグは、安定したミドルウェアサポートと新しいコンテキスト API を有効にします。ローダーとアクション内の context パラメーターは、型安全な API を通じてミドルウェアが設定したデータへのアクセスを提供するようになりました。

コンテキスト API を理解する

React Router 7 の新しいコンテキスト API は、ミドルウェアとルートハンドラー間でデータを共有するための型安全な Map ライクなインターフェースを提供します。コンテキストオブジェクトに直接プロパティをアタッチする代わりに、型付けされたコンテキストキーを使用して context.set()context.get() を使用するようになりました:

import { createContext } from "react-router";

// 型付けされたコンテキストキーを作成
const userContext = createContext<User>();

// ミドルウェア内で
context.set(userContext, user);

// ローダー/アクション内で
const user = context.get(userContext); // 型安全な User オブジェクト

このアプローチにより、TypeScript の型アサーションが不要になり、異なるミドルウェア間での名前の競合を防ぎます。

最初のミドルウェアを作成する

ルートを保護し、ユーザーデータを共有する認証ミドルウェアを構築してみましょう:

// app/middleware/auth.ts
import { redirect, createContext } from "react-router";
import type { Route } from "./+types/root";

export const userContext = createContext<User | null>();

export const authMiddleware: Route.Middleware = async ({ 
  request, 
  context,
  next
}) => {
  const user = await getUserFromSession(request);
  
  if (!user) {
    throw redirect("/login");
  }
  
  context.set(userContext, user);
  return next();
};

このミドルウェアを保護されたルートに適用します:

// app/routes/dashboard.tsx
import { authMiddleware, userContext } from "~/middleware/auth";

export  const  middleware: Route.MiddlewareFunction[] = [authMiddleware];

export async function loader({ context }: Route.LoaderArgs) {
  const user = context.get(userContext); // 存在が保証される
  const profile = await getProfile(user.id);
  return { profile };
}

ミドルウェアはローダーの前に実行され、認証されたアクセスを保証し、追加のデータベースクエリなしでユーザーオブジェクトを提供します。

サーバーミドルウェア vs クライアントミドルウェア

React Router は、わずかに異なる動作を持つサーバーとクライアントの両方のミドルウェアをサポートしています:

サーバーミドルウェアはサーバー上で実行され、Response を返す必要があります:

export const middleware: Route.MiddlewareFunction[] = [
  async ({ request }, next) => {
    console.log(`Server: ${request.method} ${request.url}`);
    const response = await next();
    response.headers.set("X-Custom-Header", "value");
    return response; // 必須
  };
];

クライアントミドルウェアは、クライアント側のナビゲーション中にブラウザで実行されます:

export const middleware: Route.ClientMiddlewareFunction[] = [
  async ({ next }) => {
    const start = performance.now();
    await next();
    console.log(`Navigation took ${performance.now() - start}ms`);
  },
];

一般的なミドルウェアパターン

ログ記録ミドルウェア

export const loggingMiddleware: Route.Middleware = async ({ request, next }) => {
  const requestId = crypto.randomUUID();
  console.log(`[${requestId}] ${request.method} ${request.url}`);
  
  const response = await next();
  
  console.log(`[${requestId}] Response ${response.status}`);
  return response;
};

複数のミドルウェアを組み合わせる

複数のミドルウェア関数を連鎖させることができます:

// app/routes/admin.tsx
export const middleware = [
  loggingMiddleware,
  authMiddleware,
  adminRoleMiddleware
];

各ミドルウェアは順番に実行され、リダイレクトやエラーレスポンスをスローすることで、どのミドルウェアもチェーンをショートサーキットできます。

まとめ

React Router のミドルウェアは、アプリケーションで横断的関心事を処理する方法を変革します。v8_middleware フラグを有効にし、新しいコンテキスト API を採用することで、コードの重複を排除し、ルート間でデータを効率的に共有し、認証、ログ記録、その他のパターンをクリーンで再利用可能な方法で実装できます。

認証やログ記録などのシンプルなミドルウェアから始めて、アプリケーションの成長に応じて高度なパターンを探求してください。順次実行モデルと型安全なコンテキスト API により、ミドルウェアは React Router ツールキットへの強力な追加機能となります。

よくある質問

いいえ、ミドルウェアは React Router 7.3 でフラグの背後に導入された新機能で、7.9 で `future.v8_middleware` を通じて安定化されました。それ以前のバージョンでは、ラッパーコンポーネントやカスタムローダーロジックを通じて同様の機能を実装する必要があります。

ミドルウェアは冗長な操作を削減することでパフォーマンスを向上させることができます。ミドルウェアは順次実行され、コンテキストを通じてデータを共有できるため、並列ローダーで発生する可能性のある重複したデータベースクエリや API 呼び出しを回避できます。

ミドルウェアがエラーをスローすると、React Router は他のルートエラーと同様に処理します。エラーは最も近いエラーバウンダリまでバブルアップし、チェーン内の後続のミドルウェアは実行されません。

ミドルウェアはリクエストオブジェクトを直接変更することはできませんが、ローダーやアクションがアクセスできるコンテキストにデータを追加することはできます。リクエストの変更が必要な場合は、新しい Request オブジェクトを作成して次のミドルウェアに渡してください。

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay