工具函数
server/utils/ 目录下的文件自动导入为服务端工具函数,在整个 server/ 目录中可以直接使用,无需手动 import。
自动导入机制
ts
// server/utils/database.ts
export const createDb = () => {
return drizzle(process.env.DATABASE_URL!)
}在服务端任意位置直接使用,无需 import:
ts
// server/api/users.ts
export default defineEventHandler(async () => {
const db = createDb() // 自动导入!
return await db.select().from(users)
})自动导入的原理
Nitro 扫描 server/utils/ 目录下的所有导出,将它们注册为自动导入。和 Nuxt 的客户端自动导入类似,但只作用于 server/ 目录。
自动导入的范围
server/utils/ 中的导出只在 server/ 目录内可用,客户端代码无法使用。反之亦然。
#server 别名
在深层嵌套的服务端文件中,使用 #server 别名代替繁琐的相对路径:
ts
// server/api/admin/users/roles.ts
// ❌ 相对路径——层级深时难以维护
import { isAdmin } from '../../../utils/auth'
// ✅ #server 别名——清晰明确
import { isAdmin } from '#server/utils/auth'#server 的适用范围
只能在 server/ 目录内使用。客户端代码中不存在 #server。
何时用 #server?
当你的工具函数在深层嵌套的目录中,相对路径超过 2 层时,推荐用 #server。
常用工具函数封装
响应封装
统一 API 响应格式,让前端处理更一致:
ts
// server/utils/response.ts
export const successResponse = <T>(data: T, message = 'ok') => {
return { code: 0, message, data }
}
export const errorResponse = (message: string, code = -1) => {
return { code, message, data: null }
}
export const paginatedResponse = <T>(
list: T[],
total: number,
page: number,
pageSize: number
) => {
return {
code: 0,
message: 'ok',
data: {
list,
pagination: {
total,
page,
pageSize,
totalPages: Math.ceil(total / pageSize),
},
},
}
}ts
// server/api/users.ts
export default defineEventHandler(async () => {
const users = await getUsers()
return successResponse(users)
})
// server/api/users.ts(分页)
export default defineEventHandler(async (event) => {
const { page = 1, size = 20 } = getQuery(event)
const { list, total } = await getPaginatedUsers(page, size)
return paginatedResponse(list, total, page, size)
})统一响应格式的好处
- 前端可以统一处理
code判断成功/失败 - 分页数据格式一致,避免每个接口重复定义
- 错误消息格式统一,便于展示
JWT 工具
ts
// server/utils/jwt.ts
import jwt from 'jsonwebtoken'
export const signToken = (payload: object) => {
return jwt.sign(payload, process.env.JWT_SECRET!, { expiresIn: '7d' })
}
export const verifyToken = (token: string) => {
return jwt.verify(token, process.env.JWT_SECRET!) as { userId: number }
}
export const getTokenFromHeader = (event: H3Event) => {
const auth = getRequestHeader(event, 'authorization')
if (!auth?.startsWith('Bearer ')) {
throw createError({ statusCode: 401, statusMessage: 'Unauthorized', message: 'Missing token' })
}
return verifyToken(auth.replace('Bearer ', ''))
}ts
// server/api/me.ts
export default defineEventHandler((event) => {
const { userId } = getTokenFromHeader(event)
return getUserById(userId)
})密码工具
ts
// server/utils/password.ts
import bcrypt from 'bcryptjs'
export const hashPassword = (password: string) => {
return bcrypt.hash(password, 10)
}
export const verifyPassword = (password: string, hash: string) => {
return bcrypt.compare(password, hash)
}ts
// server/api/auth/register.ts
export default defineEventHandler(async (event) => {
const { name, email, password } = await readBody(event)
const hashedPassword = await hashPassword(password)
const user = await createUser({ name, email, password: hashedPassword })
return { id: user.id, name: user.name }
})分页工具
ts
// server/utils/pagination.ts
export interface PaginationResult<T> {
list: T[]
total: number
page: number
pageSize: number
totalPages: number
}
export const paginate = async <T>(
query: any,
page = 1,
pageSize = 20
): Promise<PaginationResult<T>> => {
const offset = (page - 1) * pageSize
const [list, totalResult] = await Promise.all([
query.offset(offset).limit(pageSize),
query.count(),
])
return {
list,
total: totalResult,
page,
pageSize,
totalPages: Math.ceil(totalResult / pageSize),
}
}
// 从请求中获取分页参数
export const getPaginationParams = (event: H3Event) => {
const query = getQuery(event)
return {
page: Math.max(1, Number(query.page) || 1),
pageSize: Math.min(100, Math.max(1, Number(query.size) || 20)),
}
}ts
// server/api/users.ts
export default defineEventHandler(async (event) => {
const { page, pageSize } = getPaginationParams(event)
const result = await paginate(db.select().from(users), page, pageSize)
return result
})getPaginationParams 的安全处理
Math.max(1, ...)→ page 最小为 1Math.min(100, ...)→ pageSize 最大为 100(防止一次查询太多数据)Math.max(1, ...)→ pageSize 最小为 1
权限检查工具
ts
// server/utils/auth.ts
export const requireAuth = (event: H3Event) => {
if (!event.context.user) {
throw createError({ statusCode: 401, statusMessage: 'Unauthorized', message: '未认证' })
}
return event.context.user
}
export const requireAdmin = (event: H3Event) => {
const user = requireAuth(event)
if (user.role !== 'admin') {
throw createError({ statusCode: 403, statusMessage: 'Forbidden', message: '需要管理员权限' })
}
return user
}
export const requireOwner = (event: H3Event, resourceUserId: number) => {
const user = requireAuth(event)
if (user.role !== 'admin' && user.id !== resourceUserId) {
throw createError({ statusCode: 403, statusMessage: 'Forbidden', message: '需要资源所有者或管理员权限' })
}
return user
}ts
// server/api/admin/users.ts
export default defineEventHandler((event) => {
requireAdmin(event) // 只允许管理员
return getAllUsers()
})
// server/api/users/[id].ts
export default defineEventHandler((event) => {
const user = requireAuth(event)
const id = getRouterParam(event, 'id')
requireOwner(event, Number(id)) // 只允许本人或管理员
return getUserById(id)
})H3 内置工具函数
H3 是 Nuxt 服务端的底层框架,提供了丰富的工具函数:
请求相关
| 函数 | 说明 | 示例 |
|---|---|---|
readBody(event) | 读取请求体(JSON 自动解析) | const body = await readBody(event) |
readValidatedBody(event, schema) | 校验请求体 | const body = await readValidatedBody(event, schema.parse) |
getQuery(event) | 获取查询参数 | const { page } = getQuery(event) |
getValidatedQuery(event, schema) | 校验查询参数 | const query = await getValidatedQuery(event, schema.parse) |
getRouterParam(event, name) | 获取路由参数 | const id = getRouterParam(event, 'id') |
getRequestHeader(event, name) | 获取请求头 | const auth = getRequestHeader(event, 'authorization') |
getRequestURL(event) | 获取请求 URL | const url = getRequestURL(event) |
getCookie(event, name) | 获取 Cookie | const token = getCookie(event, 'auth-token') |
响应相关
| 函数 | 说明 | 示例 |
|---|---|---|
setResponseStatus(event, code) | 设置状态码 | setResponseStatus(event, 201) |
setResponseHeader(event, name, value) | 设置响应头 | setResponseHeader(event, 'x-total', '100') |
sendRedirect(event, url, code) | 重定向 | await sendRedirect(event, '/login', 302) |
setCookie(event, name, value, opts) | 设置 Cookie | setCookie(event, 'token', value, { httpOnly: true }) |
deleteCookie(event, name, opts) | 删除 Cookie | deleteCookie(event, 'token') |
sendStream(event, stream) | 发送流 | return sendStream(event, createReadStream(path)) |
这些函数都是自动导入的
无需手动 import。直接在服务端代码中使用即可。
知识脉络
text
API 路由 → 服务路由 → 服务中间件 → 服务插件 → 你在这里:工具函数
│
├─→ 下一步:事件处理
│
└─→ 相关:事件处理(H3 工具函数详解)