ASP.NET Core × Next.js の BFF パターン — 認証・キャッシュ・SSR の最適解
Architecture

ASP.NET Core × Next.js の BFF パターン — 認証・キャッシュ・SSR の最適解

公開: 更新: 約2分で読めます

BFF(Backend For Frontend)とは

フロントエンド専用のバックエンドを用意し、ブラウザと API ドメインサーバの間に薄い層を挟む構成です。Next.js の App Router と ASP.NET Core を組み合わせる場合、BFF は Next.js 側の Route Handler が担うのが最も素直です。

構成の全体像

[Browser] ─(Cookie)─> [Next.js BFF] ─(JWT)─> [ASP.NET Core API] ─> [DB/Service]

ブラウザは BFF としか話さず、認証 Cookie もドメイン内で完結します。API ドメインサーバは JWT 経由のマシン間通信に集中でき、CORS を緩める必要がありません。

ブラウザ / Next.js BFF / ASP.NET Core API / ストレージの 4 層構成と、Cookie→JWT 変換・キャッシュ層のフロー図
BFF 層が Cookie ↔ JWT の変換とキャッシュを担い、API はドメイン外の認証を意識しなくてよくなる。

認証: OIDC を BFF に寄せる

OpenID Connect のトークン交換は BFF で完結させ、ブラウザにはHttpOnly / SameSite=Lax の短命 Cookie のみを返します。

// app/api/auth/[...nextauth]/route.ts
export const { GET, POST } = NextAuth({
  providers: [EntraID({ clientId, clientSecret, tenantId })],
  session: { strategy: "jwt" },
  callbacks: {
    async jwt({ token, account }) {
      if (account) token.apiAccessToken = account.access_token;
      return token;
    },
  },
});

データ取得: Server Components からの呼び出し

Server Component 内で BFF → API を呼び、Next.js のキャッシュ層でレスポンスをメモ化します。頻繁に更新される画面では revalidate を短く、静的ページは force-cache で。

export default async function ProductsPage() {
  const res = await fetch("https://api.internal/products", {
    headers: { Authorization: `Bearer ${await getApiToken()}` },
    next: { revalidate: 60, tags: ["products"] },
  });
  const products = await res.json();
  return <ProductList items={products} />;
}

ASP.NET Core 側のポイント

  • JWT バリデーションAddJwtBearer 1 行で済ませる。発行元は BFF(IdP)。
  • CORS は閉じたままで OK。BFF 経由しかアクセスされないため。
  • Output Caching を API 側でも併用。BFF とマルチレイヤで効かせる。

運用で気をつけること

  • BFF の障害 = フロント全断なので、BFF は読み取り専用キャッシュのフォールバックを持っておく。
  • トークン有効期限はログ・メトリクスに載せ、リフレッシュ失敗時にユーザーへ明示的に再ログインを促す。
  • API が増えるほど BFF は肥大化しやすい。ドメインごとの BFF 分割を早めに検討する。

まとめ

Next.js の Server Components と ASP.NET Core の Minimal API は驚くほど相性が良く、BFF レイヤを一枚挟むだけで認証・キャッシュ・SSR の要件が素直に片付きます。既存の SPA + API 構成の刷新を検討中なら、まず BFF パターンへのリファクタから始めるのが費用対効果で優れています。