Nuxt4服务器开发 nuxt-server

Nuxt 4 服务器端开发技能,专注于使用 Nitro 框架构建后端逻辑,包括 API 路由、服务器中间件、数据库集成(如 D1、PostgreSQL、Drizzle)、文件上传、WebSocket 实现和错误处理。关键词:Nuxt 4,服务器开发,Nitro,API 路由,数据库集成,WebSocket,文件上传

后端开发 0 次安装 0 次浏览 更新于 3/7/2026

名称: nuxt-server 描述: | Nuxt 4 服务器端开发,使用 Nitro:API 路由、服务器中间件、数据库集成和后端模式。

使用场景:创建服务器 API 路由、实现服务器中间件、集成数据库(如 D1、PostgreSQL、Drizzle)、处理文件上传、实现 WebSocket 或使用 Nitro 构建后端逻辑。

关键词:服务器路由、API 路由、Nitro、defineEventHandler、getRouterParam、getQuery、readBody、setCookie、createError、服务器中间件、D1、Drizzle、PostgreSQL、WebSocket、文件上传 许可证: MIT 元数据: 版本: 4.0.0 作者: Claude Skills 维护者 类别: 框架 框架: Nuxt 框架版本: 4.x 最后验证日期: 2025-12-28

Nuxt 4 服务器开发

服务器路由、API 模式和使用 Nitro 的后端开发。

快速参考

基于文件的服务器路由

server/
├── api/                      # API 端点 (/api/*)
│   ├── users/
│   │   ├── index.get.ts      → GET  /api/users
│   │   ├── index.post.ts     → POST /api/users
│   │   ├── [id].get.ts       → GET  /api/users/:id
│   │   ├── [id].put.ts       → PUT  /api/users/:id
│   │   └── [id].delete.ts    → DELETE /api/users/:id
│   └── health.get.ts         → GET  /api/health
├── routes/                   # 非 API 路由
│   └── sitemap.xml.get.ts    → GET  /sitemap.xml
├── middleware/               # 服务器中间件
│   └── auth.ts               # 在每个请求上运行
├── plugins/                  # Nitro 插件
│   └── database.ts           # 初始化数据库
└── utils/                    # 服务器工具
    └── db.ts                 # 数据库辅助函数

HTTP 方法后缀

后缀 HTTP 方法
.get.ts GET
.post.ts POST
.put.ts PUT
.patch.ts PATCH
.delete.ts DELETE
.ts 所有方法

何时加载参考资料

加载 references/server.md 时:

  • 实现复杂的 API 路由
  • 处理认证和会话
  • 处理 cookie 和 headers
  • 构建文件上传端点
  • 理解 Nitro 内部机制

加载 references/database-patterns.md 时:

  • 集成 Cloudflare D1 与 Drizzle
  • 设置 PostgreSQL 连接
  • 实现数据库迁移
  • 构建查询模式

加载 references/websocket-patterns.md 时:

  • 实现实时功能
  • 构建 WebSocket 端点
  • 使用 Durable Objects 处理状态

基本事件处理器

// server/api/users/index.get.ts
export default defineEventHandler(async (event) => {
  // 返回数据(自动序列化为 JSON)
  return {
    users: [
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ]
  }
})

请求工具

URL 参数

// server/api/users/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')

  if (!id) {
    throw createError({
      statusCode: 400,
      message: '需要用户 ID'
    })
  }

  return { id }
})

查询参数

// GET /api/users?page=1&limit=10&search=john
export default defineEventHandler(async (event) => {
  const query = getQuery(event)

  const page = Number(query.page) || 1
  const limit = Number(query.limit) || 10
  const search = query.search as string | undefined

  return { page, limit, search }
})

请求体

// server/api/users/index.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)

  // 验证 body
  if (!body.name || !body.email) {
    throw createError({
      statusCode: 400,
      message: '需要名称和邮箱'
    })
  }

  // 创建用户...
  return { success: true, user: { id: 1, ...body } }
})

