---
title: "server"
description: "server/ 目录用于向您的应用程序注册 API 和服务器处理程序。"
canonical_url: "https://nuxt.zhcndoc.com/docs/4.x/directory-structure/server"
---
# server

> server/ 目录用于向您的应用程序注册 API 和服务器处理程序。

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

```bash [Directory Structure]
-| server/
---| api/
-----| hello.ts      # /api/hello
---| routes/
-----| bonjour.ts    # /bonjour
---| middleware/
-----| log.ts        # 记录所有请求
```

每个文件都应该使用 `defineEventHandler()` 或 `eventHandler()`（别名）导出一个默认函数。

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

::important
不要在您的服务器路由或工具中导入 Vue 应用代码（组件、composables 或其他仅限应用的工具），也不要在您的应用中导入仅限服务器的代码。
::

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/directory-structure/shared#why-you-cannot-mix-vue-and-nitro-code" title="为什么不能混用 Vue 和 Nitro 代码"}
::

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

现在您可以在页面和组件中全局调用此 API：

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

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

## 服务器路由

位于 `~/server/api` 内的文件在其路由中会自动加上 `/api` 前缀。

::video-accordion{title="观看来自 Vue School 的关于 API 路由的视频" videoId="761468863" platform="vimeo"}
::

若想添加不带 `/api` 前缀的服务器路由，请将它们放入 `~/server/routes` 目录。

**示例：**

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

根据上面的示例，`/hello` 路由将在 <http://localhost:3000/hello> 可访问。

