server

server/ 目录用于将 API 和服务器处理程序注册到你的应用。

Nuxt 会自动扫描这些目录中的文件,以注册支持热模块替换 (HMR) 的 API 和服务器处理程序。

¥Nuxt automatically scans files inside these directories to register API and server handlers with Hot Module Replacement (HMR) support.

Directory structure
-| server/
---| api/
-----| hello.ts      # /api/hello
---| routes/
-----| bonjour.ts    # /bonjour
---| middleware/
-----| log.ts        # log all requests

每个文件都应导出一个用 defineEventHandler()eventHandler()(别名)定义的默认函数。

¥Each file should export a default function defined with defineEventHandler() or eventHandler() (alias).

处理程序可以直接返回 JSON 数据、Promise 或使用 event.node.res.end() 发送响应。

¥The handler can directly return JSON data, a Promise, or use event.node.res.end() to send a response.

server/api/hello.ts
export default defineEventHandler((event) => {
  return {
    hello: 'world'
  }
})

现在,你可以在页面和组件中通用地调用此 API:

¥You can now universally call this API in your pages and components:

pages/index.vue
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>

<template>
  <pre>{{ data }}</pre>
</template>

服务器路由

¥Server Routes

~/server/api 中的文件在其路由中会自动添加 /api 前缀。

¥Files inside the ~/server/api are automatically prefixed with /api in their route.

要添加不带 /api 前缀的服务器路由,请将其放入 ~/server/routes 目录中。

¥To add server routes without /api prefix, put them into ~/server/routes directory.

示例:

¥Example:

server/routes/hello.ts
export default defineEventHandler(() => 'Hello World!')

根据上述示例,你可以通过 /hello 对象访问组件内的 group/id:

¥Given the example above, the /hello route will be accessible at http://localhost:3000/hello.

请注意,当前服务器路由不像 pages 那样支持动态路由的全部功能。

服务器中间件

¥Server Middleware

Nuxt 将自动读取 ~/server/middleware 中的任何文件,为你的项目创建服务器中间件。

¥Nuxt will automatically read in any file in the ~/server/middleware to create server middleware for your project.

中间件处理程序将在任何其他服务器路由之前在每个请求上运行,以添加或检查标头、记录请求或扩展事件的请求对象。

¥Middleware handlers will run on every request before any other server route to add or check headers, log requests, or extend the event's request object.

中间件处理程序不应返回任何内容(也不应关闭或响应请求),而应仅检查、扩展请求上下文或抛出错误。

示例:

¥Examples:

server/middleware/log.ts
export default defineEventHandler((event) => {
  console.log('New request: ' + getRequestURL(event))
})
server/middleware/auth.ts
export default defineEventHandler((event) => {
  event.context.auth = { user: 123 }
})

服务器插件

¥Server Plugins

Nuxt 将自动读取 ~/server/plugins 目录中的所有文件并将其注册为 Nitro 插件。这允许扩展 Nitro 的运行时行为并挂接到生命周期事件。

¥Nuxt will automatically read any files in the ~/server/plugins directory and register them as Nitro plugins. This allows extending Nitro's runtime behavior and hooking into lifecycle events.

示例:

¥Example:

server/plugins/nitroPlugin.ts
export default defineNitroPlugin((nitroApp) => {
  console.log('Nitro plugin', nitroApp)
})
Read more in Nitro Plugins.

服务器实用程序

¥Server Utilities

服务器路由由 unjs/h3 提供支持,它附带一组方便的辅助函数。

¥Server routes are powered by unjs/h3 which comes with a handy set of helpers.

Read more in Available H3 Request Helpers.

你可以在 ~/server/utils 目录中自行添加更多助手。

¥You can add more helpers yourself inside the ~/server/utils directory.

例如,你可以定义一个自定义处理程序实用程序,它封装原始处理程序并在返回最终响应之前执行其他操作。

¥For example, you can define a custom handler utility that wraps the original handler and performs additional operations before returning the final response.

示例:

¥Example:

