服务端与 Nitro

每个 Nuxt 应用背后都有 Nitro —— 负责 SSR、API 路由、中间件、部署预设的 HTTP 引擎。server/ 目录下的一切都跑在 Nitro 上,Vite 不参与。

Nitro 的能力

  • 构建出一个 .output/ 目录,能运行在所有 JS 运行时。
  • 15+ 部署预设(Node、Vercel、Netlify、Cloudflare Workers、AWS Lambda、Deno……)。
  • 基于 h3 的 HTTP 原语 —— 小、快、可移植。
  • 通过 routeRules 做混合渲染(SSR / ISR / SWR / 预渲染 / CSR)。
  • 文件式 API 路由、中间件、插件。

目录布局

server/
├── api/                # /api/* 接口
│   ├── posts.get.ts
│   └── posts.post.ts
├── routes/             # 自定义路由
│   └── sitemap.xml.ts
├── middleware/         # 每个请求都会跑
└── plugins/            # Nitro 插件(钩子、长连接)

HMR 与自动导入机制跟 Vue 层一致。

API 路由

文件名后缀映射 HTTP 方法:

// server/api/posts.get.ts
export default defineEventHandler(async () => {
  return await db.posts.findMany()
})
// server/api/posts.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody<{ title: string }>(event)
  const post = await db.posts.create({ data: body })
  return post
})

可以返回字符串、可 JSON 序列化对象、ResponseReadableStream,Nitro 负责序列化。

动态参数用方括号:

// server/api/posts/[id].get.ts
export default defineEventHandler((event) => {
  const id = event.context.params!.id
  return db.posts.findUnique({ where: { id: Number(id) } })
})

自定义路由(非 API)

想要根路径的 /sitemap.xml/healthz?放 server/routes/

// server/routes/healthz.ts
export default defineEventHandler(() => 'ok')

服务端中间件

在所有 API handler 之前,每个服务端请求都会跑:

// server/middleware/request-id.ts
export default defineEventHandler((event) => {
  event.context.requestId = crypto.randomUUID()
})

通过 event.context 往下游传值最方便。

Nitro 插件

挂到服务端生命周期上做日志、数据库连接池、定时任务等:

// server/plugins/db.ts
import { prisma } from '~~/server/utils/prisma'

export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('close', async () => {
    await prisma.$disconnect()
  })
})

h3 常用工具

Nuxt / Nitro 内置的这些工具又快又兼容 Edge:

export default defineEventHandler(async (event) => {
  const body = await readBody(event)           // 解析 body
  const query = getQuery(event)                // 解析 ?query
  const headers = getRequestHeaders(event)     // 读取 headers
  setResponseStatus(event, 201)                // 设置状态码
  setResponseHeader(event, 'x-custom', '1')    // 设置响应头
  return { ok: true }
})

错误

createError 抛结构化错误:

throw createError({ status: 404, statusText: 'Post Not Found' })

Nitro 会转成正确的 HTTP 响应(面向 HTML 请求还会渲染错误页)。

通过 routeRules 做混合渲染

SSR / ISR / 预渲染 / CSR 的策略都放在 routeRules 里:

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },              // 构建期静态化
    '/blog/**': { swr: 3600 },             // SWR 1 小时
    '/api/public/*': { cache: { maxAge: 300 } },
    '/admin/**': { ssr: false },           // 只 CSR
    '/old': { redirect: { to: '/new', statusCode: 301 } },
  },
})

Nitro 会把这些规则翻译成各平台原生能力:Cloudflare Workers 用 Cache API,Vercel 用 ISR,Node 用内存缓存。

部署相关

部署预设常见几个:

export default defineNuxtConfig({
  nitro: {
    preset: 'node-server',    // 也可 'vercel'、'netlify'、'cloudflare-pages' 等
  },
})

详见“部署”那一章 —— 这里只要记住 server/ 里的东西都会随应用一起进入你选的运行时。

Checklist

  • 数据库连接放 Nitro 插件里,每个进程一个连接池。
  • 横切关注点(请求 ID、鉴权)用 server/middleware/
  • 渲染策略能用 routeRules 就用,别自己写轮子。
  • 抛错用 createError,不要手写状态码。
  • 纯数据逻辑抽到 server/utils/,它们也会被自动导入。

至此你就拥有了一个完整的全栈 Nuxt 应用:前端 Vue + 后端 Nitro,都在同一个仓库里。