::note
请注意，目前服务器路由尚不支持像 [页面](https://nuxt.zhcndoc.com/docs/4.x/directory-structure/app/pages#dynamic-routes) 那样的完整动态路由功能。
::

## 服务器中间件

Nuxt 会自动读取 `~/server/middleware` 中的任何文件，为您的项目创建服务器中间件。

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

::note
中间件处理程序不应返回任何内容（也不应关闭或响应请求），仅应检查或扩展请求上下文或抛出错误。
::

**示例：**

```ts [server/middleware/log.ts]
export default defineEventHandler((event) => {
  console.log('新请求: ' + getRequestURL(event))
})
```

```ts [server/middleware/auth.ts]
export default defineEventHandler((event) => {
  event.context.auth = { user: 123 }
})
```

## 服务器插件

Nuxt 会自动读取 `~/server/plugins` 目录中的任何文件并将它们注册为 Nitro 插件。这允许扩展 Nitro 的运行时行为并钩入生命周期事件。

**示例：**

```ts [server/plugins/nitroPlugin.ts]
export default defineNitroPlugin((nitroApp) => {
  console.log('Nitro 插件', nitroApp)
})
```

::read-more{to="https://nitro.zhcndoc.com/guide/plugins" title="Nitro 插件" target="_blank"}
::

## 服务器实用工具

服务器路由由 [h3js/h3](https://github.com/unjs/h3) 提供支持，它带有一组实用的辅助函数。

::read-more{to="https://www.jsdocs.io/package/h3#package-index-functions" title="可用的 H3 请求辅助函数" target="_blank"}
::

您可以在 `~/server/utils` 目录中添加更多的辅助函数。

例如，您可以定义一个自定义的处理程序实用函数，它包装原始处理程序并在返回最终响应之前执行额外操作。

**示例：**

```ts [server/utils/handler.ts]
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 }
    }
  })
```

```ts [server/api/hello.get.ts]
export default defineWrappedResponseHandler(event => 'hello world')
```

## 服务器别名 :badge[v4.3]{color="info" size="xs" .align-middle}

您可以使用 `#server` 别名从 `server/` 目录的任意位置导入文件，而不受导入文件所在位置限制。

```ts [server/api/users/[id\\]/profile.ts]
// 代替如下相对路径：
// import { formatUser } from '../../../utils/formatUser'

// 使用 #server 别名：
import { formatUser } from '#server/utils/formatUser'
```

此别名确保服务器代码中的导入保持一致，尤其适用于嵌套较深的路由处理程序。

::note
`#server` 别名只能在 `server/` 目录内使用。在客户端代码中从 `#server` 导入将导致错误。
::

## 服务器类型

自动导入和其他类型对于 `server/` 目录来说是不同的，因为它在与 `app/` 目录不同的上下文中运行。

默认情况下，Nuxt 4 会生成一个 [`tsconfig.json`](https://nuxt.zhcndoc.com/docs/4.x/directory-structure/tsconfig)，其中包含覆盖 `server/` 文件夹的项目引用，从而确保类型准确。

放置在 `~~/server/types/` 中的类型仅在服务器上下文中自动导入，因此您可以在服务器路由、中间件、插件和实用工具中引用它们，而无需导入。Vue 应用中也需要使用的类型应放在 [`shared/types/`](https://nuxt.zhcndoc.com/docs/4.x/directory-structure/shared) 中。

```ts twoslash [server/types/todo.ts]
export interface Todo {
  id: string
  title: string
  completed: boolean
}
```

```ts [server/api/todos.get.ts]
export default defineEventHandler((): Todo[] => {
  return []
})
```

只会扫描直接位于 `server/types/` 中的文件；嵌套子目录中的文件不会自动导入，这与 [`shared/types/`](https://nuxt.zhcndoc.com/docs/4.x/directory-structure/shared#how-files-are-scanned) 的工作方式一致。

## 食谱

### 路由参数

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

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

  return `Hello, ${name}!`
})
```

::tip{to="https://h3.dev/examples/validate-data#validate-params"}
或者，使用 `getValidatedRouterParams` 配合 Zod 或 Valibot 等 schema 验证器，以实现运行时和类型安全。
::

现在您可以在 `/api/hello/nuxt` 调用此 API 并获得 `Hello, nuxt!`。

### 匹配 HTTP 方法

处理文件名可以以 `.get`、`.post`、`.put`、`.delete` 等后缀来匹配请求的 [HTTP 方法](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods)。

```ts [server/api/test.get.ts]
export default defineEventHandler(() => '测试 GET 处理程序')
```

```ts [server/api/test.post.ts]
export default defineEventHandler(() => '测试 POST 处理程序')
```

根据上面的示例，请求 `/test` 时：

- **GET** 方法：返回 `测试 GET 处理程序`
- **POST** 方法：返回 `测试 POST 处理程序`
- 任何其他方法：返回 405 错误

您也可以在目录中使用 `index.[method].ts` 来以不同方式组织代码，这在创建 API 命名空间时很有用。

::code-group
```ts [server/api/foo/index.get.ts]
export default defineEventHandler((event) => {
  // 处理 `api/foo` 端点的 GET 请求
})
```

```ts [server/api/foo/index.post.ts]
export default defineEventHandler((event) => {
  // 处理 `api/foo` 端点的 POST 请求
})
```

```ts [server/api/foo/bar.get.ts]
export default defineEventHandler((event) => {
  // 处理 `api/foo/bar` 端点的 GET 请求
})
```
::

### 全捕获路由

全捕获路由有助于进行兜底路由处理。

例如，创建名为 `~/server/api/foo/[...].ts` 的文件将为所有未匹配到任何路由处理程序的请求注册一个全捕获路由，例如 `/api/foo/bar/baz`。

```ts [server/api/foo/[...\\].ts]
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` 访问它。

```ts [server/api/foo/[...slug\\].ts]
export default defineEventHandler((event) => {
  // 可用 event.context.params.slug 获取路由参数，比如 'bar/baz'
  return `默认 foo 处理程序`
})
```

### 请求体处理

```ts [server/api/submit.post.ts]
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return { body }
})
```

::tip{to="https://unjs.io/blog/2023-08-15-h3-towards-the-edge-of-the-web/#runtime-type-safe-request-utils"}
或者，使用 `readValidatedBody` 配合 Zod 或 Valibot 等 schema 验证器，以实现运行时和类型安全。
::

现在您可以使用以下方式全局调用此 API：

```vue [app/app.vue]
<script setup lang="ts">
async function submit () {
  const { body } = await $fetch('/api/submit', {
    method: 'post',
    body: { test: 123 },
  })
}
</script>
```

::note
我们在文件名中使用 `submit.post.ts` 仅是为了与可接受请求体的 `POST` 方法匹配。当在 GET 请求中使用 `readBody` 时，`readBody` 将抛出 `405 Method Not Allowed` HTTP 错误。
::

### 查询参数

示例请求 `/api/query?foo=bar&baz=qux`

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

  return { a: query.foo, b: query.baz }
})
```

::tip{to="https://unjs.io/blog/2023-08-15-h3-towards-the-edge-of-the-web#runtime-type-safe-request-utils"}
或者，使用 `getValidatedQuery` 配合 Zod 或 Valibot 等 schema 验证器，以实现运行时和类型安全。
::

### 错误处理

如果没有抛出错误，将返回 `200 OK` 状态码。

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

要返回其他错误代码，请使用 [`createError`](https://nuxt.zhcndoc.com/docs/4.x/api/utils/create-error) 抛出异常：

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

  if (!Number.isInteger(id)) {
    throw createError({
      statusCode: 400,
      statusMessage: 'ID 应该是一个整数',
    })
  }
  return '一切正常'
})
```

### 状态码

要返回其他状态码，请使用 [`setResponseStatus`](https://nuxt.zhcndoc.com/docs/4.x/api/utils/set-response-status) 实用工具。

例如，要返回 `202 Accepted`

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

### 运行时配置

::code-group
```ts [server/api/foo.ts]
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
})
```

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  runtimeConfig: {
    githubToken: '',
  },
})
```

