一键导入
nitro-api-development
使用 Nitro v3 框架和 H3 编写服务端 API 的技能。适用于后端接口开发、Mock 数据迁移到 Neon 数据库、以及编写符合 Drizzle ORM 标准的查询逻辑。当需要开发新的 CRUD 接口或修复现有后端逻辑时使用此技能。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
使用 Nitro v3 框架和 H3 编写服务端 API 的技能。适用于后端接口开发、Mock 数据迁移到 Neon 数据库、以及编写符合 Drizzle ORM 标准的查询逻辑。当需要开发新的 CRUD 接口或修复现有后端逻辑时使用此技能。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
规范类型项目(apps/type)的代码组织方式、导出语法和文件结构。用于解决类型导出冲突、创建统一导出入口、处理重复导出等问题。适用于类型项目开发、类型错误修复、代码规范实施场景。在处理类型项目的代码写法时,请使用本技能。
当你修改数据库结构或种子生成脚本时,请务必阅读并遵循此指南,以防止性能问题、数据一致性崩溃和部署失败。新 Schema 应在 apps/type 中创建。
数据库 Schema 变更时的全项目同步检查清单。当修改 apps/type 中 schema.ts 的表字段、新增数据库表、或删除表时,使用此技能确保类型项目、数据库迁移、后端接口、前端页面、种子数据和技能文档全部同步更新,避免遗漏。
当用户要求在 bug 已经定位并修复后,记录排错经验、事故结论、AI 记忆更新、复盘摘要或本地 MCP 记忆时使用。这个技能只负责沉淀"发生了什么、为什么会发生、如何修好、以后要记住什么",不要把它用于实际修复 bug。
新建公共组件规范专家 - 指导在 src/components/common 目录下创建符合项目规范的公共组件,包括文件结构、TypeScript 类型、Vue 组件、文档和测试页面。 触发条件(满足任意一项即触发): - 任务包含"新建组件"、"公共组件"、"common 组件"、"创建组件"等关键词 - 需要在 src/components/common 目录下创建新组件 - 需要创建可复用的业务组件(如表单分区标题、操作按钮组、信息展示卡片) - 需要编写组件的 TypeScript 类型定义 - 需要编写组件使用文档(index.md) - 需要创建组件测试页面(src/pages/test-use/) - 用户提及"组件规范"、"组件文档"、"组件测试"等关键词 必须协同的技能: - beautiful-component-design(组件美化时)- 图标、响应式设计、表单分区标题 - component-migration(从旧组件迁移时)- ColorUI → wot-design-uni - use-wd-form(组件内包含表单时)- 表单结构、wd-picker、校验规则 禁止事项: - 禁止在 components 目录外创建公共组件 - 禁止不编写组件文档(index.md) - 禁止不提供使用示例和测试页面 - 禁止组件命名不规范(必须使用短横线命名法) - 禁止不定义 TypeScript 类型(types.ts) - 禁止在组件文件顶部不添加说明注释 - 禁止不使用 withDefaults 设置 props 默认值 覆盖场景:所有需要跨页面复用的业务组件,包括表单分区标题(FormSectionTitle)、操作按钮组(ActivityActions)、信息展示卡片(ActivityInfo)、加载状态组件(ZPagingLoading)等。
接口错误提示能力 - 提供统一的接口错误提示标准和实施方案,基于 wot-design-uni 和 Alova useRequest 回调模式。 触发条件(满足任意一项即触发): - 编写任何 API 接口调用代码(使用 useRequest) - 处理 useRequest 的 onError 回调 - 实现全局错误拦截逻辑 - 用户提及"接口错误提示"、"错误处理"、"Toast 提示"等关键词 - 从 Vue2 迁移 API 调用(需要添加错误处理) - 实现表单提交、列表加载等涉及 API 的功能 必须协同的技能: - api-migration(API 接口迁移时) - z-paging-integration(分页列表时) - use-wd-form(表单提交时) 禁止项: - 禁止使用 try/catch 包装 send() 函数 - 禁止在组件内手动显示错误 Toast(全局拦截器已处理) - 禁止使用 immediate: true(必须手动触发请求) - 禁止在 onError 中重复显示错误提示 覆盖场景:几乎所有 API 调用都需要此技能,包括列表查询、详情查询、表单提交、数据删除、状态更新等。
| name | nitro-api-development |
| description | 使用 Nitro v3 框架和 H3 编写服务端 API 的技能。适用于后端接口开发、Mock 数据迁移到 Neon 数据库、以及编写符合 Drizzle ORM 标准的查询逻辑。当需要开发新的 CRUD 接口或修复现有后端逻辑时使用此技能。 |
| license | MIT |
本技能指导在 apps/api/server 目录下使用 Nitro 框架开发服务端 API。旧 apps/admin/server 仅作为 legacy source 或兼容参考,不再作为长期权威服务端与 DB 运维入口。
defineHandler)。JsonVO 和 PageDTO 结构返回 { success, code, message, data }。这两个类型必须从 @01s-11comm/type 导入。try-catch 包裹全部业务逻辑,catch 块返回标准化错误响应。apps/api/server/routes/api/ 创建文件。文件路径即 API 路由 (例如 api/users.post.ts -> /api/users)。旧 apps/admin/server/api/ 只用于对照 legacy source。defineHandler 定义处理函数,必须使用 try-catch 包裹。import type { JsonVO } from "@01s-11comm/type"(列表接口额外导入 PageDTO)。useDb(event) 获取 Drizzle 实例,并从 @01s-11comm/type 导入 schema。JsonVO<T> 结构({ success, code, message, data })。readBody 使用、参数清洗 (空字符串/pageIndex 映射) 及错误捕获模式。event.req.runtime.cloudflare.env 的正确使用方式。仅 import type { JsonVO } 是不够的——这只是一个死导入,TypeScript 不会检查返回值结构。
必须将 JsonVO 用作响应变量的类型注解 (type annotation),让 TypeScript 编译器在编译期验证字段结构。
// ❌ 错误:仅导入类型,直接返回字面量 → 形同虚设,TypeScript 不做任何检查
import type { JsonVO } from "@01s-11comm/type";
return { success: true, code: 200, msg: "ok", data: result }; // msg 拼错也不会报错
// ✅ 正确:用类型注解标注响应变量 → TypeScript 会严格检查每个字段
import type { JsonVO } from "@01s-11comm/type";
const response: JsonVO<typeof result> = { success: true, code: 200, message: "ok", data: result };
return response; // 如果字段名/类型不符合 JsonVO,编译期立即报错
| 端点类型 | 类型注解写法 |
|---|---|
| 分页列表(list) | JsonVO<PageDTO<(typeof data)[number]>> |
| 单条数据(detail/create/update) | JsonVO<typeof result> |
| 无数据返回(delete) | JsonVO<null> |
| 错误响应(catch 块) | JsonVO<null> |
(typeof data)[number]自动从 Drizzle 查询结果数组推断行类型,无需额外导入实体类型。
/** 列表接口 */
const response: JsonVO<PageDTO<(typeof data)[number]>> = {
success: true,
code: 200,
message: "查询成功",
data: { list: data, total, pageSize: query.pageSize, pageIndex: query.page, totalPages },
};
return response;
/** 单条数据接口 */
const response: JsonVO<typeof result> = {
success: true,
code: 200,
message: "操作成功",
data: result,
};
return response;
JsonVO 类型包含可选的 error 和 stack 字段,专门用于错误场景。error 携带错误信息,stack 仅在开发环境暴露:
const errorResponse: JsonVO<null> = {
success: false,
code: 500,
message: "操作失败",
data: null,
error: error.message || String(error),
stack: error.stack,
};
return errorResponse;
数据库 Schema 中的时间字段使用 Drizzle timestamp 类型,TypeScript 推断为 Date 类型。前端展示需要 string 类型。
API Handler 负责时间字段的格式化转换。
| DB 字段 (Drizzle) | 前端字段 (ListItem) | 转换规则 |
|---|---|---|
createTime | createTime | Date → string (YYYY-MM-DD HH:mm:ss) |
updateTime | updateTime | Date → string (YYYY-MM-DD HH:mm:ss) |
deletedAt | - | 移除(不展示) |
必须从 server/utils/format-date 导入 formatDateTime 函数,禁止在 Handler 内重复定义格式化函数。
// ✅ 正确:导入工具函数
import { formatDateTime } from "server/utils/format-date";
// ❌ 错误:在 Handler 内定义重复的格式化函数
function formatDateTime(date: Date): string {
const pad = (n: number) => n.toString().padStart(2, "0");
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}...`;
}
import { formatDateTime } from "server/utils/format-date";
// 查询数据库(返回 Date 类型)
const data = await db
.select({
id: table.id,
name: table.name,
createTime: table.createTime,
updateTime: table.updateTime,
})
.from(table);
// 映射到前端类型(转换为 string 类型)
const list: XxxListItem[] = data.map((item) => ({
id: item.id,
name: item.name,
createTime: formatDateTime(item.createTime),
updateTime: formatDateTime(item.updateTime),
}));
| 函数 | 参数 | 返回值 | 用途 |
|---|---|---|---|
formatDateTime | date: Date | string | number | null | undefined, fallback?: string | string | 格式化为 YYYY-MM-DD HH:mm:ss |
formatDate | 同上 | string | 格式化为 YYYY-MM-DD |
工具函数源码: apps/admin/server/utils/format-date.ts
createError、defineHandler、defineMiddleware、readBody、getQuery 等)必须从 "nitro/h3" 导入,严禁从 "h3" 直接导入。从 "h3" 导入将导致运行时模块解析失败。
import { createError } from "nitro/h3"; // ✅ 正确
import { createError } from "h3"; // ❌ 运行时报错
import type { JsonVO } from "@01s-11comm/type" 约束返回值结构。server/db 和 server/db/schema(项目未配置 @/server 别名)。{ success, code, message, data } 结构(即 JsonVO)。使用 msg 而非 message,或缺失 success 字段,会导致前端解析异常。try-catch 包裹,catch 块返回标准化错误响应。await。sql 模板字符串。请使用 Drizzle 的查询构建器 (Query Builder)。server/utils/format-date 中的工具函数,禁止在 Handler 内重复定义 formatDateTime。Nitro v3 文件路由中,文件名已经代表了 HTTP 方法,不支持 H3 某些旧版本的对象格式写法:
// ❌ 错误(文件名虽然是 post.ts,但这种写法在 Nitro v3 不支持)
export default defineHandler({
async post(event, body) { ... },
});
// ✅ 正确(文件名 post.ts 已代表 POST 方法,直接在 handler 内读取 body)
export default defineHandler(async (event) => {
const body = await readBody<MyInput>(event);
return handleXxx(event, body);
});
所有需要调用数据库的服务端工具函数,必须将 event: H3Event 作为第一个参数,并透传给 useDb(event)。
这是 Cloudflare Worker 环境的强制约束——process.env 在 Worker 顶层作用域为空,必须通过 event 透传获取:
// ❌ 错误
export async function getMigrationStats(): Promise<...> {
const db = useDb(); // 类型错误,Cloudflare 环境无法获取 DATABASE_URL
}
// ✅ 正确(透传 event)
export async function getMigrationStats(event: H3Event): Promise<...> {
const db = useDb(event);
// ...
}
// 调用时
export default defineHandler(async (event) => {
const result = await getMigrationStats(event);
});
设计原则:所有 server/utils/ 下的工具函数,只要涉及数据库操作,必须接收 event: H3Event 参数。
Nitro v3 使用的 H3 v2 移除了 event.request 和 event.response,必须使用 nitro/h3 提供的函数:
// ❌ 旧写法(H3 v1)
const ip = event.request.headers.get("x-forwarded-for");
event.response.headers.set("X-RateLimit-Limit", "100");
// ✅ 新写法(H3 v2)
import { getRequestHeader, setResponseHeader } from "nitro/h3";
const ip = getRequestHeader(event, "x-forwarded-for");
setResponseHeader(event, "X-RateLimit-Limit", "100");
Nitro 插件的 hook 回调接收的是原始 HTTPEvent,需要强制转换为 H3Event:
import type { H3Event } from "nitro/h3";
nitroApp.hooks.hook("request", async (rawEvent) => {
const event = rawEvent as H3Event; // 必须强制转换
const path = event.path;
});
当 update().set() 遇到跨包类型不匹配时,可接受使用 as any 进行类型断言:
// 可接受
await db
.update(smStaff)
.set({ neonAuthId } as any)
.where(eq(smStaff.id, id));
// ✅ 正确
import type { JsonVO } from "@01s-11comm/type";
// ❌ 错误 - 会缺少 success 字段,导致前端解析异常
import type { JsonVO } from "@ruan-cat/utils/vueuse";
import { createAuthClient } from "@neondatabase/auth";
import type { NeonAuthPublicApi } from "@neondatabase/auth";
export type AuthClientType = NeonAuthPublicApi<any>;
当 readValidatedBody 的类型推导不足以满足 Drizzle values() 的严格类型要求时,必须显式回填 Insert 类型。
@01s-11comm/type 导出的 New<Entity> 类型readValidatedBody<NewX> 泛型写法const body = (await readValidatedBody(event, insertSchema.parse)) as unknown as NewX;
const result = await db.insert(table).values(body).returning();
本节是本项目在 Cloudflare Worker 环境下排查真实严重 Bug 后沉淀的核心经验。所有涉及数据库连接的代码都必须遵守本节规范。
useDb(event) 获取数据库实例严禁在模块顶层或全局作用域直接创建 Drizzle 数据库连接实例:
// ❌ 错误:模块顶层创建,Cloudflare Worker 环境下 process.env 为空
const db = drizzle(neon(process.env.DATABASE_URL!));
// ✅ 正确:在每个 handler 内通过 event 动态获取
export default defineHandler(async (event) => {
const db = useDb(event); // 内部自动处理多平台环境变量
return await db.select().from(table);
});
在 Nitro v3 + Cloudflare Worker 环境中,event.context.cloudflare.env 不存在。
正确路径必须是 event.req.runtime?.cloudflare?.env(Nitro v3 官方确认路径)。
生产环境真实日志验证:
"req.runtime exists": true,
"req.runtime keys": ["name", "cloudflare"],
"req.runtime.cloudflare.env keys": ["NITRO_DATABASE_URL", "comm_admin_11__DATABASE_URL", "ASSETS"]
使用 cloudflare:workers 动态导入时,必须在 nitro.config.ts 中声明 external,
否则 Vite 在 vite:build:prod 阶段会因无法解析该 CF 专属运行时模块而构建失败:
// nitro.config.ts
export default defineConfig({
rollupConfig: {
external: ["cloudflare:workers"],
},
});
详细内容请参考:cloudflare-env-database.md