API 路由

示例

须知:如果您使用应用路由 (App Router),可以使用服务端组件 (Server Components)路由处理器 (Route Handlers) 替代 API 路由。

API 路由提供了一种使用 Next.js 构建公共 API 的解决方案。

pages/api 目录下的任何文件都会映射到 /api/* 路径,并被视为 API 端点而非页面 (page)。这些文件仅在生产服务端打包,不会增加客户端包体积。

例如,以下 API 路由会返回状态码为 200 的 JSON 响应:

import type { NextApiRequest, NextApiResponse } from 'next'

type ResponseData = {
  message: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

须知

参数

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  // ...
}

HTTP 方法

要在 API 路由中处理不同的 HTTP 方法,可以在请求处理函数中使用 req.method,如下所示:

import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    // 处理 POST 请求
  } else {
    // 处理其他 HTTP 方法
  }
}

请求辅助工具

API 路由提供内置的请求辅助工具来解析传入的请求 (req):

  • req.cookies - 包含请求发送的 cookie 的对象,默认为 {}
  • req.query - 包含查询字符串 (query string) 的对象,默认为 {}
  • req.body - 包含根据 content-type 解析的请求体内容的对象,若无请求体则为 null

自定义配置

每个 API 路由都可以导出一个 config 对象来修改默认配置,默认配置如下:

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '1mb',
    },
  },
  // 指定此函数执行的最大允许时长(秒)
  maxDuration: 5,
}

bodyParser 默认启用。如果您希望以 Stream 形式或使用 raw-body 处理请求体,可以将其设为 false

禁用自动 bodyParsing 的一个用例是验证webhook 请求的原始内容,例如来自 GitHub 的 webhook

export const config = {
  api: {
    bodyParser: false,
  },
}

bodyParser.sizeLimit 是解析后请求体的最大允许大小,支持 bytes 库的任何格式,例如:

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '500kb',
    },
  },
}

externalResolver 是一个显式标志,用于告知服务器此路由由外部解析器(如 expressconnect)处理。启用此选项会禁用未解析请求的警告。

export const config = {
  api: {
    externalResolver: true,
  },
}

responseLimit 默认启用,当 API 路由的响应体超过 4MB 时会发出警告。

如果您不在无服务器环境中使用 Next.js,并且了解不使用 CDN 或专用媒体托管服务的性能影响,可以将此限制设为 false

export const config = {
  api: {
    responseLimit: false,
  },
}

responseLimit 也可以接受字节数或 bytes 支持的任何字符串格式,例如 1000'500kb''3mb'。 此值将作为触发警告前的最大响应大小。默认值为 4MB(如上所述)。

export const config = {
  api: {
    responseLimit: '8mb',
  },
}

响应辅助工具

服务端响应对象 (Server Response object)(通常简写为 res)包含一组类似 Express.js 的辅助方法,用于提升开发体验并加速 API 端点的创建。

包含的辅助方法有:

  • res.status(code) - 设置状态码的函数。code 必须是有效的 HTTP 状态码
  • res.json(body) - 发送 JSON 响应。body 必须是可序列化对象
  • res.send(body) - 发送 HTTP 响应。body 可以是 stringobjectBuffer
  • res.redirect([status,] path) - 重定向到指定路径或 URL。status 必须是有效的 HTTP 状态码。如未指定,status 默认为 "307" "临时重定向"。
  • res.revalidate(urlPath) - 按需重新验证页面 使用 getStaticPropsurlPath 必须为 string

设置响应状态码

在向客户端发送响应时,可以设置响应的状态码。

以下示例将响应状态码设置为 200OK),并以 JSON 格式返回包含 message 属性的响应,其值为 Hello from Next.js!

import type { NextApiRequest, NextApiResponse } from 'next'

type ResponseData = {
  message: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

发送 JSON 响应

向客户端发送响应时可以发送 JSON 响应,这必须是一个可序列化对象。 在实际应用中,您可能希望根据请求端点的结果让客户端了解请求的状态。

以下示例发送状态码为 200OK)的 JSON 响应,包含异步操作的结果。代码包含在 try catch 块中以处理可能出现的错误,捕获适当的状态码和错误消息并返回给客户端:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const result = await someAsyncOperation()
    res.status(200).json({ result })
  } catch (err) {
    res.status(500).json({ error: 'failed to load data' })
  }
}

发送 HTTP 响应

发送 HTTP 响应的方式与发送 JSON 响应相同。唯一的区别是响应体可以是 stringobjectBuffer

以下示例发送状态码为 200OK)的 HTTP 响应,包含异步操作的结果:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const result = await someAsyncOperation()
    res.status(200).send({ result })
  } catch (err) {
    res.status(500).send({ error: 'failed to fetch data' })
  }
}

重定向到指定路径或 URL

以表单为例,您可能希望在客户端提交表单后将其重定向到指定路径或 URL。

以下示例在表单成功提交后将客户端重定向到 / 路径:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { name, message } = req.body

  try {
    await handleFormInputAsync({ name, message })
    res.redirect(307, '/')
  } catch (err) {
    res.status(500).send({ error: 'Failed to fetch data' })
  }
}

添加 TypeScript 类型

通过从 next 导入 NextApiRequestNextApiResponse 类型,您可以使 API 路由更具类型安全性。此外,您还可以为响应数据添加类型:

import type { NextApiRequest, NextApiResponse } from 'next'

type ResponseData = {
  message: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

须知NextApiRequest 的 body 为 any 类型,因为客户端可能包含任何负载。在使用前应验证 body 的类型/结构。

动态 API 路由

API 路由支持动态路由 (dynamic routes),并遵循与 pages/ 相同的文件命名规则。

import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const { pid } = req.query
  res.end(`Post: ${pid}`)
}

现在,请求 /api/post/abc 将返回文本:Post: abc

通配 API 路由

通过在方括号内添加三个点(...),可以扩展 API 路由以匹配所有路径。例如:

  • pages/api/post/[...slug].js 将匹配 /api/post/a,同时也匹配 /api/post/a/bapi/post/a/b/c 等路径。

须知:您可以使用 slug 以外的名称,例如:[...param]

匹配的参数将作为查询参数(示例中的 slug)传递给页面,且始终是一个数组。因此,路径 /api/post/aquery 对象如下:

{ "slug": ["a"] }

对于 /api/post/a/b 及任何其他匹配路径,新参数将被添加到数组中,如下所示:

{ "slug": ["a", "b"] }

例如:

import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const { slug } = req.query
  res.end(`Post: ${slug.join(', ')}`)
}

现在,请求 /api/post/a/b/c 将返回文本:Post: a, b, c

可选通配 API 路由

通过在双括号中包含参数([[...slug]]),可以将通配路由设为可选。

例如,pages/api/post/[[...slug]].js 将匹配 /api/post/api/post/a/api/post/a/b 等路径。

通配路由与可选通配路由的主要区别在于,可选通配路由也会匹配不带参数的路径(示例中的 /api/post)。

query 对象如下:

{ } // GET `/api/post`(空对象)
{ "slug": ["a"] } // `GET /api/post/a`(单元素数组)
{ "slug": ["a", "b"] } // `GET /api/post/a/b`(多元素数组)

注意事项

  • 预定义的 API 路由优先于动态 API 路由,动态 API 路由优先于通配 API 路由。请看以下示例:
    • pages/api/post/create.js - 将匹配 /api/post/create
    • pages/api/post/[pid].js - 将匹配 /api/post/1/api/post/abc 等,但不匹配 /api/post/create
    • pages/api/post/[...slug].js - 将匹配 /api/post/1/2/api/post/a/b/c 等,但不匹配 /api/post/create/api/post/abc

流式响应

虽然页面路由 (Pages Router) 支持通过 API 路由实现流式响应,但我们建议在 Next.js 14+ 版本中逐步采用应用路由 (App Router) 并使用路由处理器 (Route Handlers)

以下是如何通过 writeHead 从 API 路由流式传输响应:

pages/api/hello.js
import { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': "no-store",
  })
  let i = 0
  while (i < 10) {
    res.write(`data: ${i}\n\n`)
    i++
    await new Promise((resolve) => setTimeout(resolve, 1000))
  }
  res.end()
}

On this page