Next.js

Route Handlers

Next.js

API Routes・middleware・認証

Route Handlers

app/api/ での REST エンドポイント定義

app/api/users/route.ts tsx
import { NextRequest, NextResponse } from 'next/server';

// GET /api/users
export async function GET(request: NextRequest) {
    const { searchParams } = request.nextUrl;
    const page  = Number(searchParams.get('page')  ?? 1);
    const limit = Number(searchParams.get('limit') ?? 10);

    const users = await db.user.findMany({
        skip: (page - 1) * limit,
        take: limit,
    });

    return NextResponse.json({ users, page, limit });
}

// POST /api/users
export async function POST(request: NextRequest) {
    const body = await request.json();

    // バリデーション
    if (!body.name || !body.email) {
        return NextResponse.json(
            { error: '名前とメールは必須です' },
            { status: 400 }
        );
    }

    const user = await db.user.create({ data: body });
    return NextResponse.json(user, { status: 201 });
}
app/api/users/[id]/route.ts tsx
import { NextRequest, NextResponse } from 'next/server';
import { notFound } from 'next/navigation';

interface Context { params: { id: string } }

// GET /api/users/:id
export async function GET(req: NextRequest, { params }: Context) {
    const user = await db.user.findUnique({ where: { id: Number(params.id) } });
    if (!user) return NextResponse.json({ error: 'Not found' }, { status: 404 });
    return NextResponse.json(user);
}

// PATCH /api/users/:id
export async function PATCH(req: NextRequest, { params }: Context) {
    const body = await req.json();
    const user = await db.user.update({
        where: { id: Number(params.id) },
        data:  body,
    });
    return NextResponse.json(user);
}

// DELETE /api/users/:id
export async function DELETE(req: NextRequest, { params }: Context) {
    await db.user.delete({ where: { id: Number(params.id) } });
    return new NextResponse(null, { status: 204 });
}

Middleware

認証・リダイレクト・ヘッダー操作

middleware.ts tsx
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
    const { pathname } = request.nextUrl;
    const token = request.cookies.get('token')?.value;

    // 認証チェック
    const isProtected = pathname.startsWith('/dashboard') ||
                        pathname.startsWith('/admin');

    if (isProtected && !token) {
        const loginUrl = new URL('/login', request.url);
        loginUrl.searchParams.set('from', pathname); // リダイレクト元を保存
        return NextResponse.redirect(loginUrl);
    }

    // ロール確認
    if (pathname.startsWith('/admin')) {
        const payload = decodeToken(token!);
        if (payload?.role !== 'admin') {
            return NextResponse.redirect(new URL('/403', request.url));
        }
    }

    // レスポンスヘッダーを追加
    const response = NextResponse.next();
    response.headers.set('X-Frame-Options', 'DENY');
    return response;
}

// Middleware を適用するパス
export const config = {
    matcher: ['/dashboard/:path*', '/admin/:path*', '/api/:path*'],
};