28b - API设计与生成
本文是《AI Agent 实战手册》第 28 章第 2 节。 上一节:28a-AI辅助后端开发概览 | 下一节:28c-Spec-Driven业务逻辑
概述
API 是后端系统的”门面”——它定义了前端、移动端、第三方服务与后端交互的契约。在 AI 辅助开发时代,API 设计与生成正在从”手动编写路由和控制器”演进为”Schema-first 设计 + AI 自动生成实现代码”的工作流。本节系统覆盖 REST、GraphQL、tRPC 三种主流 API 范式的 AI 辅助设计与生成方法,包括 Schema-first 设计方法论、输入验证中间件(Zod/Pydantic/Joi)、OpenAPI 文档自动生成,以及 API 版本管理策略。
1. API 范式选择:REST vs GraphQL vs tRPC
1.1 三种范式的核心差异
在开始 AI 辅助 API 设计之前,首先需要理解三种主流范式的本质区别和适用场景。
| 维度 | REST | GraphQL | tRPC |
|---|---|---|---|
| 协议基础 | HTTP 方法 + URL 路径 | 单一端点 + 查询语言 | TypeScript 函数调用 |
| 数据获取 | 固定端点返回固定结构 | 客户端按需查询字段 | 类型推断自动同步 |
| 类型安全 | 需要额外工具(OpenAPI) | Schema 内置类型系统 | TypeScript 原生类型推断 |
| 学习曲线 | 低(HTTP 基础即可) | 中(需学习查询语言) | 低(TypeScript 开发者) |
| 生态成熟度 | 极高(行业标准 20+ 年) | 高(2015 年发布,广泛采用) | 中(2021 年发布,快速增长) |
| AI 生成质量 | ⭐⭐⭐⭐⭐(训练数据极丰富) | ⭐⭐⭐⭐(模式化程度高) | ⭐⭐⭐⭐(类型系统辅助) |
| 适用团队 | 任何团队 | 前后端分离的中大型团队 | 全栈 TypeScript 团队 |
| 跨语言支持 | 所有语言 | 所有语言(需 SDK) | 仅 TypeScript/JavaScript |
| 缓存友好度 | 极高(HTTP 缓存原生支持) | 低(POST 请求为主) | 中(需额外配置) |
| 实时能力 | 需要 WebSocket/SSE 补充 | Subscription 内置支持 | 需要额外配置 |
1.2 AI 辅助选择决策树
你的项目需要什么类型的 API?
│
├── 对外公开 API(第三方集成)
│ └── REST + OpenAPI ← AI 生成 OpenAPI 规范 + 实现代码
│
├── 前后端分离(多客户端:Web + Mobile + 第三方)
│ ├── 数据关系复杂,客户端需要灵活查询?
│ │ └── GraphQL ← AI 生成 Schema + Resolver
│ └── 数据结构相对固定?
│ └── REST ← AI 生成 RESTful 端点
│
├── 全栈 TypeScript 项目(Next.js / Nuxt / SvelteKit)
│ ├── 团队全部使用 TypeScript?
│ │ └── tRPC ← AI 生成 Router + Procedure
│ └── 团队有非 TypeScript 成员?
│ └── REST 或 GraphQL
│
└── 微服务间通信
├── 高性能要求?
│ └── gRPC(不在本节范围,见微服务章节)
└── 标准 HTTP 通信?
└── REST + OpenAPI1.3 混合使用策略
实际项目中,三种范式可以混合使用:
┌─────────────────────────────────────────────┐
│ 典型混合架构 │
├─────────────────────────────────────────────┤
│ │
│ 外部 API(第三方集成) │
│ └── REST + OpenAPI 3.1 │
│ │
│ Web 前端 ↔ BFF(Backend for Frontend) │
│ └── tRPC(全栈 TypeScript 类型安全) │
│ │
│ Mobile App ↔ API Gateway │
│ └── GraphQL(灵活数据查询) │
│ │
│ 微服务间通信 │
│ └── gRPC / REST(内部服务) │
│ │
└─────────────────────────────────────────────┘工具推荐:API 设计与生成工具
| 工具 | 用途 | 支持范式 | 价格 | 适用场景 |
|---|---|---|---|---|
| Swagger Editor / SwaggerHub | OpenAPI 规范编辑与协作 | REST | 免费(Editor)/ $95/月起(Hub) | 团队协作的 API 设计 |
| Stoplight Studio | 可视化 API 设计 | REST (OpenAPI) | 免费(个人)/ $99/月(Team) | 非技术人员参与的 API 设计 |
| Apidog | API 设计、Mock、测试、文档一体化 | REST / GraphQL | 免费 / $9/月(Basic) | API 全生命周期管理 |
| Postman | API 开发与测试平台 | REST / GraphQL / gRPC | 免费 / $14/月(Basic) | API 测试和文档自动化 |
| Apollo Studio | GraphQL Schema 管理与监控 | GraphQL | 免费 / $59/月(Team) | GraphQL API 的生产监控 |
| GraphQL Code Generator | 从 Schema 生成类型和 SDK | GraphQL | 免费(开源) | GraphQL 客户端/服务端代码生成 |
| openapi-generator | 从 OpenAPI 规范生成客户端/服务端代码 | REST | 免费(开源) | 多语言 API 客户端生成 |
| Orval | 从 OpenAPI 生成 TypeScript 客户端 | REST | 免费(开源) | TypeScript 前端的 API 客户端 |
| tRPC | 端到端类型安全 API 框架 | tRPC | 免费(开源) | 全栈 TypeScript 项目 |
| Hono | 轻量级 Web 框架(支持 RPC 模式) | REST / RPC | 免费(开源) | Serverless 和边缘计算 |
| Scalar | OpenAPI 文档渲染与交互 | REST | 免费(开源)/ 按需定价(Cloud) | 美观的 API 文档站点 |
| Redocly | OpenAPI 文档生成与 lint | REST | 免费(CLI)/ $69/月(Starter) | 企业级 API 文档 |
| Theneo | AI 驱动的 API 文档生成 | REST / GraphQL | 免费 / $120/月(Startup) | 从 Postman/OpenAPI 生成文档 |
| express-zod-api | Zod 驱动的 Express API 框架 | REST | 免费(开源) | Zod-first 的 Express API 开发 |
2. Schema-First 设计方法论
2.1 什么是 Schema-First 设计
Schema-First(契约优先)是一种 API 设计方法论,核心思想是:先定义 API 契约(Schema),再生成实现代码。与之相对的是 Code-First(代码优先),即先写代码,再从代码生成文档。
┌─────────────────────────────────────────────────────┐
│ Schema-First vs Code-First 对比 │
├─────────────────────────────────────────────────────┤
│ │
│ Schema-First(推荐用于 AI 辅助开发) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ API 契约 │ → │ AI 生成 │ → │ 实现代码 │ │
│ │ (Schema) │ │ 代码骨架 │ │ + 测试 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ 优点:一致性高、前后端可并行、AI 生成质量高 │
│ │
│ Code-First(传统方式) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 写实现 │ → │ 提取注解 │ → │ 生成文档 │ │
│ │ 代码 │ │ /装饰器 │ │ (Schema) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ 优点:开发速度快、适合快速原型 │
│ │
└─────────────────────────────────────────────────────┘2.2 为什么 Schema-First 更适合 AI 辅助开发
- AI 生成的一致性更高:有了明确的 Schema,AI 生成的代码严格遵循契约,不会出现”不同端点风格不一致”的问题
- 前后端可并行开发:Schema 定义完成后,前端可以基于 Schema 生成 Mock 数据和类型定义,后端同时实现 API
- 自动化验证:Schema 可以自动生成输入验证、类型检查、文档,减少手动维护成本
- 变更可追踪:Schema 文件纳入版本控制,API 变更有明确的 diff 记录
- AI 的”单一事实来源”:Schema 作为 AI 的上下文输入,确保生成的代码与设计一致
2.3 三种范式的 Schema-First 实践
REST:OpenAPI 3.1 Schema-First
OpenAPI 3.1 是 REST API 的行业标准规范格式,完全兼容 JSON Schema 2020-12。
AI 辅助 OpenAPI Schema 设计的提示词模板:
请为以下业务需求设计 OpenAPI 3.1 规范:
## 业务需求
[描述 API 的业务目的和核心功能]
## 资源列表
[列出核心资源/实体,如 User, Product, Order]
## 设计约束
- API 基础路径:/api/v1
- 认证方式:Bearer Token (JWT)
- 分页方式:cursor-based(使用 cursor + limit 参数)
- 错误响应格式:RFC 7807 Problem Details
- 日期格式:ISO 8601
- ID 格式:CUID2
## 要求
- 每个资源包含 CRUD 端点
- 使用 [占位符] 标记需要替换的部分
- 包含请求/响应 Schema 定义(使用 $ref 引用 components)
- 包含认证安全方案定义
- 包含分页、过滤、排序的查询参数
- 包含标准错误响应(400, 401, 403, 404, 409, 422, 500)AI 生成的 OpenAPI Schema 示例(部分):
openapi: 3.1.0
info:
title: 项目管理 API
version: 1.0.0
description: 多租户项目管理 SaaS 的 RESTful API
servers:
- url: https://api.example.com/v1
description: 生产环境
security:
- BearerAuth: []
paths:
/projects:
get:
operationId: listProjects
summary: 获取项目列表
tags: [Projects]
parameters:
- $ref: '#/components/parameters/CursorParam'
- $ref: '#/components/parameters/LimitParam'
- name: status
in: query
schema:
$ref: '#/components/schemas/ProjectStatus'
responses:
'200':
description: 项目列表
content:
application/json:
schema:
$ref: '#/components/schemas/ProjectListResponse'
'401':
$ref: '#/components/responses/Unauthorized'
post:
operationId: createProject
summary: 创建项目
tags: [Projects]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateProjectRequest'
responses:
'201':
description: 项目创建成功
content:
application/json:
schema:
$ref: '#/components/schemas/ProjectResponse'
'400':
$ref: '#/components/responses/BadRequest'
'422':
$ref: '#/components/responses/ValidationError'
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
ProjectStatus:
type: string
enum: [active, archived, deleted]
CreateProjectRequest:
type: object
required: [name]
properties:
name:
type: string
minLength: 1
maxLength: 100
description:
type: string
maxLength: 2000
ProjectResponse:
type: object
properties:
id:
type: string
name:
type: string
description:
type: string
nullable: true
status:
$ref: '#/components/schemas/ProjectStatus'
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
ProjectListResponse:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/ProjectResponse'
pagination:
$ref: '#/components/schemas/CursorPagination'
CursorPagination:
type: object
properties:
nextCursor:
type: string
nullable: true
hasMore:
type: boolean
ProblemDetail:
type: object
description: RFC 7807 Problem Details
properties:
type:
type: string
format: uri
title:
type: string
status:
type: integer
detail:
type: string
instance:
type: string
format: uri
parameters:
CursorParam:
name: cursor
in: query
schema:
type: string
LimitParam:
name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
responses:
BadRequest:
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
Unauthorized:
description: 未认证
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'
ValidationError:
description: 输入验证失败
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetail'GraphQL:Schema-First 设计
GraphQL 天然是 Schema-First 的——SDL(Schema Definition Language)就是 API 契约。
AI 辅助 GraphQL Schema 设计的提示词模板:
请为以下业务需求设计 GraphQL Schema(SDL 格式):
## 业务需求
[描述业务领域和核心实体]
## 设计约束
- 使用 Relay 风格的连接分页(Connection/Edge/Node)
- 使用 Input 类型封装变更参数
- 错误处理使用 Union 类型(Result Pattern)
- 包含 Subscription 定义(如需实时功能)
## 要求
- 定义所有 Query、Mutation、Subscription
- 使用自定义标量类型(DateTime, Email, URL)
- 包含权限指令(@auth)
- 包含输入验证指令(@constraint)
- 遵循 GraphQL 命名约定(camelCase 字段,PascalCase 类型)AI 生成的 GraphQL Schema 示例:
# schema.graphql — AI 生成的项目管理 GraphQL Schema
# 自定义标量
scalar DateTime
scalar Email
scalar URL
# 权限指令
directive @auth(requires: Role!) on FIELD_DEFINITION
# 枚举类型
enum Role {
OWNER
ADMIN
MEMBER
VIEWER
}
enum TaskStatus {
TODO
IN_PROGRESS
IN_REVIEW
DONE
CANCELLED
}
enum Priority {
LOW
MEDIUM
HIGH
URGENT
}
enum SortDirection {
ASC
DESC
}
# 接口
interface Node {
id: ID!
}
interface Timestamped {
createdAt: DateTime!
updatedAt: DateTime!
}
# 核心类型
type User implements Node & Timestamped {
id: ID!
email: Email!
name: String!
avatarUrl: URL
createdAt: DateTime!
updatedAt: DateTime!
memberships: [Membership!]!
}
type Project implements Node & Timestamped {
id: ID!
name: String!
description: String
archived: Boolean!
createdAt: DateTime!
updatedAt: DateTime!
tasks(
first: Int
after: String
filter: TaskFilterInput
sort: TaskSortInput
): TaskConnection!
taskCount: Int!
}
type Task implements Node & Timestamped {
id: ID!
title: String!
description: String
status: TaskStatus!
priority: Priority!
dueDate: DateTime
assignee: User
creator: User!
project: Project!
createdAt: DateTime!
updatedAt: DateTime!
}
type Membership {
id: ID!
role: Role!
user: User!
organization: Organization!
}
type Organization implements Node & Timestamped {
id: ID!
name: String!
slug: String!
createdAt: DateTime!
updatedAt: DateTime!
members: [Membership!]!
projects(first: Int, after: String): ProjectConnection!
}
# Relay 风格分页
type TaskConnection {
edges: [TaskEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type TaskEdge {
node: Task!
cursor: String!
}
type ProjectConnection {
edges: [ProjectEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type ProjectEdge {
node: Project!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# 输入类型
input CreateProjectInput {
name: String!
description: String
}
input UpdateProjectInput {
name: String
description: String
archived: Boolean
}
input CreateTaskInput {
title: String!
description: String
priority: Priority = MEDIUM
assigneeId: ID
dueDate: DateTime
}
input UpdateTaskInput {
title: String
description: String
status: TaskStatus
priority: Priority
assigneeId: ID
dueDate: DateTime
}
input TaskFilterInput {
status: [TaskStatus!]
priority: [Priority!]
assigneeId: ID
}
input TaskSortInput {
field: TaskSortField!
direction: SortDirection!
}
enum TaskSortField {
CREATED_AT
UPDATED_AT
PRIORITY
DUE_DATE
}
# Result Pattern(错误处理)
union CreateProjectResult = Project | ValidationError | AuthorizationError
union UpdateTaskResult = Task | ValidationError | NotFoundError | AuthorizationError
type ValidationError {
message: String!
field: String
code: String!
}
type NotFoundError {
message: String!
resourceType: String!
resourceId: ID!
}
type AuthorizationError {
message: String!
requiredRole: Role
}
# 查询
type Query {
# 当前用户
me: User! @auth(requires: VIEWER)
# 组织
organization(slug: String!): Organization @auth(requires: VIEWER)
# 项目
project(id: ID!): Project @auth(requires: VIEWER)
# 任务
task(id: ID!): Task @auth(requires: VIEWER)
# Node 接口查询
node(id: ID!): Node
}
# 变更
type Mutation {
# 项目
createProject(
organizationId: ID!
input: CreateProjectInput!
): CreateProjectResult! @auth(requires: MEMBER)
updateProject(
id: ID!
input: UpdateProjectInput!
): Project! @auth(requires: ADMIN)
deleteProject(id: ID!): Boolean! @auth(requires: OWNER)
# 任务
createTask(
projectId: ID!
input: CreateTaskInput!
): Task! @auth(requires: MEMBER)
updateTask(
id: ID!
input: UpdateTaskInput!
): UpdateTaskResult! @auth(requires: MEMBER)
deleteTask(id: ID!): Boolean! @auth(requires: ADMIN)
}
# 订阅
type Subscription {
taskUpdated(projectId: ID!): Task! @auth(requires: VIEWER)
taskCreated(projectId: ID!): Task! @auth(requires: VIEWER)
}tRPC:类型即契约
tRPC 的独特之处在于不需要单独的 Schema 文件——TypeScript 类型本身就是契约。tRPC v11(2025 年 3 月正式发布)引入了多项改进,包括更好的中间件类型推断和标准 HTTP 支持。
tRPC 的 Schema-First 等价物:先定义 Zod Schema,再构建 Router。
// schemas/task.ts — 先定义验证 Schema(这就是 tRPC 的"契约")
import { z } from 'zod';
export const TaskStatus = z.enum([
'TODO', 'IN_PROGRESS', 'IN_REVIEW', 'DONE', 'CANCELLED'
]);
export const Priority = z.enum(['LOW', 'MEDIUM', 'HIGH', 'URGENT']);
export const CreateTaskSchema = z.object({
title: z.string().min(1).max(200),
description: z.string().max(5000).optional(),
priority: Priority.default('MEDIUM'),
assigneeId: z.string().cuid2().optional(),
dueDate: z.coerce.date().optional(),
});
export const UpdateTaskSchema = z.object({
title: z.string().min(1).max(200).optional(),
description: z.string().max(5000).optional(),
status: TaskStatus.optional(),
priority: Priority.optional(),
assigneeId: z.string().cuid2().nullable().optional(),
dueDate: z.coerce.date().nullable().optional(),
});
export const TaskFilterSchema = z.object({
status: z.array(TaskStatus).optional(),
priority: z.array(Priority).optional(),
assigneeId: z.string().cuid2().optional(),
cursor: z.string().optional(),
limit: z.number().int().min(1).max(100).default(20),
});
// 类型自动从 Schema 推断
export type CreateTaskInput = z.infer<typeof CreateTaskSchema>;
export type UpdateTaskInput = z.infer<typeof UpdateTaskSchema>;
export type TaskFilter = z.infer<typeof TaskFilterSchema>;// routers/task.ts — 基于 Schema 构建 tRPC Router
import { router, protectedProcedure } from '../trpc';
import {
CreateTaskSchema,
UpdateTaskSchema,
TaskFilterSchema,
} from '../schemas/task';
import { z } from 'zod';
export const taskRouter = router({
list: protectedProcedure
.input(TaskFilterSchema)
.query(async ({ ctx, input }) => {
const tasks = await ctx.db.task.findMany({
where: {
projectId: ctx.projectId,
...(input.status && { status: { in: input.status } }),
...(input.priority && { priority: { in: input.priority } }),
...(input.assigneeId && { assigneeId: input.assigneeId }),
},
take: input.limit + 1,
...(input.cursor && {
cursor: { id: input.cursor },
skip: 1,
}),
orderBy: { createdAt: 'desc' },
});
const hasMore = tasks.length > input.limit;
const data = hasMore ? tasks.slice(0, -1) : tasks;
return {
data,
nextCursor: hasMore ? data[data.length - 1]?.id : null,
hasMore,
};
}),
create: protectedProcedure
.input(CreateTaskSchema)
.mutation(async ({ ctx, input }) => {
return ctx.db.task.create({
data: {
...input,
projectId: ctx.projectId,
creatorId: ctx.user.id,
},
});
}),
update: protectedProcedure
.input(z.object({
id: z.string().cuid2(),
data: UpdateTaskSchema,
}))
.mutation(async ({ ctx, input }) => {
return ctx.db.task.update({
where: { id: input.id },
data: input.data,
});
}),
delete: protectedProcedure
.input(z.object({ id: z.string().cuid2() }))
.mutation(async ({ ctx, input }) => {
await ctx.db.task.delete({ where: { id: input.id } });
return { success: true };
}),
});3. REST API 设计与 AI 生成
3.1 RESTful API 设计原则(AI 友好版)
以下原则不仅是好的 REST 设计实践,也是让 AI 生成更一致代码的关键约束:
| 原则 | 说明 | AI 友好度 | Steering 规则建议 |
|---|---|---|---|
| 资源命名用复数名词 | /users, /projects, /tasks | ⭐⭐⭐⭐⭐ | “URL 路径使用复数名词,禁止动词” |
| HTTP 方法语义化 | GET 读取、POST 创建、PUT/PATCH 更新、DELETE 删除 | ⭐⭐⭐⭐⭐ | “严格遵循 HTTP 方法语义” |
| 嵌套资源表示关系 | /projects/:projectId/tasks | ⭐⭐⭐⭐ | “嵌套不超过 2 层” |
| 统一错误响应格式 | RFC 7807 Problem Details | ⭐⭐⭐⭐ | “所有错误响应使用 ProblemDetail 格式” |
| Cursor 分页 | ?cursor=xxx&limit=20 | ⭐⭐⭐⭐ | “列表端点必须支持分页” |
| 版本化 | /api/v1/... | ⭐⭐⭐⭐⭐ | “所有端点包含版本前缀” |
| HATEOAS(可选) | 响应中包含相关资源链接 | ⭐⭐⭐ | 仅在公开 API 中使用 |
3.2 AI 生成 REST API 的操作步骤
步骤 1:定义 OpenAPI Schema
使用 AI 生成 OpenAPI 3.1 规范文件(见 2.3 节的模板)。
步骤 2:从 Schema 生成服务端代码
方法 A:使用 openapi-generator(多语言支持)
# 安装 openapi-generator
npm install @openapitools/openapi-generator-cli -g
# 从 OpenAPI 规范生成 NestJS 服务端代码
openapi-generator-cli generate \
-i openapi.yaml \
-g typescript-nestjs \
-o src/generated \
--additional-properties=useTags=true
# 从 OpenAPI 规范生成 FastAPI 服务端代码
openapi-generator-cli generate \
-i openapi.yaml \
-g python-fastapi \
-o src/generated方法 B:使用 AI Agent 直接生成(推荐)
基于以下 OpenAPI 规范,为每个端点生成 NestJS 的 Controller + Service + DTO:
[粘贴 OpenAPI YAML 或指定文件路径]
## 生成要求
- Controller 使用 @nestjs/swagger 装饰器(与 OpenAPI 规范一致)
- DTO 使用 class-validator + class-transformer 装饰器
- Service 使用 Prisma Client 操作数据库
- 包含统一的异常过滤器(映射到 RFC 7807 格式)
- 每个 Service 方法包含日志记录
- 生成对应的单元测试文件方法 C:Zod-first 生成(TypeScript 推荐)
使用 express-zod-api 或类似框架,从 Zod Schema 同时生成路由、验证和 OpenAPI 文档:
// 使用 express-zod-api 的 Schema-First 方式
import { createConfig, Routing, EndpointsFactory } from 'express-zod-api';
import { z } from 'zod';
const config = createConfig({
server: { listen: 3000 },
cors: true,
logger: { level: 'info' },
});
const endpointsFactory = new EndpointsFactory(defaultResultHandler);
// 定义端点 — Zod Schema 同时用于验证和文档生成
const createTaskEndpoint = endpointsFactory.build({
method: 'post',
input: z.object({
title: z.string().min(1).max(200),
description: z.string().max(5000).optional(),
priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'URGENT']).default('MEDIUM'),
}),
output: z.object({
id: z.string(),
title: z.string(),
status: z.string(),
createdAt: z.string(),
}),
handler: async ({ input }) => {
const task = await prisma.task.create({ data: input });
return task;
},
});
// 路由定义
const routing: Routing = {
v1: {
tasks: {
'': createTaskEndpoint,
},
},
};步骤 3:生成客户端 SDK
# 使用 Orval 从 OpenAPI 生成 TypeScript 客户端(React Query 集成)
npx orval --input openapi.yaml --output src/api/generated
# 使用 openapi-typescript 生成类型定义
npx openapi-typescript openapi.yaml -o src/api/types.ts3.3 REST API 的 AI 生成提示词模板
模板:完整 CRUD 端点生成
请为 [资源名称] 资源生成完整的 RESTful CRUD API:
## 技术栈
- 框架:[NestJS / FastAPI / Express / Gin]
- ORM:[Prisma / SQLAlchemy / TypeORM / GORM]
- 验证:[Zod / Pydantic / class-validator / Joi]
- 数据库:[PostgreSQL / MySQL]
## 资源定义
[描述资源的字段、类型、约束]
## API 端点
- GET /api/v1/[资源复数] — 列表(支持分页、过滤、排序)
- GET /api/v1/[资源复数]/:id — 详情
- POST /api/v1/[资源复数] — 创建
- PATCH /api/v1/[资源复数]/:id — 部分更新
- DELETE /api/v1/[资源复数]/:id — 删除
## 必须包含
1. 输入验证 Schema(创建和更新分开定义)
2. 统一错误响应(RFC 7807 格式)
3. Cursor 分页(cursor + limit)
4. 过滤参数(按 [指定字段] 过滤)
5. 排序参数(sortBy + sortDirection)
6. 认证守卫(JWT Bearer Token)
7. 权限检查(基于角色)
8. 请求日志中间件
9. 单元测试(覆盖正常路径和错误路径)
## 禁止
- 禁止使用 SELECT *
- 禁止在响应中暴露内部错误堆栈
- 禁止硬编码任何密钥或配置值4. GraphQL API 设计与 AI 辅助
4.1 GraphQL Schema 设计最佳实践
| 实践 | 说明 | AI 提示建议 |
|---|---|---|
| Relay 风格分页 | 使用 Connection/Edge/Node 模式 | ”列表字段使用 Relay Connection 分页” |
| Input 类型封装 | Mutation 参数使用 Input 类型 | ”所有 Mutation 参数使用 Input 类型” |
| Result Union | 错误处理使用 Union 类型 | ”Mutation 返回 Result Union(成功类型 | 错误类型)“ |
| 接口复用 | 使用 interface 定义共享字段 | ”使用 Node 和 Timestamped 接口” |
| 自定义标量 | DateTime、Email、URL 等 | ”定义 DateTime、Email 自定义标量” |
| N+1 防护 | 使用 DataLoader 批量加载 | ”所有关联字段使用 DataLoader” |
| 深度限制 | 限制查询嵌套深度 | ”查询深度限制为 5 层” |
| 复杂度分析 | 限制查询复杂度 | ”查询复杂度上限为 1000” |
4.2 AI 生成 GraphQL Resolver 的操作步骤
步骤 1:从 Schema 生成类型和 Resolver 骨架
使用 GraphQL Code Generator 从 SDL 生成 TypeScript 类型:
# 安装 GraphQL Code Generator
npm install -D @graphql-codegen/cli @graphql-codegen/typescript \
@graphql-codegen/typescript-resolvers
# codegen.ts 配置// codegen.ts
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'src/schema.graphql',
generates: {
'src/generated/types.ts': {
plugins: [
'typescript',
'typescript-resolvers',
],
config: {
contextType: '../context#GraphQLContext',
mappers: {
User: '../models#UserModel',
Project: '../models#ProjectModel',
Task: '../models#TaskModel',
},
useIndexSignature: true,
},
},
},
};
export default config;# 生成类型
npx graphql-codegen步骤 2:AI 生成 Resolver 实现
基于以下 GraphQL Schema 和生成的 TypeScript 类型,实现 Resolver:
## Schema 文件
[粘贴 schema.graphql 内容]
## 生成的类型文件
[粘贴 generated/types.ts 内容]
## 技术栈
- 运行时:Node.js + TypeScript
- GraphQL 服务器:Apollo Server 4 / Yoga
- ORM:Prisma
- 认证:JWT(通过 context 传递)
## 要求
- 所有关联字段使用 DataLoader 批量加载(防止 N+1)
- Mutation 包含输入验证(使用 Zod)
- 错误处理使用 Result Union 模式
- 包含权限检查(基于 @auth 指令)
- 分页使用 Relay Connection 规范
- 包含查询复杂度分析中间件AI 生成的 Resolver 示例:
// resolvers/task.ts — AI 生成的 Task Resolver
import { Resolvers } from '../generated/types';
import { GraphQLContext } from '../context';
import { CreateTaskSchema, UpdateTaskSchema } from '../schemas/task';
import { GraphQLError } from 'graphql';
export const taskResolvers: Resolvers<GraphQLContext> = {
Query: {
task: async (_parent, { id }, ctx) => {
const task = await ctx.prisma.task.findUnique({
where: { id },
});
if (!task) {
throw new GraphQLError('Task not found', {
extensions: { code: 'NOT_FOUND' },
});
}
return task;
},
},
Mutation: {
createTask: async (_parent, { projectId, input }, ctx) => {
// 输入验证
const validated = CreateTaskSchema.safeParse(input);
if (!validated.success) {
return {
__typename: 'ValidationError',
message: validated.error.issues[0].message,
field: validated.error.issues[0].path.join('.'),
code: 'VALIDATION_ERROR',
};
}
// 权限检查
const membership = await ctx.prisma.membership.findFirst({
where: {
userId: ctx.user.id,
organization: { projects: { some: { id: projectId } } },
role: { in: ['OWNER', 'ADMIN', 'MEMBER'] },
},
});
if (!membership) {
return {
__typename: 'AuthorizationError',
message: 'Insufficient permissions',
requiredRole: 'MEMBER',
};
}
const task = await ctx.prisma.task.create({
data: {
...validated.data,
projectId,
creatorId: ctx.user.id,
},
});
return { __typename: 'Task', ...task };
},
},
Task: {
// DataLoader 批量加载关联
assignee: (task, _args, ctx) => {
if (!task.assigneeId) return null;
return ctx.loaders.userLoader.load(task.assigneeId);
},
creator: (task, _args, ctx) => {
return ctx.loaders.userLoader.load(task.creatorId);
},
project: (task, _args, ctx) => {
return ctx.loaders.projectLoader.load(task.projectId);
},
},
// Result Union 类型解析
CreateProjectResult: {
__resolveType(obj) {
if ('name' in obj && 'id' in obj) return 'Project';
if ('field' in obj) return 'ValidationError';
if ('requiredRole' in obj) return 'AuthorizationError';
return null;
},
},
};4.3 GraphQL 的 AI 生成提示词模板
请为以下 GraphQL Schema 生成完整的服务端实现:
## Schema
[粘贴 SDL]
## 技术栈
- GraphQL 服务器:[Apollo Server 4 / GraphQL Yoga / Mercurius]
- ORM:[Prisma / TypeORM / Drizzle]
- 验证:Zod
## 必须包含
1. DataLoader 工厂函数(防止 N+1 查询)
2. Relay Connection 分页辅助函数
3. 输入验证(Zod Schema)
4. Result Union 错误处理
5. 权限中间件(基于 @auth 指令)
6. 查询深度限制(最大 [N] 层)
7. 查询复杂度分析(最大 [N] 点)
8. 上下文工厂函数(包含认证、DataLoader、Prisma)5. tRPC 端到端类型安全
5.1 tRPC v11 核心特性
tRPC v11 于 2025 年 3 月正式发布,带来了多项重要改进:
| 特性 | 说明 | AI 辅助价值 |
|---|---|---|
| 标准 HTTP 支持 | 支持标准 HTTP 方法和路径 | AI 可以生成符合 REST 风格的 tRPC 路由 |
| 改进的中间件类型 | 中间件链的类型推断更精确 | AI 生成的中间件代码类型错误更少 |
| FormData 支持 | 原生支持文件上传 | AI 可以生成文件上传端点 |
| Server-Sent Events | 内置 SSE 支持 | AI 可以生成实时推送端点 |
| 更好的错误处理 | 类型安全的错误类型 | AI 生成的错误处理更精确 |
| React Server Components | 与 RSC 深度集成 | AI 可以生成 RSC 友好的数据获取 |
5.2 tRPC 项目结构(AI 友好)
src/
├── server/
│ ├── trpc.ts # tRPC 初始化(context, middleware)
│ ├── routers/
│ │ ├── _app.ts # 根 Router(合并所有子 Router)
│ │ ├── auth.ts # 认证相关
│ │ ├── user.ts # 用户管理
│ │ ├── project.ts # 项目管理
│ │ └── task.ts # 任务管理
│ ├── schemas/ # Zod Schema(共享验证逻辑)
│ │ ├── auth.ts
│ │ ├── user.ts
│ │ ├── project.ts
│ │ └── task.ts
│ └── services/ # 业务逻辑层
│ ├── auth.service.ts
│ ├── user.service.ts
│ ├── project.service.ts
│ └── task.service.ts
├── client/
│ └── trpc.ts # tRPC 客户端配置
└── shared/
└── types.ts # 共享类型(从 Zod Schema 推断)5.3 tRPC 的 AI 生成操作步骤
步骤 1:初始化 tRPC 配置
// server/trpc.ts — tRPC 初始化
import { initTRPC, TRPCError } from '@trpc/server';
import { type CreateNextContextOptions } from '@trpc/server/adapters/next';
import superjson from 'superjson';
import { ZodError } from 'zod';
import { prisma } from '../lib/prisma';
import { verifyToken } from '../lib/auth';
// Context 创建
export const createTRPCContext = async (opts: CreateNextContextOptions) => {
const token = opts.req.headers.authorization?.replace('Bearer ', '');
const user = token ? await verifyToken(token) : null;
return {
prisma,
user,
req: opts.req,
};
};
type Context = Awaited<ReturnType<typeof createTRPCContext>>;
// tRPC 初始化
const t = initTRPC.context<Context>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError
? error.cause.flatten()
: null,
},
};
},
});
// 导出
export const router = t.router;
export const publicProcedure = t.procedure;
export const createCallerFactory = t.createCallerFactory;
// 认证中间件
const enforceAuth = t.middleware(({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({
ctx: { ...ctx, user: ctx.user },
});
});
export const protectedProcedure = t.procedure.use(enforceAuth);
// 日志中间件
const loggerMiddleware = t.middleware(async ({ path, type, next }) => {
const start = Date.now();
const result = await next();
const duration = Date.now() - start;
console.log(`[tRPC] ${type} ${path} - ${duration}ms`);
return result;
});
export const loggedProcedure = t.procedure.use(loggerMiddleware);步骤 2:AI 生成 Router
提示词模板:
请基于以下 Zod Schema 生成 tRPC Router:
## Zod Schema
[粘贴 schemas/ 目录下的 Schema 定义]
## 技术栈
- tRPC v11
- Prisma ORM
- Zod 验证
- SuperJSON 序列化
## 要求
- 使用 protectedProcedure(需要认证的端点)
- 列表查询支持 cursor 分页
- 包含输入验证(使用已定义的 Zod Schema)
- 包含权限检查中间件
- 错误处理使用 TRPCError
- 业务逻辑封装在 Service 层
- 包含乐观更新支持(返回更新后的完整对象)步骤 3:客户端集成
// client/trpc.ts — Next.js 客户端配置
import { createTRPCReact } from '@trpc/react-query';
import { httpBatchLink } from '@trpc/client';
import superjson from 'superjson';
import type { AppRouter } from '../server/routers/_app';
export const trpc = createTRPCReact<AppRouter>();
export function getTRPCClient() {
return trpc.createClient({
links: [
httpBatchLink({
url: '/api/trpc',
transformer: superjson,
headers() {
return {
authorization: `Bearer ${getToken()}`,
};
},
}),
],
});
}// 客户端使用 — 完全类型安全,零代码生成
// 修改服务端 Schema → 客户端自动获得类型错误提示
// 列表查询
const { data, fetchNextPage, hasNextPage } =
trpc.task.list.useInfiniteQuery(
{ limit: 20, status: ['TODO', 'IN_PROGRESS'] },
{ getNextPageParam: (lastPage) => lastPage.nextCursor }
);
// 创建任务(带乐观更新)
const utils = trpc.useUtils();
const createTask = trpc.task.create.useMutation({
onSuccess: () => {
utils.task.list.invalidate();
},
});
// 调用 — 参数类型自动推断,拼写错误立即报错
createTask.mutate({
title: '实现用户注册 API',
priority: 'HIGH', // 类型安全:只能是 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT'
});6. 输入验证中间件
6.1 为什么输入验证是 API 安全的第一道防线
输入验证是防止注入攻击、数据损坏和意外行为的最基本也是最重要的安全措施。AI 生成的 API 代码如果缺少输入验证,就像没有锁的门——看起来完整,但毫无防护。
AI 生成代码中常见的验证缺失:
| 缺失类型 | 风险 | 示例 |
|---|---|---|
| 缺少类型验证 | 类型混淆攻击 | 期望 number 收到 string |
| 缺少长度限制 | DoS 攻击(超大 payload) | 无限长度的 description 字段 |
| 缺少格式验证 | 注入攻击 | email 字段接受任意字符串 |
| 缺少范围验证 | 业务逻辑错误 | 负数的 price 或 quantity |
| 缺少枚举验证 | 非法状态 | status 字段接受任意值 |
| 缺少嵌套验证 | 深层注入 | 嵌套对象中的恶意字段 |
6.2 三大验证库对比
| 特性 | Zod (TypeScript) | Pydantic (Python) | Joi (JavaScript) |
|---|---|---|---|
| 语言 | TypeScript/JavaScript | Python | JavaScript/TypeScript |
| 类型推断 | ✅ 自动推断 TS 类型 | ✅ 自动推断 Python 类型 | ❌ 需要手动定义类型 |
| 运行时验证 | ✅ | ✅ | ✅ |
| Schema 复用 | ✅ .extend(), .pick(), .omit() | ✅ 继承、model_validator | ✅ .keys(), .fork() |
| 错误消息 | 可自定义,支持 i18n | 可自定义,详细的错误路径 | 可自定义,丰富的错误模板 |
| 性能 | 快(v3.x 大幅优化) | 极快(Rust 核心,v2.x) | 中等 |
| 生态集成 | tRPC、NestJS、Express、Hono | FastAPI(内置)、Django | Express、Hapi、NestJS |
| OpenAPI 生成 | ✅ zod-to-openapi | ✅ FastAPI 内置 | ✅ joi-to-swagger |
| AI 生成质量 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 推荐指数 | 强烈推荐(TS 项目) | 强烈推荐(Python 项目) | 可用(遗留项目) |
6.3 Zod 验证中间件实战
基础 Schema 定义模式
// schemas/user.ts — Zod Schema 最佳实践
import { z } from 'zod';
// 基础字段定义(可复用)
const emailField = z.string().email('请输入有效的邮箱地址').max(255);
const passwordField = z.string()
.min(8, '密码至少 8 个字符')
.max(128, '密码最多 128 个字符')
.regex(/[A-Z]/, '密码必须包含至少一个大写字母')
.regex(/[a-z]/, '密码必须包含至少一个小写字母')
.regex(/[0-9]/, '密码必须包含至少一个数字');
const nameField = z.string().min(1).max(100).trim();
// 创建用户 Schema
export const CreateUserSchema = z.object({
email: emailField,
password: passwordField,
name: nameField,
});
// 更新用户 Schema(所有字段可选,使用 .partial())
export const UpdateUserSchema = CreateUserSchema
.omit({ password: true }) // 更新时不允许直接修改密码
.partial(); // 所有字段变为可选
// 修改密码 Schema
export const ChangePasswordSchema = z.object({
currentPassword: z.string().min(1),
newPassword: passwordField,
confirmPassword: z.string(),
}).refine(
(data) => data.newPassword === data.confirmPassword,
{ message: '两次输入的密码不一致', path: ['confirmPassword'] }
);
// 查询参数 Schema
export const UserQuerySchema = z.object({
search: z.string().max(100).optional(),
role: z.enum(['ADMIN', 'MEMBER', 'VIEWER']).optional(),
cursor: z.string().cuid2().optional(),
limit: z.coerce.number().int().min(1).max(100).default(20),
sortBy: z.enum(['name', 'email', 'createdAt']).default('createdAt'),
sortDir: z.enum(['asc', 'desc']).default('desc'),
});
// 类型导出(从 Schema 自动推断)
export type CreateUserInput = z.infer<typeof CreateUserSchema>;
export type UpdateUserInput = z.infer<typeof UpdateUserSchema>;
export type UserQuery = z.infer<typeof UserQuerySchema>;Express 验证中间件
// middleware/validate.ts — 通用 Zod 验证中间件
import { Request, Response, NextFunction } from 'express';
import { ZodSchema, ZodError } from 'zod';
type ValidationTarget = 'body' | 'query' | 'params';
interface ValidationConfig {
body?: ZodSchema;
query?: ZodSchema;
params?: ZodSchema;
}
export function validate(config: ValidationConfig) {
return (req: Request, res: Response, next: NextFunction) => {
const errors: Array<{
target: ValidationTarget;
issues: Array<{ path: string; message: string }>;
}> = [];
for (const [target, schema] of Object.entries(config)) {
if (!schema) continue;
const result = schema.safeParse(req[target as ValidationTarget]);
if (!result.success) {
errors.push({
target: target as ValidationTarget,
issues: result.error.issues.map((issue) => ({
path: issue.path.join('.'),
message: issue.message,
})),
});
} else {
// 用验证后的数据替换原始数据(包含默认值和类型转换)
(req as any)[target] = result.data;
}
}
if (errors.length > 0) {
return res.status(422).json({
type: 'https://api.example.com/errors/validation',
title: 'Validation Error',
status: 422,
detail: 'One or more fields failed validation',
errors,
});
}
next();
};
}
// 使用示例
import { CreateUserSchema, UserQuerySchema } from '../schemas/user';
router.post(
'/users',
validate({ body: CreateUserSchema }),
userController.create
);
router.get(
'/users',
validate({ query: UserQuerySchema }),
userController.list
);6.4 Pydantic 验证实战(FastAPI)
# schemas/user.py — Pydantic v2 Schema
from pydantic import BaseModel, EmailStr, Field, field_validator
from typing import Optional
from enum import Enum
from datetime import datetime
import re
class UserRole(str, Enum):
ADMIN = "admin"
MEMBER = "member"
VIEWER = "viewer"
class SortDirection(str, Enum):
ASC = "asc"
DESC = "desc"
class CreateUserRequest(BaseModel):
email: EmailStr = Field(..., max_length=255)
password: str = Field(..., min_length=8, max_length=128)
name: str = Field(..., min_length=1, max_length=100)
@field_validator("password")
@classmethod
def validate_password_strength(cls, v: str) -> str:
if not re.search(r"[A-Z]", v):
raise ValueError("密码必须包含至少一个大写字母")
if not re.search(r"[a-z]", v):
raise ValueError("密码必须包含至少一个小写字母")
if not re.search(r"[0-9]", v):
raise ValueError("密码必须包含至少一个数字")
return v
@field_validator("name")
@classmethod
def strip_name(cls, v: str) -> str:
return v.strip()
class UpdateUserRequest(BaseModel):
name: Optional[str] = Field(None, min_length=1, max_length=100)
email: Optional[EmailStr] = Field(None, max_length=255)
class UserQueryParams(BaseModel):
search: Optional[str] = Field(None, max_length=100)
role: Optional[UserRole] = None
cursor: Optional[str] = None
limit: int = Field(default=20, ge=1, le=100)
sort_by: str = Field(default="created_at")
sort_dir: SortDirection = Field(default=SortDirection.DESC)
class UserResponse(BaseModel):
id: str
email: str
name: str
role: UserRole
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}# routes/user.py — FastAPI 路由(Pydantic 自动验证 + OpenAPI 文档生成)
from fastapi import APIRouter, Depends, HTTPException, Query
from typing import Annotated
from ..schemas.user import (
CreateUserRequest,
UpdateUserRequest,
UserQueryParams,
UserResponse,
)
from ..services.user import UserService
from ..dependencies import get_current_user, get_user_service
router = APIRouter(prefix="/api/v1/users", tags=["Users"])
@router.post("/", response_model=UserResponse, status_code=201)
async def create_user(
body: CreateUserRequest, # Pydantic 自动验证请求体
service: Annotated[UserService, Depends(get_user_service)],
):
"""创建新用户。密码要求:至少 8 位,包含大小写字母和数字。"""
return await service.create(body)
@router.get("/", response_model=list[UserResponse])
async def list_users(
params: Annotated[UserQueryParams, Query()], # 自动验证查询参数
current_user=Depends(get_current_user),
service: Annotated[UserService, Depends(get_user_service)],
):
"""获取用户列表,支持搜索、过滤和分页。"""
return await service.list(params)
@router.patch("/{user_id}", response_model=UserResponse)
async def update_user(
user_id: str,
body: UpdateUserRequest,
current_user=Depends(get_current_user),
service: Annotated[UserService, Depends(get_user_service)],
):
"""更新用户信息。仅传入需要修改的字段。"""
return await service.update(user_id, body)6.5 验证中间件的 AI 生成提示词模板
请为以下 API 端点生成输入验证 Schema:
## 技术栈
- 验证库:[Zod / Pydantic / Joi]
- 框架:[Express / NestJS / FastAPI / Hono]
## 端点列表
[列出每个端点的路径、方法、参数]
## 验证要求
- 字符串字段:最小/最大长度、格式(email/url/uuid)、trim
- 数字字段:最小/最大值、整数/浮点数
- 枚举字段:明确列出允许的值
- 日期字段:格式验证、范围限制
- 数组字段:最小/最大长度、元素类型验证
- 嵌套对象:递归验证
- 可选字段:明确标注,提供默认值
- 自定义验证:[描述业务规则]
## 安全要求
- 所有字符串输入必须有最大长度限制(防止 DoS)
- 分页 limit 参数必须有上限(如 100)
- ID 参数必须验证格式(CUID2/UUID)
- 禁止接受未定义的额外字段(strict mode)
## 输出要求
- 创建和更新使用不同的 Schema(更新字段可选)
- 导出 TypeScript 类型(从 Schema 推断)
- 包含自定义错误消息(中文)7. OpenAPI 文档自动生成
7.1 文档生成策略对比
API 文档是开发者体验的关键组成部分。2025-2026 年,AI 驱动的文档生成工具正在改变 API 文档的创建方式。
| 策略 | 工具 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|---|
| Schema-First 生成 | Swagger UI、Scalar、Redocly | 文档与实现始终一致 | 需要维护 Schema 文件 | 公开 API、团队协作 |
| Code-First 提取 | NestJS Swagger、FastAPI 内置 | 开发速度快,无需额外文件 | 文档质量依赖代码注释 | 内部 API、快速原型 |
| AI 辅助生成 | Theneo、Mintlify、AI Agent | 可从代码/Postman 集合生成 | 可能不够精确 | 遗留 API 文档补全 |
| Zod-to-OpenAPI | @asteasolutions/zod-to-openapi | Zod Schema 即文档 | 仅限 TypeScript | tRPC/Express + Zod 项目 |
7.2 NestJS + Swagger 自动文档生成
// main.ts — NestJS Swagger 配置
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Swagger 文档配置
const config = new DocumentBuilder()
.setTitle('项目管理 API')
.setDescription('多租户项目管理 SaaS 的 RESTful API')
.setVersion('1.0.0')
.addBearerAuth(
{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
'JWT-auth'
)
.addTag('Projects', '项目管理')
.addTag('Tasks', '任务管理')
.addTag('Users', '用户管理')
.addServer('https://api.example.com', '生产环境')
.addServer('http://localhost:3000', '开发环境')
.build();
const document = SwaggerModule.createDocument(app, config);
// 导出 OpenAPI JSON(用于客户端代码生成)
const fs = await import('fs');
fs.writeFileSync('./openapi.json', JSON.stringify(document, null, 2));
// 挂载 Swagger UI
SwaggerModule.setup('docs', app, document, {
swaggerOptions: {
persistAuthorization: true,
tagsSorter: 'alpha',
operationsSorter: 'alpha',
},
});
await app.listen(3000);
}
bootstrap();// dto/create-task.dto.ts — 使用装饰器生成文档
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsString, IsEnum, IsOptional, MinLength, MaxLength } from 'class-validator';
export class CreateTaskDto {
@ApiProperty({
description: '任务标题',
example: '实现用户注册 API',
minLength: 1,
maxLength: 200,
})
@IsString()
@MinLength(1)
@MaxLength(200)
title: string;
@ApiPropertyOptional({
description: '任务描述',
example: '包含邮箱验证和密码强度检查',
maxLength: 5000,
})
@IsOptional()
@IsString()
@MaxLength(5000)
description?: string;
@ApiPropertyOptional({
description: '优先级',
enum: ['LOW', 'MEDIUM', 'HIGH', 'URGENT'],
default: 'MEDIUM',
})
@IsOptional()
@IsEnum(['LOW', 'MEDIUM', 'HIGH', 'URGENT'])
priority?: string;
}7.3 FastAPI 内置文档生成
FastAPI 的一大优势是 Pydantic 模型自动生成 OpenAPI 文档,无需额外配置:
# main.py — FastAPI 自动文档
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
app = FastAPI(
title="项目管理 API",
description="多租户项目管理 SaaS 的 RESTful API",
version="1.0.0",
docs_url="/docs", # Swagger UI
redoc_url="/redoc", # ReDoc
openapi_url="/openapi.json",
)
# 自定义 OpenAPI Schema
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title=app.title,
version=app.version,
description=app.description,
routes=app.routes,
)
# 添加安全方案
openapi_schema["components"]["securitySchemes"] = {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
}
}
openapi_schema["security"] = [{"BearerAuth": []}]
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi7.4 Zod-to-OpenAPI(TypeScript 推荐方案)
对于使用 Zod 的 TypeScript 项目,@asteasolutions/zod-to-openapi 可以从 Zod Schema 直接生成 OpenAPI 文档:
// openapi.ts — 从 Zod Schema 生成 OpenAPI 文档
import {
OpenAPIRegistry,
OpenApiGeneratorV31,
extendZodWithOpenApi,
} from '@asteasolutions/zod-to-openapi';
import { z } from 'zod';
extendZodWithOpenApi(z);
const registry = new OpenAPIRegistry();
// 注册 Schema
const TaskSchema = z.object({
id: z.string().cuid2().openapi({ example: 'clx1234567890' }),
title: z.string().min(1).max(200).openapi({ example: '实现用户注册 API' }),
status: z.enum(['TODO', 'IN_PROGRESS', 'IN_REVIEW', 'DONE']),
priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'URGENT']),
createdAt: z.string().datetime(),
}).openapi('Task');
const CreateTaskSchema = z.object({
title: z.string().min(1).max(200),
description: z.string().max(5000).optional(),
priority: z.enum(['LOW', 'MEDIUM', 'HIGH', 'URGENT']).default('MEDIUM'),
}).openapi('CreateTaskInput');
// 注册端点
registry.registerPath({
method: 'post',
path: '/api/v1/tasks',
summary: '创建任务',
tags: ['Tasks'],
request: {
body: {
content: { 'application/json': { schema: CreateTaskSchema } },
},
},
responses: {
201: {
description: '任务创建成功',
content: { 'application/json': { schema: TaskSchema } },
},
422: {
description: '输入验证失败',
},
},
});
// 生成 OpenAPI 文档
const generator = new OpenApiGeneratorV31(registry.definitions);
const document = generator.generateDocument({
openapi: '3.1.0',
info: { title: '项目管理 API', version: '1.0.0' },
servers: [{ url: 'https://api.example.com' }],
});
// 导出为 JSON 文件
import fs from 'fs';
fs.writeFileSync('openapi.json', JSON.stringify(document, null, 2));7.5 文档生成的 AI 提示词模板
请为以下 API 端点生成完整的 OpenAPI 3.1 文档:
## 端点列表
[列出所有端点的路径、方法、参数、请求体、响应]
## 文档要求
- 每个端点包含 summary 和 description(中文)
- 每个参数包含 description 和 example
- 每个 Schema 包含 example 值
- 包含认证安全方案(Bearer Token)
- 包含标准错误响应(400, 401, 403, 404, 422, 500)
- 使用 $ref 引用共享的 Schema 组件
- 包含 tags 分组
## 输出格式
- YAML 格式
- 符合 OpenAPI 3.1.0 规范
- 可直接导入 Swagger UI / Scalar / Redocly8. API 版本管理策略
8.1 版本管理方式对比
API 版本管理是后端开发中最容易被忽视但影响深远的决策之一。选择错误的版本策略可能导致维护噩梦。
| 策略 | 示例 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|---|
| URL 路径版本 | /api/v1/users | 直观、缓存友好、易于路由 | URL 变长、版本间代码重复 | 公开 API(最常用,约 70% 采用) |
| Header 版本 | Accept: application/vnd.api+json;version=1 | URL 干净、灵活 | 不直观、调试困难 | 内部 API、高级场景 |
| Query 参数版本 | /api/users?version=1 | 简单、向后兼容 | 缓存不友好、不够规范 | 简单 API、过渡方案 |
| 日期版本 | /api/2025-01-15/users | 精确、适合频繁变更 | 版本号不直观 | Stripe 风格的 API |
| 无版本(演进式) | /api/users + 向后兼容 | 最简单、无版本管理开销 | 需要严格的兼容性纪律 | 内部微服务通信 |
8.2 推荐的版本管理实践
URL 路径版本(推荐默认方案)
// NestJS — URL 路径版本管理
// main.ts
import { VersioningType } from '@nestjs/common';
app.enableVersioning({
type: VersioningType.URI,
defaultVersion: '1',
prefix: 'api/v',
});
// controllers/user.controller.ts
@Controller({ path: 'users', version: '1' })
export class UserControllerV1 {
@Get()
findAll() {
return this.userService.findAllV1();
}
}
@Controller({ path: 'users', version: '2' })
export class UserControllerV2 {
@Get()
findAll() {
// V2 返回不同的响应格式
return this.userService.findAllV2();
}
}版本废弃策略
// middleware/deprecation.ts — 版本废弃通知中间件
import { Request, Response, NextFunction } from 'express';
interface VersionConfig {
current: string;
supported: string[];
deprecated: Record<string, { sunset: string; successor: string }>;
}
const versionConfig: VersionConfig = {
current: 'v2',
supported: ['v1', 'v2'],
deprecated: {
v1: {
sunset: '2026-06-01',
successor: 'v2',
},
},
};
export function versionDeprecationMiddleware(
req: Request,
res: Response,
next: NextFunction
) {
const version = req.path.match(/\/api\/(v\d+)\//)?.[1];
if (!version) return next();
const deprecation = versionConfig.deprecated[version];
if (deprecation) {
res.setHeader('Deprecation', 'true');
res.setHeader('Sunset', deprecation.sunset);
res.setHeader(
'Link',
`</api/${deprecation.successor}${req.path.replace(`/api/${version}`, '')}>; rel="successor-version"`
);
// 可选:记录废弃版本的使用情况
console.warn(
`[DEPRECATED] ${version} API called: ${req.method} ${req.path}`
);
}
next();
}8.3 AI 辅助版本迁移
当需要从 v1 迁移到 v2 时,AI 可以辅助生成迁移代码和兼容层:
提示词模板:
请帮我将以下 API 从 v1 迁移到 v2:
## v1 API 定义
[粘贴 v1 的 OpenAPI 规范或代码]
## v2 变更列表
- [描述每个破坏性变更]
- 例如:User 响应中 fullName 拆分为 firstName + lastName
- 例如:分页从 offset 改为 cursor
- 例如:错误格式从自定义改为 RFC 7807
## 要求
1. 生成 v2 的 Controller/Router 代码
2. 生成 v1 → v2 的兼容适配层(让 v1 调用者平滑过渡)
3. 生成迁移指南文档(面向 API 消费者)
4. v1 添加 Deprecation 响应头
5. 包含版本路由配置9. AI 辅助 API 设计的完整工作流
操作步骤
步骤 1:需求分析与资源建模
使用 AI 从业务需求中提取 API 资源和关系:
请分析以下业务需求,提取 API 资源模型:
## 业务需求
[描述业务场景]
## 输出要求
1. 核心资源列表(名称、描述、关键属性)
2. 资源间关系图(Mermaid ER 图)
3. 每个资源的 CRUD 需求分析
4. 非 CRUD 操作识别(如"审批"、"发布"、"归档")
5. 建议的 URL 结构步骤 2:Schema 设计
根据资源模型,使用 AI 生成 API Schema(OpenAPI/GraphQL SDL/Zod)。
步骤 3:Mock 服务生成
在实现代码之前,先生成 Mock 服务供前端开发使用:
# 使用 Prism 从 OpenAPI 生成 Mock 服务
npx @stoplight/prism-cli mock openapi.yaml --port 4010
# 使用 MSW(Mock Service Worker)生成前端 Mock
# AI 可以从 OpenAPI 规范生成 MSW handlers步骤 4:实现代码生成
使用 AI 基于 Schema 生成服务端实现代码(见第 3-5 节)。
步骤 5:测试生成
请基于以下 OpenAPI 规范生成 API 集成测试:
## OpenAPI 规范
[粘贴规范]
## 测试框架
- [Jest + Supertest / Pytest + httpx / Go testing]
## 测试覆盖要求
- 每个端点的正常路径(200/201)
- 输入验证失败(422)
- 认证失败(401)
- 权限不足(403)
- 资源不存在(404)
- 分页边界(空列表、最后一页)
- 并发创建(幂等性验证)步骤 6:文档生成与发布
使用 Scalar 或 Redocly 渲染 OpenAPI 文档:
# 使用 Scalar 渲染美观的 API 文档
npx @scalar/cli serve openapi.yaml --port 3100
# 使用 Redocly 生成静态文档站点
npx @redocly/cli build-docs openapi.yaml --output docs/api/index.html实战案例:用 AI 从零设计并生成一个电商 API
案例背景
为一个中型电商平台设计和生成后端 API,技术栈为 TypeScript + NestJS + Prisma + PostgreSQL,采用 Schema-First 方法论。
步骤 1:需求分析(AI 辅助资源建模)
向 AI 描述业务需求:
我需要为一个电商平台设计 API,核心功能包括:
1. 商品管理(CRUD、分类、搜索、库存管理)
2. 购物车(添加/移除/更新数量)
3. 订单(创建、支付、发货、退款)
4. 用户(注册、登录、地址管理)
请提取资源模型和 URL 结构。AI 输出资源模型:
核心资源:
- Product(商品): id, name, description, price, stock, categoryId, images, status
- Category(分类): id, name, slug, parentId
- Cart(购物车): id, userId, items[]
- CartItem(购物车项): id, cartId, productId, quantity
- Order(订单): id, userId, items[], status, totalAmount, shippingAddress
- OrderItem(订单项): id, orderId, productId, quantity, unitPrice
- User(用户): id, email, name, addresses[]
- Address(地址): id, userId, street, city, state, zipCode, isDefault
URL 结构:
/api/v1/products — 商品 CRUD + 搜索
/api/v1/categories — 分类 CRUD
/api/v1/cart — 当前用户购物车
/api/v1/cart/items — 购物车项操作
/api/v1/orders — 订单 CRUD
/api/v1/orders/:id/pay — 支付(非 CRUD 操作)
/api/v1/orders/:id/ship — 发货(非 CRUD 操作)
/api/v1/orders/:id/refund — 退款(非 CRUD 操作)
/api/v1/users/me — 当前用户信息
/api/v1/users/me/addresses — 地址管理步骤 2:Schema 设计(AI 生成 OpenAPI)
AI 生成关键部分的 OpenAPI Schema(以订单为例):
# 订单相关端点(OpenAPI 3.1 片段)
paths:
/api/v1/orders:
post:
operationId: createOrder
summary: 创建订单
tags: [Orders]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [shippingAddressId]
properties:
shippingAddressId:
type: string
description: 收货地址 ID
note:
type: string
maxLength: 500
description: 订单备注
responses:
'201':
description: 订单创建成功
content:
application/json:
schema:
$ref: '#/components/schemas/OrderResponse'
'400':
description: 购物车为空或库存不足
/api/v1/orders/{orderId}/pay:
post:
operationId: payOrder
summary: 支付订单
tags: [Orders]
parameters:
- name: orderId
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [paymentMethod]
properties:
paymentMethod:
type: string
enum: [credit_card, alipay, wechat_pay]
paymentToken:
type: string
description: 支付网关返回的 token
responses:
'200':
description: 支付成功
'402':
description: 支付失败
'409':
description: 订单状态不允许支付步骤 3:验证 Schema 生成(Zod)
AI 基于 OpenAPI Schema 生成 Zod 验证:
// schemas/order.ts
import { z } from 'zod';
export const PaymentMethod = z.enum(['credit_card', 'alipay', 'wechat_pay']);
export const CreateOrderSchema = z.object({
shippingAddressId: z.string().cuid2(),
note: z.string().max(500).optional(),
});
export const PayOrderSchema = z.object({
paymentMethod: PaymentMethod,
paymentToken: z.string().min(1).optional(),
});
export const OrderQuerySchema = z.object({
status: z.enum([
'PENDING', 'PAID', 'SHIPPED', 'DELIVERED', 'CANCELLED', 'REFUNDED'
]).optional(),
cursor: z.string().optional(),
limit: z.coerce.number().int().min(1).max(50).default(20),
});步骤 4:Controller 生成
AI 生成 NestJS Controller(关键部分):
// controllers/order.controller.ts
@Controller('api/v1/orders')
@UseGuards(JwtAuthGuard)
@ApiTags('Orders')
export class OrderController {
constructor(private readonly orderService: OrderService) {}
@Post()
@HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: '创建订单(从购物车)' })
async create(
@CurrentUser() user: AuthUser,
@Body(new ZodValidationPipe(CreateOrderSchema)) dto: CreateOrderInput,
) {
return this.orderService.createFromCart(user.id, dto);
}
@Post(':orderId/pay')
@ApiOperation({ summary: '支付订单' })
async pay(
@CurrentUser() user: AuthUser,
@Param('orderId') orderId: string,
@Body(new ZodValidationPipe(PayOrderSchema)) dto: PayOrderInput,
) {
return this.orderService.processPayment(user.id, orderId, dto);
}
@Post(':orderId/refund')
@Roles('ADMIN')
@UseGuards(RolesGuard)
@ApiOperation({ summary: '退款(仅管理员)' })
async refund(
@Param('orderId') orderId: string,
@Body() dto: RefundOrderDto,
) {
return this.orderService.processRefund(orderId, dto);
}
}步骤 5:安全审查
AI 对生成的 API 进行安全审查,发现以下问题:
| 严重程度 | 问题 | 修复 |
|---|---|---|
| Critical | 创建订单时未验证库存充足性(可能超卖) | 在 Service 层添加库存检查 + 数据库事务 + 乐观锁 |
| High | 支付端点缺少幂等性保护(重复支付风险) | 添加幂等键(Idempotency-Key header) |
| High | 退款端点仅检查角色,未验证订单归属 | 添加订单归属验证 |
| Medium | 订单查询未限制只能查看自己的订单 | 添加 userId 过滤条件 |
| Low | 缺少请求速率限制 | 添加 @Throttle() 装饰器 |
案例分析
关键决策点:
- Schema-First 的价值:先定义 OpenAPI Schema,确保了前后端对 API 契约的共识。前端团队可以立即基于 Schema 生成 Mock 数据和类型定义,无需等待后端实现
- 非 CRUD 操作的设计:支付、发货、退款等操作使用
POST /orders/:id/pay风格,而非PATCH /orders/:id,因为这些是有副作用的业务操作,不是简单的状态更新 - 安全审查的必要性:AI 生成的 CRUD 代码质量很高,但涉及支付、库存等业务关键路径时,必须人工审查并补充安全措施(幂等性、事务、乐观锁)
学习要点:
- Schema-First + AI 生成是目前最高效的 API 开发方式——Schema 作为”单一事实来源”,AI 从 Schema 生成实现代码、验证逻辑、文档和测试
- 非 CRUD 操作(状态转换、业务动作)是 AI 生成的薄弱环节,需要人工设计 URL 结构和业务逻辑
- 安全审查应该是 API 开发流程的固定环节,特别是涉及金融交易和数据修改的端点
避坑指南
❌ 常见错误
-
跳过 Schema 设计直接让 AI 写代码
- 问题:没有明确的 API 契约,AI 生成的不同端点可能风格不一致——有的用 camelCase,有的用 snake_case;有的返回数组,有的返回包装对象;错误格式各不相同。这在多人协作时尤其致命
- 正确做法:先用 AI 生成 OpenAPI/GraphQL Schema/Zod Schema,审查确认后再生成实现代码。Schema 是”单一事实来源”,所有代码从 Schema 派生
-
AI 生成的验证 Schema 缺少安全约束
- 问题:AI 生成的 Zod/Pydantic Schema 通常只包含基本类型检查,缺少关键的安全约束:字符串没有最大长度限制(可被用于 DoS 攻击)、数字没有范围限制、分页 limit 没有上限、ID 格式未验证
- 正确做法:在 Steering 规则中明确要求”所有字符串字段必须有 maxLength,所有数字字段必须有 min/max,分页 limit 上限为 100,ID 必须验证格式”。审查每个 Schema 的安全约束
-
GraphQL 未防护 N+1 查询
- 问题:AI 生成的 GraphQL Resolver 通常直接在关联字段中调用数据库查询,导致严重的 N+1 问题。例如查询 100 个任务的创建者,会触发 100 次用户查询
- 正确做法:在 Prompt 中明确要求”所有关联字段使用 DataLoader 批量加载”。同时配置查询深度限制和复杂度分析,防止恶意的深层嵌套查询
-
tRPC 项目中 Schema 和 Router 混在一起
- 问题:AI 倾向于在 Router 文件中直接定义 Zod Schema(内联定义),导致 Schema 无法复用、难以测试、前后端无法共享类型
- 正确做法:将 Zod Schema 独立到
schemas/目录,Router 文件只引用 Schema。这样 Schema 可以在前端、后端、测试中复用
-
API 版本管理从一开始就被忽略
- 问题:项目初期不考虑版本管理,等到需要破坏性变更时才发现没有版本策略,被迫在不兼容的情况下修改 API,导致客户端崩溃
- 正确做法:从第一天起就使用 URL 路径版本(
/api/v1/)。即使短期内不需要 v2,版本前缀的成本几乎为零,但未来的灵活性极高
-
让 AI 生成 API 时不指定错误响应格式
- 问题:如果不明确要求,AI 会为每个端点生成不同的错误格式——有的返回
{ error: "message" },有的返回{ message: "...", code: "..." },有的直接返回字符串。前端处理错误时需要为每个端点写不同的逻辑 - 正确做法:在 Prompt 和 Steering 规则中指定统一的错误格式(推荐 RFC 7807 Problem Details)。所有端点的错误响应必须遵循同一格式
- 问题:如果不明确要求,AI 会为每个端点生成不同的错误格式——有的返回
-
GraphQL Schema 设计中滥用 nullable 字段
- 问题:AI 生成的 GraphQL Schema 倾向于将大量字段标记为 nullable(可空),因为这样”更安全”。但过多的 nullable 字段会让客户端代码充满空值检查,降低类型安全的价值
- 正确做法:遵循”默认 non-null,显式标记 nullable”的原则。只有在业务上确实可能为空的字段才标记为 nullable。在 Prompt 中明确要求”字段默认 non-null,仅在 [列出字段] 使用 nullable”
-
忽略 API 的幂等性设计
- 问题:AI 生成的 POST 端点通常不考虑幂等性。如果客户端因网络问题重试请求,可能导致重复创建资源(重复订单、重复支付等)
- 正确做法:对于有副作用的操作(创建订单、支付、发送通知),要求 AI 实现幂等键(Idempotency-Key header)机制。在 Steering 规则中标注哪些端点需要幂等性保护
-
REST API 中混用 PUT 和 PATCH
- 问题:AI 有时混淆 PUT(全量替换)和 PATCH(部分更新)的语义,生成的 PUT 端点实际上只更新传入的字段(PATCH 行为),或者 PATCH 端点要求传入所有字段(PUT 行为)
- 正确做法:在 Prompt 中明确指定”使用 PATCH 进行部分更新(仅更新传入的字段),不使用 PUT”。大多数现代 API 只需要 PATCH,不需要 PUT
-
OpenAPI 文档与实际 API 行为不一致
- 问题:Code-First 方式生成的 OpenAPI 文档可能与实际 API 行为不一致——文档说必填的字段实际上可选,文档说返回 200 实际返回 201,文档中的 example 值与实际格式不匹配
- 正确做法:使用契约测试(如 Schemathesis、Dredd)自动验证 API 实现是否符合 OpenAPI 规范。将契约测试集成到 CI/CD 管线中
✅ 最佳实践
-
Schema-First 是默认选择:无论使用 REST、GraphQL 还是 tRPC,都应该先定义数据契约(OpenAPI/SDL/Zod Schema),再生成实现代码。这是 AI 辅助 API 开发中收益最高的实践
-
验证 Schema 即文档:使用 Zod/Pydantic 定义的验证 Schema 应该同时作为 API 文档的数据源。避免维护两套定义(验证 + 文档)
-
统一错误格式从第一天开始:选择 RFC 7807 Problem Details 或类似的标准格式,在项目初始化时就配置好全局错误处理中间件
-
分页策略统一:所有列表端点使用相同的分页方式(推荐 cursor-based)。在 Steering 规则中固定分页参数名和响应格式
-
AI 生成 + 契约测试:让 AI 生成 API 代码后,使用契约测试工具自动验证实现是否符合 Schema。这是验证 AI 输出质量的最可靠方式
-
安全验证是 Schema 的一部分:不要把安全验证(长度限制、格式检查、范围约束)当作”额外工作”——它们应该直接定义在 Schema 中,与业务验证一起执行
-
版本策略写入 Steering 规则:将 API 版本管理策略(URL 路径版本、废弃通知、迁移周期)写入 Steering 规则,确保 AI 生成的每个端点都遵循版本规范
-
GraphQL 必须配置安全防护:查询深度限制、复杂度分析、DataLoader 批量加载——这三项是 GraphQL API 的必备安全措施,必须在项目初始化时就配置好
相关资源与延伸阅读
规范与标准
- OpenAPI Specification 3.1.0 — REST API 描述的行业标准规范,完全兼容 JSON Schema 2020-12
- GraphQL 官方规范 — GraphQL 查询语言和类型系统的官方规范文档
- RFC 7807 — Problem Details for HTTP APIs — HTTP API 错误响应的标准格式
工具与框架
- tRPC v11 官方文档 — tRPC v11 的完整文档,包含 Next.js、React Query 集成指南
- Zod 官方文档 — TypeScript-first 的 Schema 验证库,广泛用于 tRPC 和 API 验证
- Pydantic v2 官方文档 — Python 的数据验证库,FastAPI 的核心依赖
- Scalar — 现代 API 文档渲染 — 开源的 OpenAPI 文档渲染工具,替代 Swagger UI
- GraphQL Code Generator — 从 GraphQL Schema 生成 TypeScript 类型和 SDK
深度阅读
- Generating OpenAPI Specifications from API Documentation with LLMs — 使用大语言模型从 API 文档自动生成 OpenAPI 规范的研究论文
- AI-Powered API Documentation and Client Generation (2026) — AI 驱动的 API 文档和客户端生成的最新实践综述
参考来源
- OpenAPI Specification — The Definitive Guide (2026) (2026 年 2 月)
- tRPC — Announcing tRPC v11 (2025 年 3 月)
- Encore — Which Type-Safe Backend Approach to Choose in 2026 (2026 年 2 月)
- AI-Powered API Documentation and Client Generation (2026 年 2 月)
- Generate Production-Ready OpenAPI Specs in 20 Minutes (2026 年 2 月)
- Infrasity — Top 4 AI Document Generators for Developer Docs (2026) (2025 年 12 月)
- Treblle — 13 Best OpenAPI Documentation Tools for 2026 (2026 年 1 月)
- Devzery — REST API Versioning: Strategies & Best Practices 2025 (2025 年 6 月)
- OneUptime — How to Version REST APIs Effectively (2026 年 1 月)
- FreeCodeCamp — Type Safety Without Code Generation Using tRPC and Hono (2026 年 1 月)
- Medium — Type-Safe API with Zod + NestJS + OpenAPI (2025 年 5 月)
- JSON Schema as Security Control — OpenAPI 3.1 (2025 年 2 月)
- Binary.ph — AI-Assisted Development in 2026: Best Practices, Risks, and Evolution (2026 年 2 月)
📖 返回 总览与导航 | 上一节:28a-AI辅助后端开发概览 | 下一节:28c-Spec-Driven业务逻辑