伺服器與 Nitro

每個 Nuxt 應用背後皆有 Nitro —— 負責 SSR、API 路由、中介層、部署預設的 HTTP 引擎。server/ 目錄下的一切皆運行於 Nitro,Vite 不參與。

Nitro 的能力

  • 建置產生單一 .output/ 目錄,可運行於所有 JS 執行時。
  • 15+ 部署預設(Node、Vercel、Netlify、Cloudflare Workers、AWS Lambda、Deno……)。
  • 基於 h3 的 HTTP 原語 —— 小、快、可攜。
  • 透過 routeRules 進行混合渲染(SSR / ISR / SWR / 預渲染 / CSR)。
  • 檔案式 API 路由、中介層與外掛。

目錄佈局

server/
├── api/                # /api/* 端點
│   ├── posts.get.ts
│   └── posts.post.ts
├── routes/             # 自訂路由
│   └── sitemap.xml.ts
├── middleware/         # 每個請求皆執行
└── plugins/            # Nitro 外掛(鉤子、長連線)

HMR 與自動匯入機制與 Vue 層一致。

API 路由

檔名後綴對應 HTTP 方法:

// server/api/posts.get.ts
export default defineEventHandler(async () => {
  return await db.posts.findMany()
})
// server/api/posts.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody<{ title: string }>(event)
  const post = await db.posts.create({ data: body })
  return post
})

可回傳字串、可 JSON 序列化物件、ResponseReadableStream,Nitro 負責序列化。

動態參數用方括號:

// server/api/posts/[id].get.ts
export default defineEventHandler((event) => {
  const id = event.context.params!.id
  return db.posts.findUnique({ where: { id: Number(id) } })
})

自訂路由(非 API)

需要根路徑 /sitemap.xml/healthz?置於 server/routes/

// server/routes/healthz.ts
export default defineEventHandler(() => 'ok')

伺服器中介層

於所有 API handler 之前,每個伺服器請求皆會執行:

// server/middleware/request-id.ts
export default defineEventHandler((event) => {
  event.context.requestId = crypto.randomUUID()
})

透過 event.context 向下游傳遞資料最方便。

Nitro 外掛

掛在伺服器生命週期上處理日誌、資料庫連線池、排程等:

// server/plugins/db.ts
import { prisma } from '~~/server/utils/prisma'

export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('close', async () => {
    await prisma.$disconnect()
  })
})

h3 常用工具

Nuxt/Nitro 內建的工具既快又相容 Edge:

export default defineEventHandler(async (event) => {
  const body = await readBody(event)           // 解析 body
  const query = getQuery(event)                // 解析 ?query
  const headers = getRequestHeaders(event)     // 讀取 headers
  setResponseStatus(event, 201)                // 設定狀態碼
  setResponseHeader(event, 'x-custom', '1')    // 設定回應標頭
  return { ok: true }
})

錯誤

createError 拋結構化錯誤:

throw createError({ status: 404, statusText: 'Post Not Found' })

Nitro 會轉為正確的 HTTP 回應(對 HTML 請求還會渲染錯誤頁)。

routeRules 做混合渲染

SSR / ISR / 預渲染 / CSR 策略皆寫於 routeRules

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },              // 建置期靜態化
    '/blog/**': { swr: 3600 },             // SWR 1 小時
    '/api/public/*': { cache: { maxAge: 300 } },
    '/admin/**': { ssr: false },           // 僅 CSR
    '/old': { redirect: { to: '/new', statusCode: 301 } },
  },
})

Nitro 會將這些規則翻譯為各平台原生能力:Cloudflare Workers 採 Cache API,Vercel 採 ISR,Node 採記憶體快取。

部署相關

常見部署預設:

export default defineNuxtConfig({
  nitro: {
    preset: 'node-server',    // 亦可 'vercel'、'netlify'、'cloudflare-pages' 等
  },
})

詳情見「部署」章節 —— 此處只需記住 server/ 內容會隨應用一同進入所選執行時。

Checklist

  • 資料庫連線置於 Nitro 外掛,每個 process 一個連線池。
  • 橫切關注點(請求 ID、鑑權)用 server/middleware/
  • 渲染策略能用 routeRules 就用,勿自行造輪子。
  • 拋錯用 createError,勿手寫狀態碼。
  • 純資料邏輯抽至 server/utils/,它們亦會自動匯入。

至此你已擁有完整的全棧 Nuxt 應用:前端 Vue + 後端 Nitro,皆位於同一儲存庫。

Server with Nitro

Behind every Nuxt app lives Nitro, the HTTP engine that powers SSR, API routes, middleware, and deployment presets. Everything in your server/ directory runs on Nitro; Vite stays out of it.

What Nitro gives you

  • A single .output/ directory that runs anywhere JavaScript runs.
  • 15+ deployment presets (Node, Vercel, Netlify, Cloudflare Workers, AWS Lambda, Deno…).
  • h3 HTTP primitives — small, fast, portable.
  • Per-route rules for hybrid rendering (SSR / ISR / SWR / prerender / CSR).
  • File-based API routes, middleware, and plugins.

Project layout

server/
├── api/                # /api/* endpoints
│   ├── posts.get.ts
│   └── posts.post.ts
├── routes/             # Arbitrary custom routes
│   └── sitemap.xml.ts
├── middleware/         # Runs on every request
└── plugins/            # Nitro plugins (hooks, services)

HMR and auto-import work the same as on the Vue side.

API routes

Filenames map HTTP methods via a .<method>.ts suffix:

// server/api/posts.get.ts
export default defineEventHandler(async () => {
  return await db.posts.findMany()
})
// server/api/posts.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody<{ title: string }>(event)
  const post = await db.posts.create({ data: body })
  return post
})

Return strings, JSON-serializable objects, Response, or ReadableStream. Nitro handles serialization.

Dynamic params use brackets in the filename:

// server/api/posts/[id].get.ts
export default defineEventHandler((event) => {
  const id = event.context.params!.id
  return db.posts.findUnique({ where: { id: Number(id) } })
})

Custom routes (non-API)

Need /sitemap.xml or /healthz at the root? Put them in server/routes/:

// server/routes/healthz.ts
export default defineEventHandler(() => 'ok')

Server middleware

Runs for every server request, before API handlers:

// server/middleware/request-id.ts
export default defineEventHandler((event) => {
  event.context.requestId = crypto.randomUUID()
})

Set-once, read anywhere in your handlers via event.context.

Nitro plugins

Hook into the server lifecycle for things like logging, DB connection pools, cron:

// server/plugins/db.ts
import { prisma } from '~~/server/utils/prisma'

export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('close', async () => {
    await prisma.$disconnect()
  })
})

h3 utilities

Use the ones that come with Nuxt/Nitro — they're fast and edge-safe:

export default defineEventHandler(async (event) => {
  const body = await readBody(event)           // parse body
  const query = getQuery(event)                // parse ?query
  const headers = getRequestHeaders(event)     // read headers
  setResponseStatus(event, 201)                // set status
  setResponseHeader(event, 'x-custom', '1')    // set header
  return { ok: true }
})

Errors

Throw structured errors with createError:

throw createError({ status: 404, statusText: 'Post Not Found' })

Nitro converts that into the right HTTP response (or an error page for HTML clients).

Hybrid rendering via routeRules

routeRules is where SSR / ISR / prerender / CSR decisions live:

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },              // static at build time
    '/blog/**': { swr: 3600 },             // SWR: 1 hour
    '/api/public/*': { cache: { maxAge: 300 } },
    '/admin/**': { ssr: false },           // CSR-only
    '/old': { redirect: { to: '/new', statusCode: 301 } },
  },
})

Nitro translates these into platform-native behavior — Cloudflare Workers uses cache APIs, Vercel uses ISR, Node caches in memory.

Deployment context

When you're ready, a few presets worth remembering:

export default defineNuxtConfig({
  nitro: {
    preset: 'node-server',    // also: 'vercel', 'netlify', 'cloudflare-pages', …
  },
})

See the Deployment chapter for the details — here, just know that everything in server/ travels with your app into whatever runtime you pick.

Checklist

  • Keep DB connections in a Nitro plugin (one pool per process).
  • Use server/middleware/ for cross-cutting concerns (request IDs, auth).
  • Prefer routeRules over custom logic when choosing rendering strategies.
  • Throw createError instead of writing status codes by hand.
  • Separate pure data logic into server/utils/ — they're auto-imported too.

You now have a full-stack Nuxt app: Vue on the frontend, Nitro on the backend, all in one repo.