App Router 中的 API Routes 是如何定义的?app/api/route.js 与 Pages Router 中的 pages/api/*.js 有何不同?
App Router API Routes 定义
基本结构
在 App Router 中,API Routes 使用 route.js 文件定义,支持多种 HTTP 方法:
// app/api/users/route.js
import { NextRequest, NextResponse } from 'next/server'
// GET 请求
export async function GET(request) {
const users = await fetchUsers()
return NextResponse.json(users)
}
// POST 请求
export async function POST(request) {
const body = await request.json()
const newUser = await createUser(body)
return NextResponse.json(newUser, { status: 201 })
}
// PUT 请求
export async function PUT(request) {
const body = await request.json()
const updatedUser = await updateUser(body)
return NextResponse.json(updatedUser)
}
// DELETE 请求
export async function DELETE(request) {
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
await deleteUser(id)
return NextResponse.json({ message: 'User deleted' })
}
动态路由
// app/api/users/[id]/route.js
export async function GET(request, { params }) {
const { id } = params
const user = await fetchUser(id)
if (!user) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
return NextResponse.json(user)
}
export async function PUT(request, { params }) {
const { id } = params
const body = await request.json()
const updatedUser = await updateUser(id, body)
return NextResponse.json(updatedUser)
}
export async function DELETE(request, { params }) {
const { id } = params
await deleteUser(id)
return NextResponse.json({ message: 'User deleted' })
}
与 Pages Router 的区别
Pages Router 方式
// pages/api/users.js
export default function handler(req, res) {
const { method } = req
switch (method) {
case 'GET':
return handleGet(req, res)
case 'POST':
return handlePost(req, res)
case 'PUT':
return handlePut(req, res)
case 'DELETE':
return handleDelete(req, res)
default:
res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE'])
res.status(405).end(`Method ${method} Not Allowed`)
}
}
async function handleGet(req, res) {
const users = await fetchUsers()
res.status(200).json(users)
}
async function handlePost(req, res) {
const newUser = await createUser(req.body)
res.status(201).json(newUser)
}
App Router 方式
// app/api/users/route.js
export async function GET() {
const users = await fetchUsers()
return NextResponse.json(users)
}
export async function POST(request) {
const body = await request.json()
const newUser = await createUser(body)
return NextResponse.json(newUser, { status: 201 })
}
主要区别对比
1. 文件结构
// Pages Router
pages / api / users.js // 所有方法在一个文件
users / [id].js // 动态路由
// App Router
app /
api /
users /
route.js[id] / // 所有方法在一个文件
route.js // 动态路由
2. 方法定义
// Pages Router: 使用 switch 语句
export default function handler(req, res) {
switch (req.method) {
case 'GET':
return handleGet(req, res)
case 'POST':
return handlePost(req, res)
}
}
// App Router: 使用命名导出
export async function GET(request) {
/* ... */
}
export async function POST(request) {
/* ... */
}
3. 请求和响应对象
// Pages Router
export default function handler(req, res) {
// req: Node.js Request 对象
// res: Node.js Response 对象
res.status(200).json({ message: 'Hello' })
}
// App Router
export async function GET(request) {
// request: Web API Request 对象
return NextResponse.json({ message: 'Hello' })
}
实际应用示例
1. 用户管理 API
// app/api/users/route.js
import { NextRequest, NextResponse } from 'next/server'
import { db } from '@/lib/database'
export async function GET(request) {
try {
const { searchParams } = new URL(request.url)
const page = searchParams.get('page') || 1
const limit = searchParams.get('limit') || 10
const users = await db.users.findMany({
skip: (page - 1) * limit,
take: parseInt(limit),
})
return NextResponse.json({
users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
},
})
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch users' },
{ status: 500 }
)
}
}
export async function POST(request) {
try {
const body = await request.json()
const { name, email, role } = body
// 验证数据
if (!name || !email) {
return NextResponse.json(
{ error: 'Name and email are required' },
{ status: 400 }
)
}
const user = await db.users.create({
data: { name, email, role },
})
return NextResponse.json(user, { status: 201 })
} catch (error) {
return NextResponse.json(
{ error: 'Failed to create user' },
{ status: 500 }
)
}
}
2. 动态路由示例
// app/api/users/[id]/route.js
import { NextRequest, NextResponse } from 'next/server'
import { db } from '@/lib/database'
export async function GET(request, { params }) {
try {
const { id } = params
const user = await db.users.findUnique({
where: { id: parseInt(id) },
})
if (!user) {
return NextResponse.json({ error: 'User not found' }, { status: 404 })
}
return NextResponse.json(user)
} catch (error) {
return NextResponse.json({ error: 'Failed to fetch user' }, { status: 500 })
}
}
export async function PUT(request, { params }) {
try {
const { id } = params
const body = await request.json()
const user = await db.users.update({
where: { id: parseInt(id) },
data: body,
})
return NextResponse.json(user)
} catch (error) {
return NextResponse.json(
{ error: 'Failed to update user' },
{ status: 500 }
)
}
}
export async function DELETE(request, { params }) {
try {
const { id } = params
await db.users.delete({
where: { id: parseInt(id) },
})
return NextResponse.json({ message: 'User deleted successfully' })
} catch (error) {
return NextResponse.json(
{ error: 'Failed to delete user' },
{ status: 500 }
)
}
}
3. 文件上传 API
// app/api/upload/route.js
import { NextRequest, NextResponse } from 'next/server'
import { writeFile } from 'fs/promises'
import { join } from 'path'
export async function POST(request) {
try {
const data = await request.formData()
const file = data.get('file')
if (!file) {
return NextResponse.json({ error: 'No file uploaded' }, { status: 400 })
}
const bytes = await file.arrayBuffer()
const buffer = Buffer.from(bytes)
const filename = `${Date.now()}-${file.name}`
const path = join(process.cwd(), 'public/uploads', filename)
await writeFile(path, buffer)
return NextResponse.json({
message: 'File uploaded successfully',
filename,
url: `/uploads/${filename}`,
})
} catch (error) {
return NextResponse.json(
{ error: 'Failed to upload file' },
{ status: 500 }
)
}
}
高级特性
1. 中间件集成
// app/api/protected/route.js
import { NextRequest, NextResponse } from 'next/server'
import { verifyToken } from '@/lib/auth'
export async function GET(request) {
try {
const token = request.headers.get('authorization')?.replace('Bearer ', '')
if (!token) {
return NextResponse.json({ error: 'No token provided' }, { status: 401 })
}
const user = await verifyToken(token)
return NextResponse.json({
message: 'Protected data',
user: user.id,
})
} catch (error) {
return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
}
}
2. 流式响应
// app/api/stream/route.js
import { NextRequest, NextResponse } from 'next/server'
export async function GET() {
const encoder = new TextEncoder()
const stream = new ReadableStream({
start(controller) {
const sendData = (data) => {
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
}
// 模拟流式数据
let count = 0
const interval = setInterval(() => {
count++
sendData({ count, timestamp: Date.now() })
if (count >= 10) {
clearInterval(interval)
controller.close()
}
}, 1000)
},
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
},
})
}
最佳实践
1. 错误处理
// app/api/error-handling/route.js
export async function GET() {
try {
// 业务逻辑
const data = await fetchData()
return NextResponse.json(data)
} catch (error) {
console.error('API Error:', error)
if (error.status) {
return NextResponse.json(
{ error: error.message },
{ status: error.status }
)
}
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
2. 数据验证
// app/api/validate/route.js
import { z } from 'zod'
const userSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(18).max(100),
})
export async function POST(request) {
try {
const body = await request.json()
const validatedData = userSchema.parse(body)
// 处理验证后的数据
const user = await createUser(validatedData)
return NextResponse.json(user, { status: 201 })
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: 'Validation failed', details: error.errors },
{ status: 400 }
)
}
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
总结
App Router API Routes 的主要特点:
文件结构:
- 使用
route.js文件 - 支持动态路由
[id]/route.js - 所有 HTTP 方法在同一文件中
方法定义:
- 使用命名导出 (
GET,POST,PUT,DELETE) - 无需 switch 语句
- 更清晰的代码结构
请求响应:
- 使用 Web API Request 对象
- 使用 NextResponse 工具类
- 更好的类型支持
优势:
- 更简洁的代码
- 更好的类型安全
- 更清晰的错误处理
- 更好的开发体验