server/utils/handler.ts
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 {
      // do something before the route handler
      const response = await handler(event)
      // do something after the route handler
      return { response }
    } catch (err) {
      // Error handling
      return { err }
    }
  })

服务器类型

¥Server Types

此功能从 Nuxt 3.5 及以上版本开始可用

为了提高 IDE 中 'nitro' 和 'vue' 自动导入之间的清晰度,你可以添加一个包含以下内容的 ~/server/tsconfig.json

¥To improve clarity within your IDE between the auto-imports from 'nitro' and 'vue', you can add a ~/server/tsconfig.json with the following content:

server/tsconfig.json
{
  "extends": "../.nuxt/tsconfig.server.json"
}

目前,这些值在类型检查(nuxi typecheck)时不会被考虑,但你应该在 IDE 中获得更好的类型提示。

¥Currently, these values won't be respected when type checking (nuxi typecheck), but you should get better type hints in your IDE.

Recipes

路由参数

¥Route Parameters

服务器路由可以在文件名的括号中使用动态参数,例如 /api/hello/[name].ts,并通过 event.context.params 访问。

¥Server routes can use dynamic parameters within brackets in the file name like /api/hello/[name].ts and be accessed via event.context.params.

server/api/hello/[name].ts
export default defineEventHandler((event) => {
  const name = getRouterParam(event, 'name')

  return `Hello, ${name}!`
})
或者,将 getValidatedRouterParams 与模式验证器(例如 Zod)一起使用,以确保运行时和类型安全。¥Alternatively, use getValidatedRouterParams with a schema validator such as Zod for runtime and type safety.

现在,你可以在 /api/hello/nuxt 上通用地调用此 API 并获取 Hello, nuxt!

¥You can now universally call this API on /api/hello/nuxt and get Hello, nuxt!.

匹配 HTTP 方法

¥Matching HTTP Method

处理程序可以直接返回对象/数组,以自动处理 JSON 响应。以匹配请求的 HTTP 方法

¥Handle file names can be suffixed with .get, .post, .put, .delete, ... to match request's HTTP Method.

server/api/test.get.ts
export default defineEventHandler(() => 'Test get handler')
server/api/test.post.ts
export default defineEventHandler(() => 'Test post handler')

根据上述示例,/test 路由将在 上可访问。

¥Given the example above, fetching /test with:

  • GET 方法:返回 Test get handler
  • POST 方法:返回 Test post handler
  • 任何其他方法:返回 405 错误

你还可以在目录中使用 index.[method].ts 来以不同的方式构建代码,这对于创建 API 命名空间非常有用。

¥You can also use index.[method].ts inside a directory for structuring your code differently, this is useful to create API namespaces.

export default defineEventHandler((event) => {
  // handle GET requests for the `api/foo` endpoint
})

Catch-all 路由

¥Catch-all Route

捕获所有路由有助于回退路由处理。

¥Catch-all routes are helpful for fallback route handling.

例如,创建一个名为 ~/server/api/foo/[...].ts 的文件将为所有不匹配任何路由处理程序(例如 /api/foo/bar/baz)的请求注册一个 catch-all 路由。

¥For example, creating a file named ~/server/api/foo/[...].ts will register a catch-all route for all requests that do not match any route handler, such as /api/foo/bar/baz.

server/api/foo/[...].ts
export default defineEventHandler((event) => {
  // event.context.path to get the route path: '/api/foo/bar/baz'
  // event.context.params._ to get the route segment: 'bar/baz'
  return `Default foo handler`
})

你可以使用 ~/server/api/foo/[...slug].ts 为 catch-all 路由设置名称,并通过 event.context.params.slug 访问它。

¥You can set a name for the catch-all route by using ~/server/api/foo/[...slug].ts and access it via event.context.params.slug.

server/api/foo/[...slug].ts
export default defineEventHandler((event) => {
  // event.context.params.slug to get the route segment: 'bar/baz'
  return `Default foo handler`
})

Body 处理

¥Body Handling

