关于 route-handlers
route-handlers 是面向 Next.js 开发者的 API 端点参考技能,介绍请求与响应处理、HTTP 方法、流式响应、缓存和错误处理模式。
主要功能
- 01使用 route.ts 文件定义端点
- 02处理 HTTP 方法和请求数据
- 03返回 JSON、重定向和流式响应
- 04配置标头、Cookie、CORS 和缓存
适用场景
- 01创建 CRUD API 端点
- 02读取查询参数和动态路由值
- 03流式传输文本或 LLM 响应
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davepoon/ buildwithclaude --skill route-handlers安装后在 Claude Code 中输入/route-handlers确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/crud-api.mdreferences/http-methods.mdstreaming-responses.md详细介绍
Next.js 路由处理程序
概述
路由处理程序允许你使用 Web Request 和 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/大模型流式传输
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 示例
评论
支持 Markdown