Nitro 服务端入口

使用服务端入口处理没有匹配到任何路由的请求

服务端入口是 Nitro 注册为 catch-all(/**)路由的特殊处理器。特定路由始终具有优先权,因此服务端入口只会在请求没有匹配到任何路由时、渲染器运行之前执行。它通常用于在 Nitro 中挂载另一个框架,或为文件系统路由未处理的所有请求实现自定义路由。

Warning

服务端入口是回退机制,而不是全局中间件:对于已由路由处理的请求,它不会运行。对于必须应用于每个请求的横切关注点(身份验证、日志记录、请求预处理),请改用中间件。

#自动检测的 server.ts

默认情况下,Nitro 会在 serverDir(如果已设置)或项目根目录中自动查找 server.ts(或 .js、.mjs、.mts、.tsx、.jsx)文件。

如果找到,Nitro 将使用它作为服务端入口,并针对所有传入请求运行它。

export default {
  async fetch(req: Request) {
    const url = new URL(req.url);

    // 处理特定路由
    if (url.pathname === "/health") {
      return new Response("OK", {
        status: 200,
        headers: { "content-type": "text/plain" }
      });
    }

    // 为所有请求添加自定义请求头
    // 不返回内容以继续执行下一个处理器
  }
}

Tip

当检测到 server.ts 时,Nitro 会在终端中输出:已检测到 `server.ts` 作为服务端入口。

使用此配置:

  • /health → 由服务端入口处理(返回响应)
  • /api/hello → 由 API 路由处理器直接处理
  • /about 等 → 服务端入口先运行,如果没有返回响应,则继续执行到渲染器

#框架兼容性

服务端入口是与其他框架集成的绝佳方式。任何公开标准 Web fetch(request: Request): Response 接口的框架都可以用作服务端入口。

#Web 兼容框架

实现 Web fetch API 的框架可以直接与 server.ts 配合使用:

server.ts
import { H3 } from "h3";

const app = new H3()

app.get("/", () => "⚡️ 来自 H3 的问候!");

export default app;
server.ts
import { Hono } from "hono";

const app = new Hono();

app.get("/", (c) => c.text("🔥 来自 Hono 的问候!"));

export default app;
server.ts
import { Elysia } from "elysia";

const app = new Elysia();

app.get("/", () => "🦊 来自 Elysia 的问候!");

export default app.compile();

#Node.js 框架

对于使用 (req, res) 风格处理器(如 Express 或 Fastify)的 Node.js 框架,请将服务端入口文件命名为 server.node.ts 而不是 server.ts。Nitro 将自动检测 .node. 后缀,并使用 srvx 将 Node.js 处理器转换为兼容 Web 的 fetch 处理器。

server.node.ts
import Express from "express";

const app = Express();

app.use("/", (_req, res) => {
  res.send("Hello from Express with Nitro!");
});

export default app;
server.node.ts
import Fastify from "fastify";

const app = Fastify();

app.get("/", () => "Hello, Fastify with Nitro!");

await app.ready();

export default app.routing;

#服务端选项

当服务端入口的默认导出为普通对象时,以下服务端选项会传递给由 Node.js、Bun 和 Deno 预设启动的 srvx 服务器:port、hostname、reusePort、protocol、tls、silent、gracefulShutdown、maxRequestBodySize、trustProxy 以及运行时特定设置(node、bun、deno)。

使用 defineServerEntry 辅助函数获取类型化选项:

server.ts
import { defineServerEntry } from "nitro";

export default defineServerEntry({
  fetch(req) {
    return new Response("Hello from server entry!");
  },
  port: 8080,
  maxRequestBodySize: 1024 * 1024,
});
Read more in srvx server options.

Note

  • NITRO_PORT/PORT、NITRO_HOST/HOST 和 NITRO_SSL_CERT/NITRO_SSL_KEY 环境变量的优先级高于 port、hostname 和 tls 选项,因此服务器仍可在运行时进行配置。
  • 在开发过程中(nitro dev),选项会应用于开发工作进程服务器,但监听器选项(port、hostname、protocol、tls、silent、gracefulShutdown)除外,这些选项由开发服务器和 CLI(--port、--host)控制。Vite 开发服务器不会应用这些选项。
  • 其他 srvx 选项(middleware、plugins、error 和 manual)会被忽略。这些选项会改变请求的处理方式,但只适用于这些预设,不适用于无服务器部署或 nitroApp.fetch。请改用 Nitro 中间件和插件。
  • 选项只会从普通对象导出中读取(不会从 export default app 这类框架实例中读取),也不会从 Node.js 格式入口(server.node.ts)中读取。

#配置

#自定义服务端入口文件

你可以使用 Nitro 配置中的 serverEntry 选项指定自定义服务端入口文件:

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  serverEntry: "./nitro.server.ts"
})

你还可以提供一个包含 handler 和 format 选项的对象:

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  serverEntry: {
    handler: "./server.ts",
    format: "node" // "web"(默认)或 "node"
  }
})

#处理器格式

format 选项控制 Nitro 如何处理服务端入口的默认导出:

  • "web"(默认):要求使用具有 fetch(request: Request): Response 方法的 Web 兼容处理器
  • "node":要求使用 Node.js 风格的 (req, res) 处理器。Nitro 会自动将其转换为 Web 兼容处理器

自动检测时,格式由文件名决定:server.node.ts 使用 "node" 格式,而 server.ts 使用 "web" 格式。

#禁用服务端入口

将 serverEntry 设置为 false 以禁用自动检测,并阻止 Nitro 使用任何服务端入口:

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  serverEntry: false
})

#使用事件处理器

你可以导出使用 defineHandler 创建的事件处理器,而不是 Web fetch 处理器,从而获得更好的类型推断并访问 H3 事件对象:

server.ts
import { defineHandler, HTTPError } from "nitro";

export default defineHandler((event) => {
  // 仅在请求未匹配到任何路由时运行
  if (event.url.pathname.startsWith("/api/")) {
    throw new HTTPError("Unknown API endpoint", { status: 404 });
  }

  // 为渲染器添加上下文
  event.context.requestId = crypto.randomUUID();

  // 不返回内容以将请求交给渲染器
});

Important

返回 undefined(或不返回任何内容)会将请求交给渲染器。如果没有渲染器,Nitro 会返回一个空的 200 响应。返回一个值则会在此处结束请求。

#启动逻辑

服务端入口会被预先导入,因此其中的顶层代码会在服务器启动时运行一次,而不是在第一个请求到达时运行。

请注意:

  • 顶层代码会在运行时插件之前运行,因此插件注册的钩子尚不可用。不要在顶层调用 useNitroApp();应在处理器内部调用它
  • 顶层 await 会延迟启动:在服务端入口完成求值之前,服务器不会接受请求
  • 在无服务器和边缘预设中,“启动”意味着新实例的每次冷启动。某些运行时(例如 Cloudflare Workers)不允许在全局作用域中执行 I/O(例如 fetch)
  • 在开发过程中,每当服务端入口重新加载时,顶层代码都会再次运行

#请求生命周期

服务端入口被注册为通配符(/**)路由处理器。当特定路由(如 /api/hello)匹配到请求时,该路由处理器具有优先权。对于不匹配任何特定路由的请求,服务端入口在渲染器之前运行:

1. Server hook: `request`
2. Route rules (headers, redirects, etc.)
3. Global middleware (static assets first, then middleware/)
4. Route-scoped middleware (handlers config)
5. Route matching:
   a. Specific routes (routes/) ← if matched, handles the request
   b. Server entry ← runs for unmatched routes
   c. Renderer (renderer.ts or index.html)

当服务端入口和渲染器同时存在时,它们会串联执行:服务端入口先运行,如果没有返回响应,则由渲染器处理请求。

Read more in 生命周期.

#开发模式

在开发过程中,Nitro 会监视服务端入口文件的变更。当文件被创建、修改或删除时,开发服务器会自动重新加载以获取最新更改。

#最佳实践

  • 将服务端入口用作没有匹配到任何路由的请求的回退机制,或用于挂载另一个框架
  • 对于必须应用于每个请求(包括已匹配路由)的关注点,请使用中间件
  • 返回 undefined 以继续交给渲染器;返回一个值以结束请求
  • 保持服务端入口轻量;它会针对每个未匹配的请求运行
  • 使用运行时插件实现一次性初始化逻辑
  • 不要将服务端入口用于特定路由逻辑;路由处理器的性能更高。