server/api/submit.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return { body }
})
或者,将 readValidatedBody 与模式验证器(例如 Zod)一起使用,以确保运行时和类型安全。¥Alternatively, use readValidatedBody with a schema validator such as Zod for runtime and type safety.

现在你可以使用以下方式普遍调用此 API:

¥You can now universally call this API using:

app.vue
<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 时,readBody 将抛出 405 Method Not Allowed HTTP 错误。

查询参数

¥Query Parameters

示例查询 /api/query?foo=bar&baz=qux

¥Sample query /api/query?foo=bar&baz=qux

server/api/query.get.ts
export default defineEventHandler((event) => {
  const query = getQuery(event)

  return { a: query.foo, b: query.baz }
})
或者,将 getValidatedQuery 与模式验证器(例如 Zod)一起使用,以确保运行时和类型安全。¥Alternatively, use getValidatedQuery with a schema validator such as Zod for runtime and type safety.

错误处理

¥Error Handling

如果没有抛出错误,则返回状态码 200 OK

¥If no errors are thrown, a status code of 200 OK will be returned.

任何未捕获的错误都将返回 500 Internal Server Error HTTP 错误。

¥Any uncaught errors will return a 500 Internal Server Error HTTP Error.

要返回其他错误代码,请使用 createError 抛出异常:

¥To return other error codes, throw an exception with createError:

server/api/validation/[id].ts
export default defineEventHandler((event) => {
  const id = parseInt(event.context.params.id) as number

  if (!Number.isInteger(id)) {
    throw createError({
      statusCode: 400,
      statusMessage: 'ID should be an integer',
    })
  }
  return 'All good'
})

状态码

¥Status Codes

要返回其他状态代码,请使用 setResponseStatus 实用程序。

¥To return other status codes, use the setResponseStatus utility.

例如,要返回 202 Accepted

¥For example, to return 202 Accepted

server/api/validation/[id].ts
export default defineEventHandler((event) => {
  setResponseStatus(event, 202)
})

运行时配置

¥Runtime Config

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
})
全局路由中间件可以通过两种方式定义:

请求 Cookies

¥Request Cookies

server/api/cookies.ts
export default defineEventHandler((event) => {
  const cookies = parseCookies(event)

  return { cookies }
})

转发上下文和标头

¥Forwarding Context & Headers

默认情况下,在服务器路由中发出获取请求时,传入请求的标头和请求上下文都不会被转发。你可以使用 event.$fetch 在服务器路由中发出获取请求时转发请求上下文和标头。

¥By default, neither the headers from the incoming request nor the request context are forwarded when making fetch requests in server routes. You can use event.$fetch to forward the request context and headers when making fetch requests in server routes.

server/api/forward.ts
export default defineEventHandler((event) => {
  return event.$fetch('/api/forwarded')
})
在请求中,如果标头为 not meant to be forwarded,则标头将为 not be included。例如,这些标头包括:transfer-encodingconnectionkeep-aliveupgradeexpecthostaccept

响应后等待 Promises

¥Awaiting Promises After Response

处理服务器请求时,你可能需要执行不会阻塞客户端响应的异步任务(例如,缓存和日志记录)。你可以使用 event.waitUntil 在后台等待 Promise,而不会延迟响应。

¥When handling server requests, you might need to perform asynchronous tasks that shouldn't block the response to the client (for example, caching and logging). You can use event.waitUntil to await a promise in the background without delaying the response.

event.waitUntil 方法接受一个承诺,该承诺将在处理程序终止前等待,从而确保任务完成,即使服务器在发送响应后立即终止处理程序。它与运行时提供程序集成,以利用其原生功能在发送响应后处理异步操作。

¥The event.waitUntil method accepts a promise that will be awaited before the handler terminates, ensuring the task is completed even if the server would otherwise terminate the handler right after the response is sent. This integrates with runtime providers to leverage their native capabilities for handling asynchronous operations after the response is sent.

server/api/background-task.ts
const timeConsumingBackgroundTask = async () => {
  await new Promise((resolve) => setTimeout(resolve, 1000))
};