头部

export default defineEventHandler(async (event) => {
  // 读取 headers
  const authHeader = getHeader(event, 'authorization')
  const contentType = getHeader(event, 'content-type')

  // 设置响应 headers
  setHeader(event, 'X-Custom-Header', 'value')
  setHeader(event, 'Cache-Control', 'max-age=3600')

  return { authHeader, contentType }
})

响应工具

设置状态码

export default defineEventHandler(async (event) => {
  // 设置状态码
  setResponseStatus(event, 201)  // 已创建

  return { message: '资源已创建' }
})

重定向

export default defineEventHandler(async (event) => {
  // 重定向
  return sendRedirect(event, '/new-location', 302)
})

错误处理

export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')

  const user = await findUser(id)

  if (!user) {
    throw createError({
      statusCode: 404,
      statusMessage: '未找到',
      message: `未找到 ID 为 ${id} 的用户`
    })
  }

  return user
})

Cookies

export default defineEventHandler(async (event) => {
  // 读取 cookie
  const sessionId = getCookie(event, 'session_id')

  // 设置 cookie
  setCookie(event, 'session_id', 'abc123', {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 7  // 1 周
  })

  // 删除 cookie
  deleteCookie(event, 'old_cookie')

  return { sessionId }
})

服务器中间件

// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
  // 跳过公共路由
  const publicRoutes = ['/api/auth/login', '/api/health']
  if (publicRoutes.includes(event.path)) {
    return  // 继续下一个处理器
  }

  // 检查认证
  const token = getHeader(event, 'authorization')?.replace('Bearer ', '')

  if (!token) {
    throw createError({
      statusCode: 401,
      message: '需要认证'
    })
  }

  // 验证 token 并附加用户到上下文
  const user = await verifyToken(token)
  event.context.user = user
})

在路由中访问上下文

// server/api/profile.get.ts
export default defineEventHandler(async (event) => {
  // 由中间件附加的用户
  const user = event.context.user

  if (!user) {
    throw createError({ statusCode: 401, message: '未认证' })
  }

  return { user }
})

数据库集成

Cloudflare D1 与 Drizzle

// server/utils/db.ts
import { drizzle } from 'drizzle-orm/d1'
import * as schema from '~/server/database/schema'

export function useDB(event: H3Event) {
  const { DB } = event.context.cloudflare.env
  return drizzle(DB, { schema })
}

// server/api/users/index.get.ts
export default defineEventHandler(async (event) => {
  const db = useDB(event)

  const users = await db.select().from(schema.users).limit(10)

  return { users }
})

模式定义

// server/database/schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  name: text('name').notNull(),
  email: text('email').notNull().unique(),
  createdAt: integer('created_at', { mode: 'timestamp' })
    .notNull()
    .$defaultFn(() => new Date())
})

export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  userId: integer('user_id').notNull().references(() => users.id),
  title: text('title').notNull(),
  content: text('content'),
  createdAt: integer('created_at', { mode: 'timestamp' })
    .notNull()
    .$defaultFn(() => new Date())
})

CRUD 操作

// server/api/users/index.post.ts
import { users } from '~/server/database/schema'
import { eq } from 'drizzle-orm'

export default defineEventHandler(async (event) => {
  const db = useDB(event)
  const body = await readBody(event)

  // 创建
  const [user] = await db.insert(users)
    .values({ name: body.name, email: body.email })
    .returning()

  return { user }
})

// server/api/users/[id].put.ts
export default defineEventHandler(async (event) => {
  const db = useDB(event)
  const id = getRouterParam(event, 'id')
  const body = await readBody(event)

  // 更新
  const [user] = await db.update(users)
    .set({ name: body.name })
    .where(eq(users.id, Number(id)))
    .returning()

  if (!user) {
    throw createError({ statusCode: 404, message: '未找到用户' })
  }

  return { user }
})

