SuperAPI AI Directory

api-development

Coding & development

View repository

Updated 10-15 23:48

About this skill

FastGPT API Development Specification

A standardized guide for API route development in the FastGPT project, ensuring API consistency, type safety, and documentation completeness.

When to Use This Skill

  • Developing new Next.js API routes
  • Modifying the input or output parameters of an existing API
  • Needing API type definitions and documentation
  • Reviewing API-related code

Core Principles

🔴 Rules That Must Be Followed

  1. All APIs must use zod schema to define input and output parameters
  2. The TypeScript type of the schema must be exported
  3. API information (route, method, description, tags) must be declared at the top of the schema file
  4. Input parameters must be validated using schema.parse()
  5. Function return values must be validated using schema.parse()
  6. Complete OpenAPI documentation must be written

Development Workflow

Step 1: Define the Zod Schema and Declare the API

File Location: packages/global/openapi/[module]/[api].ts

API information must be declared at the top of the file:

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 Declaration Specification:

/**
 * 每个 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 Assignment Rules:

  • If an interface's capability belongs to a general module A but will be used by a business module B, the interface must declare both the A module tag and the B module tag.
  • If the same general interface is also used by business module C, continue appending the C module tag.
  • The general module tag indicates the ownership of the interface capability and implementation abstraction; the business module tag indicates that the interface should appear from the perspective of the corresponding business documentation.
  • If the interface is only a business module's own status query or status operation and does not belong to a general module capability, declare only the business module tag, and do not mistakenly add a general module tag for the sake of implementation location or adjacent directories.
  • Example:协作者管理 is a general permission capability, and the app collaborator interface needs to declare both 协作者管理 and the app-side 权限管理;获取应用权限、恢复应用继承权限 is the app's own permission status interface, and only declares the app-side 权限管理。

Schema Definition Specification:

✅ Field Definition Specification

// ✅ 好的实践: 完整的 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()
});

✅ Nested Object Definition

// 嵌套对象应该定义为独立的 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: '地址信息' })
});

✅ Array Definition

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: '用户列表' })
});

✅ Optional Fields

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: '用户邮箱' })
});

✅ Pagination Schema

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: '状态筛选' })
});

✅ Schema File for Multiple APIs

/* ============================================================================
 * 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: '日志键列表' })
});

Step 2: Implement the API Route

File Location: projects/app/src/pages/api/[path]/[route].ts

Standard Implementation Template:

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);

Complete Example:

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
} from '@fastgpt/global/openapi/core/app/log/api';

async function handler(
  req: ApiRequestProps,
  _res: NextApiResponse
): Promise<getAppChatLogsResponseType> {
  // 🔴 1. 验证入参
  const { appId, dateStart, dateEnd, sources } = GetAppChatLogsBodySchema.parse(req.body);

  // 2. 权限验证 (如果需要)
  await authApp({
    req,
    authToken: true,
    appId,
    per: AppReadChatLogPerVal
  });

  // 3. 业务逻辑
  const { list, total } = await getChatLogsFromDB({
    appId,
    dateStart,
    dateEnd,
    sources
  });

  // 🔴 4. 验证出参
  return GetAppChatLogsResponseSchema.parse({
    list,
    total
  });
}

export default NextAPI(handler);

Step 3: Permission Verification (If Needed)

Use authApp or other permission verification functions:

import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { AppWritePerVal } from '@fastgpt/global/support/permission/app/constant';

async function handler(req: ApiRequestProps, res: NextApiResponse) {
  const { appId } = YourAPIBodySchema.parse(req.body);

  // 权限验证
  await authApp({
    req,
    authToken: true,
    appId,
    per: AppWritePerVal  // 权限常量
  });

  // 继续处理...
}

Step 4: Error Handling

Use unified error handling:

import { APIError } from '@fastgpt/service/core/error/controller';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';

async function handler(req: ApiRequestProps, res: NextApiResponse) {
  try {
    const { appId } = YourAPIBodySchema.parse(req.body);

    if (!appId) {
      return Promise.reject(CommonErrEnum.missingParams);
    }

    // 业务逻辑...

  } catch (error) {
    // 统一错误处理
    return APIError(error)(req, res);
  }
}

Complete Development Example

Scenario: Create User API

1. Define the Schema (packages/global/openapi/core/user/api.ts):

import { z } from 'zod';

/* ============================================================================
 * API: 创建用户
 * Route: POST /api/core/user/create
 * Method: POST
 * Description: 创建新用户,返回创建的用户信息
 * Tags: ['User', 'Write']
 * ============================================================================ */