```ini [.env]
NUXT_GITHUB_TOKEN='<我的超级令牌>'
```
::

::note
将 `event` 作为参数传递给 `useRuntimeConfig` 是可选的，但建议传递它，以便在服务器路由运行时通过[环境变量](https://nuxt.zhcndoc.com/docs/4.x/guide/going-further/runtime-config#environment-variables)覆盖运行时配置。
::

### 请求 Cookie

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

  return { cookies }
})
```

### 转发上下文和请求头

默认情况下，在服务器路由中进行 fetch 请求时，传入请求的头和请求上下文都不会被转发。您可以使用 `event.$fetch` 在服务器路由中进行 fetch 请求时转发请求上下文和头。

```ts [server/api/forward.ts]
export default defineEventHandler((event) => {
  return event.$fetch('/api/forwarded')
})
```

::note
那些“不应被转发”的头将不会包含在请求中。例如这些头包括：
`transfer-encoding`、`connection`、`keep-alive`、`upgrade`、`expect`、`host`、`accept`
::

### 响应后等待 Promise

在处理服务器请求时，您可能需要执行一些异步任务且不应阻塞对客户端的响应（例如缓存和日志）。您可以使用 `event.waitUntil` 在后台等待一个 Promise，而不延迟响应。

`event.waitUntil` 方法接受一个 Promise，该 Promise 会在处理程序终止之前被等待，确保任务在响应发送后仍能完成。它会与运行时提供者集成，以利用其在响应发送后处理异步操作的原生能力。

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

export default eventHandler((event) => {
  // 安排一个后台任务，不阻塞响应
  event.waitUntil(timeConsumingBackgroundTask())

  // 立即向客户端发送响应
  return '完成'
})
```

## 高级用法

### Nitro 配置

您可以在 `nuxt.config` 中使用 `nitro` 键直接设置 [Nitro 配置](https://nitro.zhcndoc.com/config)。

::warning
这是一个高级选项。自定义配置可能会影响生产部署，因为当 Nitro 在 Nuxt 的次要语义版本中升级时，配置接口可能会发生变化。
::

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  // https://nitro.zhcndoc.com/config
  nitro: {},
})
```

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/guide/concepts/server-engine"}
::

### 嵌套路由

```ts [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)
```

### 发送流

::tip
这是一个实验性功能，可在所有环境中使用。
::

你可以直接从事件处理程序中返回流。

```ts [server/api/foo.get.ts]
import fs from 'node:fs'

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

### 发送重定向

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

### 旧版处理程序或中间件

```ts [server/api/legacy.ts]
export default fromNodeMiddleware((req, res) => {
  res.end('旧版处理程序')
})
```

::important
可以使用 [h3js/h3](https://github.com/unjs/h3) 实现对旧版的支持，但建议尽可能避免使用旧版处理程序。
::

```ts [server/middleware/legacy.ts]
export default fromNodeMiddleware((req, res, next) => {
  console.log('旧版中间件')
  next()
})
```

::warning
绝不要将 `next()` 回调与一个是 `async` 或返回 `Promise` 的旧版中间件结合使用。
::

### 服务器存储

Nitro 提供了跨平台的[存储层](https://nitro.build/guide/storage)。要配置其他存储挂载点，您可以使用 `nitro.storage` 或[服务器插件](https://nuxt.zhcndoc.com/docs/4.x/directory-structure/server#server-plugins)。

**添加 Redis 存储的示例：**

使用 `nitro.storage`：

```ts [nuxt.config.ts]
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 处理程序中：

```ts [server/api/storage/test.ts]
export default defineEventHandler(async (event) => {
  // 获取所有键
  const keys = await useStorage('redis').getKeys()

  // 设置键值
  await useStorage('redis').setItem('foo', 'bar')

  // 删除键
  await useStorage('redis').removeItem('foo')

  return {}
})
```

::read-more{to="https://nitro.zhcndoc.com/guide/storage" target="_blank"}
阅读有关 Nitro 存储层的更多内容。
::

或者，您可以使用服务器插件和运行时配置创建一个存储挂载点：

::code-group
```ts [server/plugins/storage.ts]
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)
})
```

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  runtimeConfig: {
    redis: { // 默认值
      host: '',
      port: 0,
      /* 其他 redis 连接器选项 */
    },
  },
})
```
::


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
