Skip to content

Next.js AppディレクトリでAPIルートが404エラーになる

ポイント

この問題はNext.js 13以降のApp Routerを導入したプロジェクトで頻発します。原因は主にファイル命名規則の変更関数定義方法の変化にあります。

発生する問題状況

Next.js 13で導入されたApp Router(appディレクトリ)を使用する場合、APIエンドポイントへアクセスすると404 Not Foundエラーが発生します。具体的な症状として:

  • APIルート(app/api/**)へのGET/POSTリクエストが404を返す
  • Postman, curl, クライアントアプリからアクセスしても同様のエラー
  • app/api/admin/all.jsのような実装では機能しない

例示されたAPIコード(実際は動作しない):

javascript
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export default async function all(req, res) {
    if (req.method !== 'GET') {
        return res.status(405).json({ error: 'Method not allowed' });
    }
    
    try {
        const admins = await prisma.admin.findMany();
        return res.status(200).json(admins);
    } catch (error) {
        return res.status(500).json({ error: 'Failed to get admins' });
    }
}

このエラーが発生する主要原因は以下の2点です:

  1. ファイル名が新しい命名規則に沿っていない
  2. 関数の定義方法がPages Routerの方法で実装されている

正しいAPI実装方法

解決策 1: 基本のAPIルート設定

重要

Next.js App Routerでは、APIエンドポイントファイルの名前は**route.jsまたはroute.ts** でなければなりません(Next.js 13.4以降)。

正しいファイル構造

bash
app/
└── api/
    └── admin/
        └── route.js  # ファイル名がキーポイント

正しいコード実装

javascript
import { NextResponse } from "next/server";
import { PrismaClient } from "@prisma/client";

// Prismaクライアントの効率的な初期化
const prisma = new PrismaClient();

export async function GET(request) {
  // メソッドチェックは不要(GET関数なので)
  try {
    const admins = await prisma.admin.findMany();
    return NextResponse.json(admins);
  } catch (error) {
    return NextResponse.json(
      { error: "Failed to get admins" },
      { status: 500 }
    );
  }
}

export async function POST(request) {
  try {
    // リクエストボディの取得
    const body = await request.json();
    
    // データ処理...
    const newAdmin = await prisma.admin.create({ data: body });
    
    return NextResponse.json(newAdmin, { status: 201 });
  } catch (error) {
    return NextResponse.json(
      { error: "Failed to create admin" },
      { status: 500 }
    );
  }
}

主な変更点

  • all.jsroute.js へのファイル名変更
  • export default async functionexport async function GET/POST へ変更
  • res.json()NextResponse.json() へ変更
  • メソッドごとに関数を分離(GET用、POST用など)

アクセスURLも/api/admin/allではなく/api/adminになります。

解決策 2: リクエストボディの取得方法

APIルートでPOSTデータを扱う場合、以下の方法でリクエストボディを取得します:

javascript
export async function POST(request) {
  // リクエストボディをJSONとして取得
  const data = await request.json();
  
  // データ処理...
}

本番環境での追加注意点

重要設定

Next.js 14以降で本番ビルド時にAPIが動作しない場合、next.config.jsの設定を確認してください。output: "export" が含まれているとAPIルートが無効になります。

javascript
/** @type {import('next').NextConfig} */
module.exports = {
  // 以下の設定があるとAPIが機能しない
  // output: 'export', // コメントアウトまたは削除
  
  // 推奨設定例
  eslint: {
    ignoreDuringBuilds: true,
  },
  images: {
    unoptimized: true, // 静的エクスポート時のみ必要
  },
};

この設定があると、VercelやAWS Amplifyへのデプロイ時に次のエラーが発生します:

  • 開発環境ではAPIが動作する
  • 本番ビルド後404エラーが発生する

最適化とベストプラクティス

javascript
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma"; // グローバルインスタンスの例

// 最適化: 環境変数を使用したAPI保護
const API_SECRET = process.env.API_SECRET;

export async function GET(request) {
  // 認証ヘッダーチェック
  const authHeader = request.headers.get('authorization');
  
  if (authHeader !== `Bearer ${API_SECRET}`) {
    return NextResponse.json(
      { error: "Unauthorized" },
      { status: 401 }
    );
  }

  try {
    // ページネーション実装例
    const { searchParams } = new URL(request.url);
    const page = parseInt(searchParams.get('page') || '1');
    const limit = 10;
    
    const admins = await prisma.admin.findMany({
      take: limit,
      skip: (page - 1) * limit,
    });
    
    return NextResponse.json(admins);
  } catch (error) {
    console.error("[API Error]", error);
    return NextResponse.json(
      { error: "Internal Server Error" },
      { status: 500 }
    );
  }
}

ベストプラクティス

  1. Prismaクライアント管理
    new PrismaClient()を毎回実行せず、グローバルインスタンスを使用します

    javascript
    import { PrismaClient } from '@prisma/client';
    
    const globalForPrisma = globalThis;
    const prisma = globalForPrisma.prisma || new PrismaClient();
    
    if (process.env.NODE_ENV !== 'production') 
         globalForPrisma.prisma = prisma;
    
    export default prisma;
  2. 環境変数による保護
    公開APIには必ず認証を実装

  3. エラーログの記録
    console.errorで開発時にエラー内容を確認可能に

  4. 安全なヘッダー設定

    return NextResponse.json(data, {
      headers: { 'Content-Type': 'application/json' }
    });

まとめ:実装ステップ

  1. APIファイル名をroute.js|tsに変更
  2. HTTPメソッドごとに関数を分離(GET, POSTなど)
  3. requestパラメータとNextResponseを使用
  4. 本番環境用にnext.config.jsを確認
  5. 必要なら環境変数による認証を実装
  6. Prismaクライアントはグローバルインスタンス化

これらの手順により、appディレクトリ内で正しくAPIルートが機能するようになります。特にファイル命名と関数定義の変更がポイントで、これらを適用するだけで多くの404問題は解決します。