// 入参
export const CreateUserBodySchema = z.object({
  name: z.string().min(2).max(50).meta({
    example: 'Alice',
    description: '用户名 (2-50 字符)'
  }),
  email: z.string().email().meta({
    example: 'alice@example.com',
    description: '用户邮箱'
  }),
  age: z.number().int().positive().optional().meta({
    example: 25,
    description: '用户年龄'
  }),
  avatar: z.string().url().optional().meta({
    example: 'https://example.com/avatar.jpg',
    description: '头像 URL'
  })
});

export type createUserBodyType = z.infer<typeof CreateUserBodySchema>;

// 出参
export const CreateUserResponseSchema = z.object({
  userId: z.string().meta({ example: '68ad85a7463006c963799a05', description: '用户 ID' }),
  name: z.string().meta({ example: 'Alice', description: '用户名' }),
  email: z.string().meta({ example: 'alice@example.com', description: '用户邮箱' }),
  createdAt: z.date().meta({ example: '2024-01-01T00:00:00.000Z', description: '创建时间' })
});

export type createUserResponseType = z.infer<typeof CreateUserResponseSchema>;

2. Implement the API (projects/app/src/pages/api/core/user/create.ts):

import type { NextApiResponse } from 'next';
import { NextAPI } from '@/service/middleware/entry';
import type { ApiRequestProps } from '@fastgpt/service/types/next';
import { MongoUser } from '@fastgpt/service/core/user/schema';
import {
  CreateUserBodySchema,
  CreateUserResponseSchema,
  type createUserResponseType
} from '@fastgpt/global/openapi/core/user/api';

async function handler(
  req: ApiRequestProps,
  _res: NextApiResponse
): Promise<createUserResponseType> {
  // 🔴 验证入参
  const { name, email, age, avatar } = CreateUserBodySchema.parse(req.body);

  // 检查邮箱是否已存在
  const existingUser = await MongoUser.findOne({ email });
  if (existingUser) {
    return Promise.reject('Email already exists');
  }

  // 创建用户
  const user = await MongoUser.create({
    name,
    email,
    age,
    avatar,
    createdAt: new Date()
  });

  // 🔴 验证出参
  return CreateUserResponseSchema.parse({
    userId: user._id.toString(),
    name: user.name,
    email: user.email,
    createdAt: user.createdAt
  });
}

export default NextAPI(handler);

Review Checklist

🔴 Must-Check Items (Blocking)

Schema File (packages/global/openapi/.../api.ts):

  • API Declaration: The top of the file has API information (route, method, description, tags)
  • Schema Definition: Both input and output parameters are defined using zod
  • Type Export: Export the z.infer<typeof Schema> type
  • Meta Information: All fields have description and example

API route file (projects/app/src/pages/api/.../route.ts):

  • Input validation: Use Schema.parse(req.body) or parse(req.query)
  • Output validation: Use Schema.parse(responseData)
  • Function return type: Declare the function return value as an exported type
  • Permission validation: The API route has the corresponding permission check (if required)

🟡 Recommended checks (advisory)

  • Error handling: Use APIError unified error handling
  • Field validation: Use zod validation methods (.min(), .max(), .email(), etc.)
  • Nullable fields: Correctly use .optional() or .nullish()
  • Reuse Schema: Extract identical structures into independent Schemas
  • Pagination support: List APIs inherit PaginationSchema

🟢 Optional checks (optimization)

  • Field order: Sort fields by importance
  • Schema reuse: Reuse existing Schemas to reduce duplication
  • Comments: Add comments for complex logic

Common issues and solutions

Issue 1: Missing API declaration

Incorrect example:

// ❌ 错误: 缺少 API 声明
import { z } from 'zod';

export const GetUserSchema = z.object({
  id: z.string()
});

Correct approach:

// ✅ 正确: 包含完整的 API 声明
import { z } from 'zod';

/* ============================================================================
 * API: 获取用户信息
 * Route: GET /api/core/user/detail
 * Method: GET
 * Description: 根据 userId 获取用户详细信息
 * Tags: ['User', 'Read']
 * ============================================================================ */

export const GetUserSchema = z.object({
  id: z.string().meta({
    example: '68ad85a7463006c963799a05',
    description: '用户 ID'
  })
});

Issue 2: Type mismatch

Incorrect example:

// ❌ 错误: 函数返回类型未声明
async function handler(req: ApiRequestProps, res: NextApiResponse) {
  const data = YourAPIBodySchema.parse(req.body);
  return { success: true, data };  // 类型未声明
}

Correct approach:

// ✅ 正确: 声明返回类型
async function handler(
  req: ApiRequestProps,
  _res: NextApiResponse
): Promise<yourAPIResponseType> {
  const data = YourAPIBodySchema.parse(req.body);

  return YourAPIResponseSchema.parse({
    success: true,
    data
  });
}

