mcpField
R
route-handlers

route-handlers

github.com/davepoon開発3.7k145 回インストール今日更新
リポジトリ

route-handlers について

route-handlersは、Next.jsでAPIエンドポイントを開発するためのリファレンススキルです。リクエストとレスポンス、HTTPメソッド、ストリーミング、キャッシュ、エラー処理を扱います。

主な機能

  1. 01route.tsファイルによるエンドポイント定義
  2. 02HTTPメソッドとリクエストデータの処理
  3. 03JSON、リダイレクト、ストリーミングレスポンスの返却
  4. 04ヘッダー、Cookie、CORS、キャッシュの設定

活用シーン

  1. 01CRUD APIエンドポイントの作成
  2. 02クエリパラメーターと動的ルート値の読み取り
  3. 03テキストやLLMレスポンスのストリーミング

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/davepoon/buildwithclaude --skill route-handlers

インストール後、Claude Code で/route-handlersを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

examples/
crud-api.md
references/
http-methods.md
streaming-responses.md

詳細

Next.js Route Handlers

概要

Route Handlers を使用すると、Web Request API と Response API を使って API エンドポイントを作成できます。app ディレクトリ内の route.ts ファイルで定義します。

基本構造

ファイルの規約

ルートハンドラーには route.ts(または route.js)を使用します。

app/
├── api/
│   ├── users/
│   │   └── route.ts      # /api/users
│   └── posts/
│       ├── route.ts      # /api/posts
│       └── [id]/
│           └── route.ts  # /api/posts/:id
HTTP メソッド

HTTP メソッド名の関数をエクスポートします。

// app/api/users/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  const users = await db.user.findMany()
  return NextResponse.json(users)
}

export async function POST(request: Request) {
  const body = await request.json()
  const user = await db.user.create({ data: body })
  return NextResponse.json(user, { status: 201 })
}

サポートされているメソッド:GET、POST、PUT、PATCH、DELETE、HEAD、OPTIONS

リクエストの処理

リクエストボディの読み取り
export async function POST(request: Request) {
  // JSON body
  const json = await request.json()

  // Form data
  const formData = await request.formData()
  const name = formData.get('name')

  // Text body
  const text = await request.text()

  return NextResponse.json({ received: true })
}
URL パラメーター

動的なルートパラメーター:

// app/api/posts/[id]/route.ts
interface RouteContext {
  params: Promise<{ id: string }>
}

export async function GET(
  request: Request,
  context: RouteContext
) {
  const { id } = await context.params
  const post = await db.post.findUnique({ where: { id } })

  if (!post) {
    return NextResponse.json(
      { error: 'Not found' },
      { status: 404 }
    )
  }

  return NextResponse.json(post)
}
クエリパラメーター
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const page = searchParams.get('page') ?? '1'
  const limit = searchParams.get('limit') ?? '10'

  const posts = await db.post.findMany({
    skip: (parseInt(page) - 1) * parseInt(limit),
    take: parseInt(limit),
  })

  return NextResponse.json(posts)
}
リクエストヘッダー
export async function GET(request: Request) {
  const authHeader = request.headers.get('authorization')

  if (!authHeader?.startsWith('Bearer ')) {
    return NextResponse.json(
      { error: 'Unauthorized' },
      { status: 401 }
    )
  }

  const token = authHeader.split(' ')[1]
  // Validate token...

  return NextResponse.json({ authenticated: true })
}

レスポンスの処理

JSON レスポンス
import { NextResponse } from 'next/server'

export async function GET() {
  return NextResponse.json(
    { message: 'Hello' },
    { status: 200 }
  )
}
ヘッダーの設定
export async function GET() {
  return NextResponse.json(
    { data: 'value' },
    {
      headers: {
        'Cache-Control': 'max-age=3600',
        'X-Custom-Header': 'custom-value',
      },
    }
  )
}
Cookie の設定
import { cookies } from 'next/headers'

export async function POST(request: Request) {
  const cookieStore = await cookies()

  // Set cookie
  cookieStore.set('session', 'abc123', {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 7, // 1 week
  })

  return NextResponse.json({ success: true })
}
リダイレクト
import { redirect } from 'next/navigation'
import { NextResponse } from 'next/server'

export async function GET() {
  // Option 1: redirect function (throws)
  redirect('/login')

  // Option 2: NextResponse.redirect
  return NextResponse.redirect(new URL('/login', request.url))
}

ストリーミングレスポンス

テキストストリーミング
export async function GET() {
  const encoder = new TextEncoder()
  const stream = new ReadableStream({
    async start(controller) {
      for (let i = 0; i < 10; i++) {
        controller.enqueue(encoder.encode(`data: ${i}\n\n`))
        await new Promise(resolve => setTimeout(resolve, 100))
      }
      controller.close()
    },
  })

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    },
  })
}
AI/LLM のストリーミング
export async function POST(request: Request) {
  const { prompt } = await request.json()

  const response = await openai.chat.completions.create({
    model: 'gpt-4',
    messages: [{ role: 'user', content: prompt }],
    stream: true,
  })

  const stream = new ReadableStream({
    async start(controller) {
      for await (const chunk of response) {
        const text = chunk.choices[0]?.delta?.content || ''
        controller.enqueue(new TextEncoder().encode(text))
      }
      controller.close()
    },
  })

  return new Response(stream, {
    headers: { 'Content-Type': 'text/plain' },
  })
}

CORS の設定

export async function OPTIONS() {
  return new Response(null, {
    status: 204,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  })
}

export async function GET() {
  return NextResponse.json(
    { data: 'value' },
    {
      headers: {
        'Access-Control-Allow-Origin': '*',
      },
    }
  )
}

キャッシュ

静的(GET のデフォルト)
// Cached by default
export async function GET() {
  const data = await fetch('https://api.example.com/data')
  return NextResponse.json(await data.json())
}
キャッシュを無効にする
export const dynamic = 'force-dynamic'

export async function GET() {
  // Always fresh
}

// Or use cookies/headers (auto opts out)
import { cookies } from 'next/headers'

export async function GET() {
  const cookieStore = await cookies()
  // Now dynamic
}

エラー処理

export async function GET(request: Request) {
  try {
    const data = await riskyOperation()
    return NextResponse.json(data)
  } catch (error) {
    console.error('API Error:', error)

    if (error instanceof ValidationError) {
      return NextResponse.json(
        { error: error.message },
        { status: 400 }
      )
    }

    return NextResponse.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    )
  }
}

リソース

詳細なパターンについては、以下を参照してください。

  • references/http-methods.md - HTTP メソッドの完全ガイド
  • references/streaming-responses.md - 高度なストリーミングパターン
  • examples/crud-api.md - CRUD API の完全な例

コメント