Skip to content

API Route の認証・バリデーションパターンの標準化を検討 #124

@konokenj

Description

@konokenj

背景

#107 に関連して、現在の認証アーキテクチャを調査しました。

現状の認証レイヤーは以下の構成です:

エントリポイント 認証方法
RSC (Server Component) proxy.ts + getSession() の二重チェック
Server Action authActionClient (next-safe-action)
API Route (/api/*) proxy.ts の matcher で除外されており、各 route 内で個別対応

Server Action は authActionClient により、認証・バリデーション・エラーハンドリングが構造的に強制されています。一方、API Route にはそのような共通レイヤーがなく、各 route で getSession() + if文を毎回書く必要があります。

このkit をベースにした3つのアプリでの実態

API Route が必要になったケースを整理すると:

ケース Server Action で代替不可の理由
ストリーミングレスポンス AIチャット (streamText) Server Action は単一レスポンスのみ
バイナリレスポンス S3画像の配信 Server Action はシリアライズ可能な値のみ
SWR/React Query でのデータフェッチ 一覧取得 + ポーリング fetch URL ベースのライブラリ
OAuth コールバック /api/auth/[slug] 外部からのリダイレクト先

RSC + Server Action だけで完結するアプリもありましたが、ストリーミングやSWRを使うアプリでは API Route が不可避でした。

課題

API Route を追加する際に以下の問題があります:

  1. 認証チェックの書き忘れリスク(proxy.ts の matcher が /api を除外しているため)
  2. バリデーションが各 route にバラバラに実装される
  3. AI コーディングエージェントが新しい route を追加する際、認証漏れやバリデーション漏れが起きやすい

相談したいこと

  1. API Route の位置づけ: このkit として「API Route は原則使わない(RSC + Server Action 推奨)」というスタンスを明示するか、それとも API Route も第一級のパターンとしてサポートするか

  2. API Route を使う場合の共通ヘルパー: Server Action の authActionClient に相当する、API Route 用の薄いヘルパーを用意するのはどうか。例えば:

// lib/api.ts
export function authedRoute<TInput, TOutput>(opts: {
  input?: z.ZodType<TInput>;
  handler: (ctx: { session: Session; input: TInput }) => Promise<TOutput>;
}) {
  return async (req: Request): Promise<NextResponse> => {
    const session = await getSession();
    // 入力バリデーション + 認証を構造的に強制
    // ...
  };
}

// 使う側
export const GET = authedRoute({
  input: z.object({ page: z.coerce.number().default(1) }),
  handler: async ({ session, input }) => {
    return prisma.item.findMany({ where: { userId: session.userId }, skip: (input.page - 1) * 10 });
  },
});
  1. proxy.ts の matcher: 現状 /api を除外していますが、API Route もカバーするように変更すべきか(その場合 /api/auth は除外が必要)

参考

  • Next.js 公式 Data Security ガイドでは「Proxy alone に頼らず、各 Server Function 内でも認証・認可を検証せよ」と明記されている
  • Next.js 16 で middleware → proxy にリネームされたが、機能は同一

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions