App Router 中的 API Routes 是如何定义的?app/api/route.js 与 Pages Router 中的 pages/api/*.j

64 阅读2分钟

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 工具类
  • 更好的类型支持

优势

  • 更简洁的代码
  • 更好的类型安全
  • 更清晰的错误处理
  • 更好的开发体验