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

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

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

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

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

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

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

你现在可以在页面和组件中普遍调用此 API：

```vue [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
请注意，目前服务器路由不支持与 [pages](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/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/h3js/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')
```

## Server 别名

你可以使用 `#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` 导入会导致错误。
::

## 服务器类型

::tip
此功能从 Nuxt >= 3.5 起可用
::

为了在 IDE 中更清晰地区分来自 'nitro' 和 'vue' 的自动导入，你可以添加一个 `~/server/tsconfig.json`，内容如下：

```json [server/tsconfig.json]
{
  "extends": "../.nuxt/tsconfig.server.json"
}
```

目前，类型检查时不会采用这些值（[`nuxt typecheck`](https://nuxt.zhcndoc.com/docs/3.x/api/commands/typecheck)），但你应该能在 IDE 中获得更好的类型提示。

## 示例

### 路由参数

服务器路由可以在文件名中使用方括号包裹的动态参数，例如 `/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.zhcndoc.com/examples/validate-data#validate-params"}
另外，可以结合 Zod 等模式验证器，使用 `getValidatedRouterParams` 实现运行时和类型安全。
::

现在你可以在 `/api/hello/nuxt` 调用此 API，得到 `Hello, nuxt!`。

### 匹配 HTTP 方法

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

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

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

以上示例：

- 使用 **GET** 方法请求 `/test` 返回 `测试 GET 处理器`
- 使用 **POST** 方法请求 `/test` 返回 `测试 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"}
另外，可结合 Zod 等模式验证器使用 `readValidatedBody` 实现运行时及类型安全。
::

现在可以这样调用 API：

```vue [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` 时，会抛出 `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"}
另外，结合 Zod 等模式验证器使用 `getValidatedQuery` 可实现运行时和类型安全。
::

### 错误处理

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

任何未捕获的错误返回 HTTP 500 内部服务器错误。

要返回其他错误代码，请使用 [`createError`](https://nuxt.zhcndoc.com/docs/3.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({
      status: 400,
      statusText: 'ID 应该是整数',
    })
  }
  return '一切正常'
})
```

### 状态码

要返回其他状态码，请使用 [`setResponseStatus`](https://nuxt.zhcndoc.com/docs/3.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='<my-super-token>'
```
::

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

### 请求 Cookies

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

  return { cookies }
})
```

### 转发上下文和头部

默认情况下，在服务器路由中发起 fetch 请求时，不会转发来自请求的头部和请求上下文。你可以使用 `event.$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，会在处理器结束前等待其完成，确保任务执行完成，即使服务器已发送响应后可能终止处理器。它还集成运行时提供者以支持异步操作。

```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/3.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'
import { sendStream } from 'h3'

export default defineEventHandler((event) => {
  return sendStream(event, 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/h3js/h3) 实现传统支持，但建议尽量避免使用传统处理器。
::

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

::warning
切勿将 `next()` 回调与返回 `Promise` 或为 `async` 的传统中间件混用。
::

### 服务器存储

Nitro 提供跨平台的[存储层](https://nitro.build/guide/storage)。若要配置其他存储挂载点，可以使用 `nitro.storage` 或[服务器插件](https://nuxt.zhcndoc.com/docs/3.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.
