Skip to main content

api-development

FastGPT API 开发规范。重点强调使用 zod schema 定义入参和出参,在 API 文档中声明路由信息,编写对应的 OpenAPI 文档,以及在 API 路由中使用 schema.parse 进行验证。

Source facts

Repository
labring/FastGPT
Last source activity
August 24, 2026 at 14:06
Detected SKILL.md language
Chinese
Stars
29,837
Forks
7,335

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
api-development
description
FastGPT API 开发规范。重点强调使用 zod schema 定义入参和出参,在 API 文档中声明路由信息,编写对应的 OpenAPI 文档,以及在 API 路由中使用 schema.parse 进行验证。
# FastGPT API 开发规范 > FastGPT 项目 API 路由开发的标准化指南,确保 API 的一致性、类型安全和文档完整性。 ## 何时使用此技能 - 开发新的 Next.js API 路由 - 修改现有 API 的入参或出参 - 需要 API 类型定义和文档 - 审查 API 相关代码 ## 核心原则 ### 🔴 必须遵守的规则 1. **API 中实际存在的业务入参和业务出参必须使用 zod schema 定义;无入参或空成功响应不创建空 Schema** 2. **已定义的 schema 必须导出对应的 TypeScript 类型** 3. **必须在 schema 文件头部声明 API 信息(路由、方法、描述、标签),一次性管理员升级/清洗能力除外** 4. **实际存在的入参必须使用 `parseApiInput` 验证;完全无入参时不做空 Query/Body Schema 校验** 5. **实际存在业务数据的函数返回值必须使用 schema.parse() 验证;空成功响应直接返回 `undefined`,不做空 Schema 校验** 6. **必须编写完整的 OpenAPI 文档,一次性管理员升级/清洗能力除外** ### 管理员升级与清洗能力的文档豁免 仅供系统管理员执行、用于一次性升级、迁移、修复或数据清洗的内部接口和脚本,不属于产品 API,不要求: - 在 `packages/global/openapi/` 声明接口文档; - 注册 OpenAPI Path; - 编写 API 头部路由、方法、描述和标签信息。 豁免只针对文档,不豁免安全和校验要求: - 管理员接口必须使用 `authSystemAdmin` 鉴权; - 实际存在的 API 入参必须使用 Zod Schema 和 `parseApiInput`,完全无入参时不创建空 Schema; - 实际存在业务数据的返回值必须使用 Zod Schema 校验,空成功响应直接返回 `undefined`; - 数据清洗默认使用 dry-run,显式确认后才能写入,并输出成功、跳过和失败统计; - 清洗逻辑应可重复执行,无法安全修复的数据必须跳过并报告,不得静默填入猜测值。 常规管理员产品接口(例如模型配置 CRUD、用户管理和系统配置)不因仅管理员可用而获得豁免,仍需按标准 API 流程维护文档。 ## 开发流程 ### 步骤 1: 定义 Zod Schema 并声明 API **文件位置**: `packages/global/openapi/[module]/[api].ts` **文件头部必须声明 API 信息**: ```typescript import { z } from 'zod'; /* ============================================================================ * API: 获取应用对话日志列表 * Route: POST /api/core/app/logs/list * Method: POST * Description: 获取指定应用的对话日志列表,支持分页和多种筛选条件 * Tags: ['App', 'Log', 'Read'] * ============================================================================ */ // 入参 Schema export const GetAppChatLogsBodySchema = PaginationSchema.extend({ appId: z.string().meta({ example: '68ad85a7463006c963799a05', description: '应用 ID' }), dateStart: z.union([z.string(), z.date()]).meta({ example: '2024-01-01T00:00:00.000Z', description: '开始时间' }), dateEnd: z.union([z.string(), z.date()]).meta({ example: '2024-12-31T23:59:59.999Z', description: '结束时间' }), sources: z.array(z.nativeEnum(ChatSourceEnum)).optional().meta({ example: [ChatSourceEnum.api, ChatSourceEnum.online], description: '对话来源筛选' }) }); // 导出入参类型 export type getAppChatLogsBody = z.infer<typeof GetAppChatLogsBodySchema>; // 出参 Schema export const GetAppChatLogsResponseSchema = z.object({ total: z.number().meta({ example: 100, description: '总记录数' }), list: z.array(ChatLogItemSchema) }); // 导出出参类型 export type getAppChatLogsResponseType = z.infer<typeof GetAppChatLogsResponseSchema>; ``` **API 声明规范**: ```typescript /** * 每个 API 文件必须在文件头部声明以下信息: * * 1. API 名称 (API): 简短的功能描述 * 2. 路由 (Route): 完整的 API 路径 * 3. 方法 (Method): HTTP 方法 (GET/POST/PUT/DELETE) * 4. 描述 (Description): API 的详细功能说明 * 5. 标签 (Tags): API 的分类标签数组 * * 标签示例: * - 'App': 应用相关 API * - 'User': 用户相关 API * - 'Log': 日志相关 API * - 'Read': 只读操作 * - 'Write': 写入操作 * - 'Delete': 删除操作 */ ``` **OpenAPI Tag 归属规则**: - 如果接口能力属于通用模块 A,但会被业务模块 B 使用,则该接口必须同时声明 A 模块 tag 和 B 模块 tag。 - 如果同一个通用接口也被业务模块 C 使用,则继续追加 C 模块 tag。 - 通用模块 tag 表示接口能力和实现抽象归属;业务模块 tag 表示该接口应出现在对应业务文档视角里。 - 如果接口只是业务模块自己的状态查询或状态操作,不属于通用模块能力,则只声明业务模块 tag,不要为了实现位置或相邻目录误加通用模块 tag。 - 示例:`协作者管理` 是通用权限能力,应用协作者接口需要同时声明 `协作者管理` 和应用侧 `权限管理`;`获取应用权限`、`恢复应用继承权限` 是应用自身权限状态接口,只声明应用侧 `权限管理`。 **Schema 定义规范**: #### ✅ 空入参与空成功响应 - 请求没有 query、body 或 params 时,不要为了形式完整而声明 `z.object({})`,路由中也不需要调用 `parseApiInput`。 - 成功响应没有业务数据时,不要声明 `z.undefined()`、`z.null()` 或 `z.object({})` 作为占位 Schema,也不需要调用 `Schema.parse(undefined)`;handler 直接不返回值或返回 `undefined`。 - 只有请求或响应实际携带业务字段时,才定义对应 Schema、导出类型并在 API 边界执行校验。 - OpenAPI 中无入参接口省略 `requestParams`/`requestBody`;空成功响应只保留状态码和说明,不声明占位 schema。 #### ✅ 字段定义规范 ```typescript // ✅ 好的实践: 完整的 meta 信息 export const GetUserSchema = z.object({ userId: z.string().meta({ example: '68ad85a7463006c963799a05', description: '用户 ID' }), email: z.string().email().meta({ example: 'user@example.com', description: '用户邮箱' }), age: z.number().int().positive().meta({ example: 25, description: '用户年龄' }), status: z.enum(['active', 'inactive']).meta({ example: 'active', description: '用户状态' }) }); // ❌ 不好的实践: 缺少 meta 信息 export const GetUserSchemaBad = z.object({ userId: z.string(), email: z.string(), age: z.number(), status: z.string() }); ``` #### ✅ 嵌套对象定义 ```typescript // 嵌套对象应该定义为独立的 Schema export const AddressSchema = z.object({ street: z.string().meta({ description: '街道地址' }), city: z.string().meta({ description: '城市' }), country: z.string().meta({ description: '国家' }) }); export const CreateUserSchema = z.object({ name: z.string().meta({ description: '用户名' }), address: AddressSchema.meta({ description: '地址信息' }) }); ``` #### ✅ 数组定义 ```typescript export const GetUserListResponseSchema = z.object({ total: z.number().meta({ example: 100, description: '总数' }), list: z.array( z.object({ id: z.string().meta({ description: '用户 ID' }), name: z.string().meta({ description: '用户名' }) }) ).meta({ description: '用户列表' }) }); ``` #### ✅ 可选字段 ```typescript export const UpdateUserSchema = z.object({ userId: z.string().meta({ description: '用户 ID' }), // 可选字段使用 .optional() name: z.string().optional().meta({ description: '用户名' }), // 或使用 .nullish() 允许 null 和 undefined email: z.string().email().nullish().meta({ description: '用户邮箱' }) }); ``` #### ✅ 分页 Schema ```typescript import { PaginationSchema } from '@fastgpt/global/openapi/api'; // 继承分页 Schema export const GetUserListSchema = PaginationSchema.extend({ // 添加额外的筛选字段 keyword: z.string().optional().meta({ description: '搜索关键词' }), status: z.enum(['active', 'inactive']).optional().meta({ description: '状态筛选' }) }); ``` 分页请求参数必须优先复用 `@fastgpt/global/openapi/api` 导出的 `PaginationSchema`,不要在业务域或单个接口中重复声明同构的分页 Schema(例如再次定义 `UserPaginationBodySchema`)。如果接口确实需要额外筛选条件,使用 `PaginationSchema.extend(...)`;只有存在明确的兼容性或协议差异时,才允许定义专用 wrapper,并在附近说明原因。分页响应优先复用 `PaginationResponseSchema`。 #### ✅ 多个 API 的 Schema 文件 ```typescript /* ============================================================================ * API: 获取日志键 * Route: GET /api/core/app/logs/keys * Method: GET * Description: 获取应用的日志配置键列表 * Tags: ['App', 'Log', 'Read'] * ============================================================================ */ export const GetLogKeysQuerySchema = z.object({ appId: z.string().meta({ description: '应用 ID' }) }); export const GetLogKeysResponseSchema = z.object({ logKeys: z.array(AppLogKeysSchema).meta({ description: '日志键列表' }) }); /* ============================================================================ * API: 更新日志键 * Route: POST /api/core/app/logs/keys * Method: POST * Description: 更新应用的日志配置键 * Tags: ['App', 'Log', 'Write'] * ============================================================================ */ export const UpdateLogKeysBodySchema = z.object({ appId: z.string().meta({ description: '应用 ID' }), logKeys: z.array(AppLogKeysSchema).meta({ description: '日志键列表' }) }); ``` ### 步骤 2: 实现 API 路由 **文件位置**: `projects/app/src/pages/api/[path]/[route].ts` **标准实现模板**: ```typescript import type { NextApiResponse } from 'next'; import { NextAPI } from '@/service/middleware/entry'; import type { ApiRequestProps } from '@fastgpt/service/types/next'; import { GetAppChatLogsBodySchema, GetAppChatLogsResponseSchema, type getAppChatLogsResponseType } from '@fastgpt/global/openapi/...'; async function handler( req: ApiRequestProps, _res: NextApiResponse ): Promise<getAppChatLogsResponseType> { // 🔴 步骤 1: 使用 schema.parse() 验证入参 const { appId, dateStart, dateEnd, sources } = GetAppChatLogsBodySchema.parse(req.body); // 或对于 query 参数 // const { param1, param2 } = YourAPIQuerySchema.parse(req.query); // 🔴 步骤 2: 业务逻辑处理 const result = await yourBusinessLogic({ appId, dateStart, dateEnd, sources }); // 🔴 步骤 3: 使用 schema.parse() 验证出参 return GetAppChatLogsResponseSchema.parse({ list: result.list, total: result.total }); } export default NextAPI(handler); ``` **完整示例**: ```typescript import type { NextApiResponse } from 'next'; import type { ApiRequestProps } from '@fastgpt/service/types/next'; import { NextAPI } from '@/service/middleware/entry'; import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { GetAppChatLogsBodySchema, GetAppChatLogsResponseSchema, type getAppChatLogsResponseType
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub