服务器导入

源码
`nuxt/server` 是供不依赖特定服务器运行时的服务器代码使用的导入接口。

Nuxt 可以搭配不同的服务器构建器使用,可以选择 Nitro 3(nitro,搭配 Nuxt 5+)、Nitro 2(nitropack,搭配 Nuxt 3/4),或直接使用 Vite。

nuxt/server 为不依赖特定服务器运行时的服务器代码提供通用工具。它是 Nuxt 的第二个运行时接口,与 nuxt/app(也可通过 #app 访问)并列,后者用于应用中也会在浏览器里运行的部分。

server/api/hello.ts
import { defineEventHandler, getQuery } from 'nuxt/server'

export default defineEventHandler((event) => {
  const { name } = getQuery<{ name?: string }>(event)
  return { message: `Hello, ${name ?? 'world'}!` }
})

这段代码可以在 @nuxt/nitro-server 和 @nuxt/vite-server 下运行,也能在 h3 或 Nitro 升级主要版本后继续运行,因为 Nuxt 会集中处理这些变化。

nuxt/server 从 Nuxt 4.6 开始提供,因此同一个文件可以供运行 nitropack v2 的 Nuxt 4.6、运行 Nitro v3 的 Nuxt 5,以及未来构建器带来的任何环境使用。

每个 Nuxt 应用都会安装 nuxt,因此 nuxt/server 无需别名或额外依赖即可解析。导入它的模块不需要将 h3 或 Nitro 声明为对等依赖。

类型和工具

导入用途
defineEventHandler定义请求处理程序。
createError、isNuxtError创建并识别 HTTP 错误。
getRequestURL、getRequestHost、getRequestProtocol获取请求的 URL、主机和协议。仅在你控制且会覆盖这些请求头的代理之后,才传入 { xForwardedHost: true, xForwardedProto: true } 来信任对应的转发标头。
getRequestHeader、getRequestHeaders读取请求标头。
getRouterParam、getRouterParams读取与请求匹配的动态路径段。
getRequestIP获取客户端 IP 地址,请求中会信任转发标头。
getQuery、getValidatedQuery读取查询字符串,可选择进行校验。
readBody、readValidatedBody读取并解析请求体,可选择进行校验。
getCookie、parseCookies、setCookie、deleteCookie读取单个或全部 Cookie,以及写入 Cookie。
setResponseStatus设置响应状态和原因短语。
handleCors应用 CORS 标头并响应预检请求。
sendRedirect重定向请求。
serverFetch在进程内获取应用的路由,路径相对于 app.baseURL,并转发 cookie 和 authorization 标头(除非设置 forwardHeaders)。在不支持进程内 fetch 的服务器构建器上会拒绝。
useSession、getSession、updateSession、clearSession读取和写入密封的会话 Cookie。
deriveSecret从 appSecret 派生用途专属的密钥。
getRouteRules、matchRouteRules获取请求匹配的路由规则,或获取其他路径匹配的规则。
useRuntimeConfig获取服务器的运行时配置。
useServerHooks获取服务器运行时钩子,包括 render:html 等渲染器钩子,以及 NuxtServerHooks 中声明的钩子。
useAppConfig获取应用配置;传入事件时,每个请求都会得到一份副本。

工具旁还导出了以下类型:RequestEvent、RequestEventContext、NuxtRequestEvent、EventHandler、AppRouteRules、ServerRoutes、CorsOptions、ValidateResult、NuxtError、NuxtErrorDetails、NuxtErrorJSON、NuxtErrorLike、Session、SessionConfig、SessionData、SessionEvent、SessionManager、SessionPassword 和 SessionUpdate。

除 useAppConfig 外,这些名称都会在服务器代码中自动导入,因此上面的导入语句是可选的。保留它可以确保文件在关闭服务器自动导入的项目中正常工作,也是模块应该采用的做法。

defineEventHandler 会保留处理程序的返回类型,而这正是路由的 $fetch 和 useFetch 调用以及对其调用 serverFetch 时的 response.json() 所需的类型。请为返回值添加类型注解,而不是为处理程序添加。

事件

RequestEvent 是 nuxt/server 工具所操作的事件,包含请求、请求 URL、待发送的响应以及请求上下文。

interface RequestEvent {
  readonly req: Request
  url: URL
  readonly res: { status?: number, statusText?: string, readonly headers: Headers }
  readonly context: RequestEventContext
}

要设置、读取、追加或移除响应标头,请直接使用 event.res.headers:它是标准的 Headers 对象。

Nitro 还会添加更多内容,例如 event.node、event.runtime 和 event.waitUntil(),但这四个核心属性适用于所有运行时,且无需使用辅助工具:

server/api/echo.post.ts
export default defineEventHandler(async (event) => {
  const { id } = await event.req.json()
  return { id, page: event.url.searchParams.get('page') }
})

NuxtRequestEvent 是同一个请求,但采用配置的构建器所提供的形式;在 @nuxt/nitro-server 下,它就是 h3 的 H3Event。

处理程序

处理程序文件必须默认导出 nuxt/server 中 defineEventHandler 的返回值。裸函数或由 h3 的 defineEventHandler 包装的函数会接收服务器运行时自己的 event。

处理程序可以返回:

  • 可序列化为 JSON 的值,将以 JSON 发送,并使用 event.res 上设置的状态和标头
  • Response,使用其自身的状态发送。event.res.headers 上设置的标头会添加到其中,并替换同名标头;set-cookie 除外,它会追加
  • ReadableStream,作为响应体流式传输

校验

readValidatedBody 和 getValidatedQuery 接受任何 Standard Schema(Zod、Valibot、ArkType 等),也接受返回已校验值的函数;返回 true 表示接受输入,返回 false 则表示拒绝输入。

server/api/users.post.ts
import { defineEventHandler, readValidatedBody } from 'nuxt/server'
import { z } from 'zod'

export default defineEventHandler(async (event) => {
  const user = await readValidatedBody(event, z.object({ name: z.string() }))
  return { created: user.name }
})

无效输入会被拒绝,并返回 400 错误,其 data.issues 会列出校验失败的内容。传入 onError 可以使用其他错误拒绝输入。

客户端 IP

getRequestIP(event) 会返回服务器运行时报告的连接地址;如果运行时没有报告地址,则返回 undefined。默认情况下不会信任任何转发标头,因为客户端可以在其中发送任意值。

如果你控制代理,可传入 { xForwardedFor: true },改为读取 X-Forwarded-For 的第一项。仅当代理会覆盖此标头时才这样做:如果代理只是追加内容,客户端发送的值仍会排在最前面。

会话

useSession 会读取请求的会话;如果没有可读取的会话,则会将新会话密封到 Cookie 中。数据使用 iron 密封,因此会存储在 Cookie 中,而不是服务器存储中;Cookie 默认使用 httpOnly、secure、sameSite: 'lax' 和 path: '/'。

server/api/visits.ts
import { defineEventHandler, useSession } from 'nuxt/server'

export default defineEventHandler(async (event) => {
  const session = await useSession<{ visits: number }>(event)
  await session.update(data => ({ visits: (data.visits ?? 0) + 1 }))
  return { visits: session.data.visits }
})

如果不传入 password,会话会使用从应用的 appSecret 派生的密钥进行密封,因此只需设置 NUXT_APP_SECRET。传入至少 32 个字符的 password,即可改用你自己的密钥进行密封。设置 maxAge(以秒为单位)可使 Cookie 和密封值过期;设置 name 可将 Cookie 名称从 nuxt-session 改为其他名称;设置 cookie 可覆盖任意 Cookie 属性。

每个请求只会解封一次会话,因此你可以在任意多个位置调用 getSession。updateSession 和 clearSession 是相同的操作,只是不需要管理器对象。

派生密钥

deriveSecret(purpose) 会从 appSecret 为特定用途解析密钥:长度为 32 字节,以十六进制编码;只要 appSecret 不变,该密钥就保持稳定;不同用途对应的密钥也各不相同。会话辅助工具会使用它;模块也应该使用它,而不是自行请求密钥。请根据密钥的所有者为用途命名空间。

import { deriveSecret } from 'nuxt/server'

const password = await deriveSecret('my-module:tokens')

如果未设置 appSecret 或其长度不足 32 个字符,则会抛出一条指出 NUXT_APP_SECRET 的 500 错误。

h3 提供了同名的会话辅助工具,可通过 import { useSession } from 'nitro/h3' 访问。它们是独立实现,不能与这些工具互换:它们默认使用 h3 而不是 nuxt-session 作为 Cookie 名称,使用自己的配置,而且其中一个工具签发的会话无法由另一个工具解封。
会话 API 是稳定的,但密封 Cookie 的格式可能会在次要版本中更改:Nuxt 计划在 iron-webcrypto 发布基于 AES-GCM 的密封方案后迁移到该方案。迁移时,现有会话会以旧格式读取,并在下次写入时重新密封为新格式,因此用户不会被登出。密封值不能与 h3 的 useSession 生成的值互换。
会话存储在 Cookie 中,因此大小受浏览器接受范围的限制。写入序列化后超过 4096 字节的会话会抛出错误;请将大型数据存储在服务器端,并在会话中通过 id 引用这些数据。

超越接口范围

nuxt/server 并非 h3 的重新导出,因此如果你需要 h3 和 Nitro 提供的其他功能,例如服务器生命周期(definePlugin、defineErrorHandler 和 Nitro 应用钩子),就需要直接导入。

这些辅助工具大多可以直接接收事件:

server/api/upload.post.ts
import { defineEventHandler } from 'nuxt/server'
import { readMultipartFormData } from 'nitro/h3'

export default defineEventHandler(async (event) => {
  const parts = await readMultipartFormData(event)
  return { received: parts?.length ?? 0 }
})

任何仅读取请求的 h3 辅助工具都可以这样使用,例如 readRawBody、readFormData、readMultipartFormData 和 assertMethod。

如果处理程序需要运行时自身的事件,例如调用 proxy、proxyRequest、fetchWithEvent 或 writeEarlyHints,或者读取 event.node、event.waitUntil() 或 event.runtime,请为该处理程序从 nitro/h3 导入 defineEventHandler:

server/api/proxy.ts
import { defineEventHandler, proxyRequest } from 'nitro/h3'

export default defineEventHandler(event => proxyRequest(event, 'https://example.com'))

Nitro 自身的 API 大多不需要传入事件:getRouteRules(method, pathname)、serverFetch()、defineCachedHandler()、useStorage()、useDatabase() 和任务。

一个文件可以同时从 nitro/* 和 nuxt/server 导入内容。这样它只能在 Nitro v3 上运行,因此模块应将其注册为 nitro3 变体。

不具备可移植性的内容

以下功能需要访问 nuxt/server 之外的内容,因为 Nuxt 无法为每种构建器都提供这些功能:

  • 存储。 useStorage() 来自 nitro/storage,其背后的驱动配置也来自那里。
  • 缓存。 defineCachedHandler() 和 defineCachedFunction() 来自 nitro/cache。如果要使用可在任何地方运行的缓存,请直接依赖缓存库。
  • 服务器插件。 definePlugin() 来自 nitro。在插件中,来自 nuxt/server 的 useServerHooks() 会注册渲染器钩子(包括 render:html)以及 NuxtServerHooks 中声明的钩子;Nitro 自身的 request、response 和 error 等钩子来自 nitro/app 中的 useNitroHooks()。
  • 任务。 defineTask() 和 runTask() 来自 nitro/task。
  • 延迟处理程序。 lazyEventHandler() 来自 nitro/h3。

仅限服务器代码

在 Vue 组件、插件或 shared/ 目录中导入 nuxt/server 会导致构建失败,错误会提示你改用 #app、#imports、$fetch 和 useFetch。这些上下文中仍能解析其类型,这也是 $fetch 能知道服务器路由返回内容的原因。

nuxt/server 也只能在服务器构建器打包的代码中工作,因为构建器提供了它背后的运行时配置、应用配置、钩子和进程内 fetch。在 Nuxt 进程中运行的代码(例如 nuxt.config.ts、模块的 setup 或传给 addDevServerHandler 的函数)不具备这些内容,因此不要在那里导入 nuxt/server:例如,useRuntimeConfig() 和 useAppConfig() 会返回空对象。请改为读取 nuxt.options,并参阅开发服务器处理程序了解如何在模块中编写处理程序。

模块

运行时代码只从 nuxt/server 导入内容的模块,可在 v4.6 及之后版本的任意服务器构建器下运行,无需检查版本,也不依赖 h3 或 Nitro:

runtime/server/api/status.ts
import { defineEventHandler, useRuntimeConfig } from 'nuxt/server'

export default defineEventHandler(() => ({
  version: useRuntimeConfig().myModule.version,
}))
打包时请将 nuxt/server 保留为外部依赖;Nuxt 构建时会将它解析为正确的服务器构建器工具。

要支持 Nuxt 4.6 之前的版本还需要多做一步,因为这些项目没有可供解析的 nuxt/server。你可以同时注册可移植文件和当前发布的文件,Nuxt 会选择应用能够运行的那个文件。

了解如何为每个服务器 API 注册一个处理程序,以及 Nitro v2 兼容层如何帮助尚未迁移的模块。
了解服务器构建器如何提供 nuxt/server 背后的实现。