// server/api/users/[id].delete.ts
export default defineEventHandler(async (event) => {
  const db = useDB(event)
  const id = getRouterParam(event, 'id')

  // 删除
  await db.delete(users).where(eq(users.id, Number(id)))

  return { success: true }
})

使用 Zod 进行验证

// server/api/users/index.post.ts
import { z } from 'zod'

const createUserSchema = z.object({
  name: z.string().min(2).max(100),
  email: z.string().email(),
  age: z.number().int().min(0).max(150).optional()
})

export default defineEventHandler(async (event) => {
  const body = await readBody(event)

  // 验证
  const result = createUserSchema.safeParse(body)

  if (!result.success) {
    throw createError({
      statusCode: 400,
      message: '验证失败',
      data: result.error.flatten()
    })
  }

  // 使用已验证的数据
  const { name, email, age } = result.data

  // 创建用户...
  return { success: true }
})

文件上传

// server/api/upload.post.ts
export default defineEventHandler(async (event) => {
  const formData = await readMultipartFormData(event)

  if (!formData) {
    throw createError({ statusCode: 400, message: '未上传文件' })
  }

  const file = formData.find(f => f.name === 'file')

  if (!file) {
    throw createError({ statusCode: 400, message: '需要文件字段' })
  }

  // file.filename - 原始文件名
  // file.type - MIME 类型
  // file.data - 包含文件内容的 Buffer

  // 上传到 R2 (Cloudflare)
  const { R2 } = event.context.cloudflare.env
  const key = `uploads/${Date.now()}-${file.filename}`
  await R2.put(key, file.data)

  return { key, filename: file.filename, type: file.type }
})

服务器工具

// server/utils/auth.ts
import { H3Event } from 'h3'

export function requireAuth(event: H3Event) {
  const user = event.context.user

  if (!user) {
    throw createError({
      statusCode: 401,
      message: '需要认证'
    })
  }

  return user
}

export function requireRole(event: H3Event, role: string) {
  const user = requireAuth(event)

  if (user.role !== role) {
    throw createError({
      statusCode: 403,
      message: '权限不足'
    })
  }

  return user
}

// 在路由中使用
export default defineEventHandler(async (event) => {
  const user = requireAuth(event)
  // 或
  const admin = requireRole(event, 'admin')
})

常见反模式

缺少方法后缀

// 错误 - 处理所有方法
// server/api/users.ts

// 正确 - 明确方法
// server/api/users.get.ts   → GET
// server/api/users.post.ts  → POST

不抛出错误

// 错误 - 将错误作为数据返回
export default defineEventHandler(async (event) => {
  const user = await findUser(id)
  if (!user) {
    return { error: '未找到' }  // 200 状态码!
  }
})

// 正确 - 抛出错误
export default defineEventHandler(async (event) => {
  const user = await findUser(id)
  if (!user) {
    throw createError({ statusCode: 404, message: '未找到' })
  }
})

忘记 Async/Await

// 错误 - Body 未等待
export default defineEventHandler((event) => {
  const body = readBody(event)  // 返回 Promise!
})

// 正确
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
})

故障排除

API 路由返回 404:

  • 确保文件在 server/api/ 中(不在 app/api/
  • 检查方法后缀是否匹配请求(.get.ts 对应 GET)
  • 验证文件扩展名为 .ts

Body 为空:

  • 确保使用 await readBody(event) 而不是 readBody(event)
  • 检查 Content-Type 头部设置正确
  • 对于 multipart,使用 readMultipartFormData

中间件未运行:

  • 检查文件是否在 server/middleware/
  • 中间件对所有请求运行,除非被过滤

D1 绑定未找到:

  • 检查 wrangler.toml 已配置 [[d1_databases]]
  • 通过 event.context.cloudflare.env.DB 访问

相关技能

  • nuxt-core:项目设置、路由、配置
  • nuxt-data:组合函数、数据获取、状态管理
  • nuxt-production:性能、测试、部署
  • cloudflare-d1:D1 数据库模式

版本:4.0.0 | 最后更新:2025-12-28 | 许可证:MIT