export default eventHandler((event) => {
  // schedule a background task without blocking the response
  event.waitUntil(timeConsumingBackgroundTask())

  // immediately send the response to the client
  return 'done'
});

高级用法

¥Advanced Usage

Nitro 配置

¥Nitro Config

你可以在 nuxt.config 中使用 nitro 键直接设置 Nitro 配置

¥You can use nitro key in nuxt.config to directly set Nitro configuration.

这是一个高级选项。自定义配置可能会影响生产部署,因为当 Nitro 在 Nuxt 的 semver-minor 版本中升级时,配置界面可能会随着时间的推移而发生变化。¥This is an advanced option. Custom config can affect production deployments, as the configuration interface might change over time when Nitro is upgraded in semver-minor versions of Nuxt.
nuxt.config.ts
export default defineNuxtConfig({
  // https://nitro.unjs.io/config
  nitro: {}
})
Read more in Docs > Guide > Concepts > Server Engine.

嵌套路由

¥Nested Router

server/api/hello/[...slug].ts
import { createRouter, defineEventHandler, useBase } from 'h3'

const router = createRouter()

router.get('/test', defineEventHandler(() => 'Hello World'))

export default useBase('/api/hello', router.handler)

发送流

¥Sending Streams

这是一个实验性功能,适用于所有环境。
server/api/foo.get.ts
import fs from 'node:fs'
import { sendStream } from 'h3'

export default defineEventHandler((event) => {
  return sendStream(event, fs.createReadStream('/path/to/file'))
})

发送重定向

¥Sending Redirect

server/api/foo.get.ts
export default defineEventHandler(async (event) => {
  await sendRedirect(event, '/path/redirect/to', 302)
})

旧版处理程序或中间件

¥Legacy Handler or Middleware

server/api/legacy.ts
export default fromNodeMiddleware((req, res) => {
  res.end('Legacy handler')
})
使用 unjs/h3 可以支持旧版,但建议尽可能避免使用旧版处理程序。
server/middleware/legacy.ts
export default fromNodeMiddleware((req, res, next) => {
  console.log('Legacy middleware')
  next()
})
切勿将 next() 回调与 async 或返回 Promise 的旧式中间件结合使用。¥Never combine next() callback with a legacy middleware that is async or returns a Promise.

服务器存储

¥Server Storage

Nitro 提供跨平台的 存储层。为了配置额外的存储挂载点,你可以使用 nitro.storage服务器插件

¥Nitro provides a cross-platform storage layer. In order to configure additional storage mount points, you can use nitro.storage, or server plugins.

添加 Redis 存储的示例:

¥Example of adding a Redis storage:

使用 nitro.storage

¥Using nitro.storage:

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    storage: {
      redis: {
        driver: 'redis',
        /* redis connector options */
        port: 6379, // Redis port
        host: "127.0.0.1", // Redis host
        username: "", // needs Redis >= 6
        password: "",
        db: 0, // Defaults to 0
        tls: {} // tls/ssl
      }
    }
  }
})

然后在你的 API 处理程序中:

¥Then in your API handler:

server/api/storage/test.ts
export default defineEventHandler(async (event) => {
  // List all keys with
  const keys = await useStorage('redis').getKeys()

  // Set a key with
  await useStorage('redis').setItem('foo', 'bar')

  // Remove a key with
  await useStorage('redis').removeItem('foo')

  return {}
})

:

Read more in https://nitro.unjs.io/guide/storage.

阅读更多关于 Nitro 存储层的内容。

¥Read more about Nitro Storage Layer.

::

或者,你可以使用服务器插件和运行时配置创建存储挂载点:

¥Alternatively, you can create a storage mount point using a server plugin and runtime config:

import redisDriver from 'unstorage/drivers/redis'

export default defineNitroPlugin(() => {
  const storage = useStorage()

  // Dynamically pass in credentials from runtime configuration, or other sources
  const driver = redisDriver({
      base: 'redis',
      host: useRuntimeConfig().redis.host,
      port: useRuntimeConfig().redis.port,
      /* other redis connector options */
    })

  // Mount driver
  storage.mount('redis', driver)
})