server
Nuxt 会自动扫描这些目录下的文件,以注册 API 和服务器处理程序,并支持热模块替换(HMR)。
-| server/
---| api/
-----| hello.ts # /api/hello
---| routes/
-----| bonjour.ts # /bonjour
---| middleware/
-----| log.ts # 记录所有请求
每个文件应默认导出一个用 defineEventHandler()
或 eventHandler()
(别名)定义的函数。
处理程序可以直接返回 JSON 数据、一个 Promise
,或使用 event.node.res.end()
发送响应。
export default defineEventHandler((event) => {
return {
hello: 'world'
}
})
你现在可以在页面和组件中普遍调用此 API:
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>
<template>
<pre>{{ data }}</pre>
</template>
服务器路由
~/server/api
内的文件,路由会自动带上 /api
前缀。
若想添加无 /api
前缀的服务器路由,可放入 ~/server/routes
目录。
示例:
export default defineEventHandler(() => 'Hello World!')
以上示例中,/hello
路由可以通过 http://localhost:3000/hello 访问。
服务器中间件
Nuxt 会自动读取 ~/server/middleware
中的任何文件,为项目创建服务器中间件。
中间件处理器会在每个请求时运行,且在其他服务器路由之前运行,用来添加或检查头部、记录请求,或扩展事件的请求对象。
示例:
export default defineEventHandler((event) => {
console.log('新请求: ' + getRequestURL(event))
})
export default defineEventHandler((event) => {
event.context.auth = { user: 123 }
})
服务器插件
Nuxt 会自动读取 ~/server/plugins
目录中的文件,并将它们注册为 Nitro 插件。这允许扩展 Nitro 的运行时行为和钩入生命周期事件。
示例:
export default defineNitroPlugin((nitroApp) => {
console.log('Nitro 插件', nitroApp)
})
服务器工具
服务器路由由 unjs/h3 驱动,附带一套方便的辅助工具。
你还可以在 ~/server/utils
目录添加更多辅助工具。
例如,可以定义一个自定义的处理器工具,包装原始处理器并在返回最终响应之前执行额外操作。
示例:
import type { EventHandler, EventHandlerRequest } from 'h3'
export const defineWrappedResponseHandler = <T extends EventHandlerRequest, D> (
handler: EventHandler<T, D>
): EventHandler<T, D> =>
defineEventHandler<T>(async event => {
try {
// 在路由处理器之前执行某些操作
const response = await handler(event)
// 在路由处理器之后执行操作
return { response }
} catch (err) {
// 错误处理
return { err }
}
})
服务器类型
为了在 IDE 中更清晰地区分来自 'nitro' 和 'vue' 的自动导入,你可以添加一个 ~/server/tsconfig.json
,内容如下:
{
"extends": "../.nuxt/tsconfig.server.json"
}
当前,在类型检查时(nuxt typecheck
)这些配置尚不会生效,但你应能在 IDE 中获得更好的类型提示。
示例
路由参数
服务器路由可以在文件名中使用方括号包裹的动态参数,例如 /api/hello/[name].ts
,并可通过 event.context.params
访问。
export default defineEventHandler((event) => {
const name = getRouterParam(event, 'name')
return `Hello, ${name}!`
})
现在你可以在 /api/hello/nuxt
调用此 API,得到 Hello, nuxt!
。
匹配 HTTP 方法
处理器文件名可后缀 .get
、.post
、.put
、.delete
等以匹配请求的 HTTP 方法。
export default defineEventHandler(() => '测试 GET 处理器')
export default defineEventHandler(() => '测试 POST 处理器')
以上示例:
- 使用 GET 方法请求
/test
返回测试 GET 处理器
- 使用 POST 方法请求
/test
返回测试 POST 处理器
- 其他方法返回 405 错误
你也可以在目录中使用 index.[method].ts
来区分代码结构,这对创建 API 命名空间非常有用。
export default defineEventHandler((event) => {
// 处理 `api/foo` 端点的 GET 请求
})
export default defineEventHandler((event) => {
// 处理 `api/foo` 端点的 POST 请求
})
export default defineEventHandler((event) => {
// 处理 `api/foo/bar` 端点的 GET 请求
})
通配路由
通配路由有助于实现回退路由处理。
例如,创建文件 ~/server/api/foo/[...].ts
将注册一个通配路由,用于匹配所有未被其他路由处理的请求,如 /api/foo/bar/baz
。
export default defineEventHandler((event) => {
// event.context.path 获取路由路径: '/api/foo/bar/baz'
// event.context.params._ 获取路由段: 'bar/baz'
return `默认的 foo 处理器`
})
你也可以为通配路由命名,例如使用 ~/server/api/foo/[...slug].ts
,并通过 event.context.params.slug
访问。
export default defineEventHandler((event) => {
// event.context.params.slug 获取路由段: 'bar/baz'
return `默认的 foo 处理器`
})
请求体处理
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return { body }
})
现在可以这样调用 API:
<script setup lang="ts">
async function submit() {
const { body } = await $fetch('/api/submit', {
method: 'post',
body: { test: 123 }
})
}
</script>
submit.post.ts
仅为匹配可接受请求体的 POST 方法。当在 GET 请求中使用 readBody
时,会抛出 405 Method Not Allowed
HTTP 错误。查询参数
例如查询 /api/query?foo=bar&baz=qux
export default defineEventHandler((event) => {
const query = getQuery(event)
return { a: query.foo, b: query.baz }
})
错误处理
如果没有抛出错误,默认返回状态码 200 OK
。
任何未捕获的错误返回 HTTP 500 内部服务器错误。
要返回其他错误码,可抛出由 createError
创建的异常:
export default defineEventHandler((event) => {
const id = parseInt(event.context.params.id) as number
if (!Number.isInteger(id)) {
throw createError({
statusCode: 400,
statusMessage: 'ID 应该是整数',
})
}
return '一切正常'
})
状态码
要返回其他状态码,请使用 setResponseStatus
工具。
例如,返回 202 Accepted
:
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
运行时配置
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event)
const repo = await $fetch('https://api.github.com/repos/nuxt/nuxt', {
headers: {
Authorization: `token ${config.githubToken}`
}
})
return repo
})
export default defineNuxtConfig({
runtimeConfig: {
githubToken: ''
}
})
NUXT_GITHUB_TOKEN='<my-super-token>'
请求 Cookies
export default defineEventHandler((event) => {
const cookies = parseCookies(event)
return { cookies }
})
转发上下文和头部
默认情况下,在服务器路由中发起 fetch 请求时,不会转发来自请求的头部和请求上下文。你可以使用 event.$fetch
来转发请求上下文和头部。
export default defineEventHandler((event) => {
return event.$fetch('/api/forwarded')
})
transfer-encoding
、connection
、keep-alive
、upgrade
、expect
、host
、accept
响应后等待 Promise
服务器处理请求时,可能需要执行异步任务且不应阻塞向客户端响应(如缓存或日志)。你可以使用 event.waitUntil
让 Promise 在后台执行,而不延迟响应。
event.waitUntil
接受一个 Promise,会在处理器结束前等待其完成,确保任务执行完成,即使服务器已发送响应后可能终止处理器。它还集成运行时提供者以支持异步操作。
const timeConsumingBackgroundTask = async () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
};
export default eventHandler((event) => {
// 调度后台任务,不阻塞响应
event.waitUntil(timeConsumingBackgroundTask())
// 立即向客户端发送响应
return '完成'
});
高级用法
Nitro 配置
你可以在 nuxt.config
中使用 nitro
配置项,直接设置 Nitro 配置。
export default defineNuxtConfig({
// https://nitro.zhcndoc.com/config
nitro: {}
})
嵌套路由
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()
router.get('/test', defineEventHandler(() => 'Hello World'))
export default useBase('/api/hello', router.handler)
发送流
import fs from 'node:fs'
import { sendStream } from 'h3'
export default defineEventHandler((event) => {
return sendStream(event, fs.createReadStream('/path/to/file'))
})
发送重定向
export default defineEventHandler(async (event) => {
await sendRedirect(event, '/path/redirect/to', 302)
})
传统处理器或中间件
export default fromNodeMiddleware((req, res) => {
res.end('传统处理器')
})
export default fromNodeMiddleware((req, res, next) => {
console.log('传统中间件')
next()
})
next()
回调与返回 Promise
或为 async
的传统中间件混用。服务器存储
Nitro 提供跨平台的 存储层。你可以通过 nitro.storage
或 服务器插件 配置额外的存储挂载点。
添加 Redis 存储示例:
使用 nitro.storage
:
export default defineNuxtConfig({
nitro: {
storage: {
redis: {
driver: 'redis',
/* redis 连接选项 */
port: 6379, // Redis 端口
host: "127.0.0.1", // Redis 主机地址
username: "", // 需 Redis >= 6
password: "",
db: 0, // 默认 0
tls: {} // tls/ssl
}
}
}
})
然后在 API 处理器里使用:
export default defineEventHandler(async (event) => {
// 列举所有键
const keys = await useStorage('redis').getKeys()
// 设置键值
await useStorage('redis').setItem('foo', 'bar')
// 删除键
await useStorage('redis').removeItem('foo')
return {}
})
或者,使用服务器插件和运行时配置创建存储挂载点:
import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
const storage = useStorage()
// 动态传入运行时配置或其他来源的凭据
const driver = redisDriver({
base: 'redis',
host: useRuntimeConfig().redis.host,
port: useRuntimeConfig().redis.port,
/* 其他 redis 连接选项 */
})
// 挂载驱动
storage.mount('redis', driver)
})
export default defineNuxtConfig({
runtimeConfig: {
redis: { // 默认值
host: '',
port: 0,
/* 其他 redis 连接选项 */
}
}
})