Skip to main content

api-development

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

소스 정보

저장소
labring/FastGPT
최근 소스 활동
2026년 8월 24일 14:06
감지된 SKILL.md 언어
중국어
스타
29,837
포크
7,335

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기