Issue 3: Missing Meta information

Incorrect example:

// ❌ 错误: 缺少 meta 信息
export const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string()
});

Correct approach:

// ✅ 正确: 完整的 meta 信息
export const UserSchema = z.object({
  id: z.string().meta({
    example: '68ad85a7463006c963799a05',
    description: '用户 ID'
  }),
  name: z.string().meta({
    example: 'Alice',
    description: '用户名'
  }),
  email: z.string().email().meta({
    example: 'alice@example.com',
    description: '用户邮箱'
  })
});

Issue 4: Output not validated

Incorrect example:

// ❌ 错误: 直接返回数据
async function handler(req: ApiRequestProps, res: NextApiResponse) {
  const { appId } = YourAPIBodySchema.parse(req.body);
  const result = await getData(appId);

  return result;  // 未验证出参结构
}

Correct approach:

// ✅ 正确: 验证出参
async function handler(req: ApiRequestProps, res: NextApiResponse) {
  const { appId } = YourAPIBodySchema.parse(req.body);
  const result = await getData(appId);

  return YourAPIResponseSchema.parse(result);
}

Issue 5: Improper Schema reuse

Bad approach:

// ❌ 重复定义相同的结构
export const Schema1 = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string()
});

export const Schema2 = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string()
});

Correct approach:

// ✅ 抽取公共 Schema
export const BaseUserSchema = z.object({
  id: z.string().meta({ description: 'ID' }),
  name: z.string().meta({ description: '名称' }),
  email: z.string().email().meta({ description: '邮箱' })
});

export const Schema1 = z.object({
  user: BaseUserSchema
});

export const Schema2 = z.object({
  users: z.array(BaseUserSchema)
});

Quick reference

API declaration template

/* ============================================================================
 * API: [简短功能描述]
 * Route: [HTTP 方法] [完整路由路径]
 * Method: [GET/POST/PUT/DELETE]
 * Description: [详细功能说明]
 * Tags: [['模块', '子模块', '操作类型']]
 * ============================================================================ */

Common tags

  • Module tags: App, User, Chat, Workflow, Dataset
  • Operation type: Read, Write, Delete, Update
  • Other: Admin, Public, Internal

Common Zod validation methods

// 字符串
z.string()                    // 字符串
  .min(2)                     // 最小长度
  .max(50)                    // 最大长度
  .email()                    // 邮箱格式
  .url()                      // URL 格式
  .uuid()                     // UUID 格式

// 数字
z.number()                    // 数字
  .int()                      // 整数
  .positive()                 // 正数
  .min(0)                     // 最小值
  .max(100)                   // 最大值

// 布尔
z.boolean()                   // 布尔值

// 日期
z.date()                      // 日期对象
  .or(z.string())             // 或日期字符串

// 枚举
z.enum(['active', 'inactive'])  // 枚举值
z.nativeEnum(MyEnum)           // TypeScript 枚举

// 数组
z.array(z.string())           // 字符串数组
  .min(1)                     // 最小长度
  .max(10)                    // 最大长度

// 可选
z.string().optional()         // 可选 (undefined)
z.string().nullish()          // 可空 (undefined | null)

// 对象
z.object({                    // 对象
  name: z.string(),
  age: z.number()
})

// 继承
PaginationSchema.extend({     // 扩展
  keyword: z.string()
})

// 联合类型
z.union([z.string(), z.number()])  // 字符串或数字
z.discriminator('type', {          // 判别联合
  type1: Type1Schema,
  type2: Type2Schema
})

Meta field description

z.string().meta({
  example: 'value',              // 示例值 (必填)
  description: '字段说明'         // 字段描述 (必填)
})

TypeScript type export

// Schema 定义
export const UserSchema = z.object({
  id: z.string(),
  name: z.string()
});

// 导出类型 (命名规范: camelCase)
export type userType = z.infer<typeof UserSchema>;

// 或使用 PascalCase
export type UserType = z.infer<typeof UserSchema>;

Reference resources

In-project examples

  • API Schema example: /Volumes/code/fastgpt-pro/FastGPT/packages/global/openapi/core/app/log/api.ts
  • API implementation example: /Volumes/code/fastgpt-pro/FastGPT/projects/app/src/pages/api/core/app/logs/list.ts
  • Pagination Schema: packages/global/openapi/api.ts

Related documentation

  • Zod official documentation: https://zod.dev/
  • FastGPT API specification: .claude/skills/pr-review/fastgpt-style-guide.md
  • PR Review review dimensions: .claude/skills/pr-review/code-quality-standards.md

Version: 1.0 Last Updated: 2026-01-27 Maintainer: FastGPT Development Team