一个开箱即用的 Mock REST API 开源产品:fork 仓库 → 在线修改 mocks/ 模板 → 提交 → CI 自动部署到 Cloudflare Pages,即可获得一套可用的 Mock API 服务,用于产品演示、前端开发、接口测试。
- 框架:Next.js(App Router)Route Handlers
- 数据生成:mock.js 语法(全量标签原子化)+ Faker.js(按需引用)
- 构建:GitHub Actions 编译期静态分析 + 原子按需打包(Tailwind 模式)
- 部署:Cloudflare Pages(重依赖不进运行时 bundle,规避体积限制)
| 📁 目录即路径 | mocks/users/[id]/get.ts → GET /api/users/:id,1:1 映射 |
| 🧬 三种模板形态 | .json(mock.js 数据模板)/ .ts(默认)/ .js |
| 🎲 运行时随机渲染 | 每次请求原子方法现场生成,数据不固化 |
| 📦 按需打包 | 模板用到什么标签,bundle 只含对应原子(Faker 全量数 MB 不进运行时) |
| 🔀 Route Handler 能力全集 | 状态码、自定义 Header、{ delay } 慢网络、请求 body/cookies 读取、Response 完全接管 |
| 📄 文档自动生成 | 构建产出 /openapi.json + /docs(Swagger UI + 样例预览) |
| ⚡ 构建缓存 | 模板 hash 比对 + actions cache + Next.js 增量缓存,只改一个文件的重活仅限该文件 |
- Fork 本仓库
- 在 fork 后的仓库 Settings → Secrets and variables → Actions 添加两个 secret:
CLOUDFLARE_API_TOKEN:Cloudflare 的 API Token(权限:Account → Cloudflare Pages → Edit)CLOUDFLARE_ACCOUNT_ID:Cloudflare 账号 ID(Dashboard 首页右侧)- (可选)
CLOUDFLARE_PROJECT_NAME:Pages 项目名,默认mock-api
- 在 Cloudflare Dashboard 手动创建同名 Pages 项目一次(首次部署需要)
- 之后每次 push 到 main,CI 自动构建部署
也可以在 Cloudflare Pages 中直接连接 GitHub 仓库:构建命令 pnpm build:cf,输出目录 .vercel/output/static。
pnpm install
pnpm dev # 自动编译 mocks/ 后启动 http://localhost:3000- API 根路径前缀
/api,如GET http://localhost:3000/api/users - 文档页
http://localhost:3000/docs - 构建产物验证:
pnpm build(Node 运行时)或pnpm build:cf(Cloudflare Pages 产物)
仓库内置 AI 指令文件(CLAUDE.md / AGENTS.md)与生成技能(.claude/skills/mock-generator/)。用 Claude Code 或 Codex 打开仓库后,直接口述需求:
"给我一个商品详情接口,路径 /api/products/:id,返回名称、价格、库存、图片"
AI 会自动:查标签注册表(packages/atoms/*/atoms.json)→ 按目录约定生成 mocks/products/[id]/get.json → 运行 pnpm build:mocks 验证 → 汇报结果。你 review 后提交即自动部署。
- Claude Code:自动加载
CLAUDE.md;也可用/mock-generator显式触发 - Codex:自动加载
AGENTS.md
正确性有硬兜底:pnpm build:mocks 是构建期校验器(未知标签、路径冲突、语法错误全部报错),AI 生成错误的文件根本部署不上去。
mocks/
├── users/
│ ├── get.ts # GET /api/users
│ └── [id]/ # 动态段:/api/users/:id
│ ├── get.json # GET /api/users/:id
│ ├── put.ts # PUT /api/users/:id
│ └── delete.ts # DELETE /api/users/:id
└── login/
└── post.ts # POST /api/login
- 方法文件:
get/post/put/patch/delete(全小写),扩展名.ts(默认)/.js/.json - 同一路径下多个方法文件合并生成一个 Route Handler;请求未定义的方法自动 405
- 同名方法文件同时存在多个扩展名(如
get.ts+get.json)会在构建期报错 - 动态段:目录名
[id]、[...slug]
{
"page": "{{ctx.query.page:number}}",
"list|10": [
{
"id|+1": 1,
"name": "@cname",
"avatar": "{{faker.image.avatar()}}",
"address": "@city@county@cword(6, 12)号"
}
]
}mock.js 标签(原语法):@cname、@datetime("yyyy-MM-dd")、@pick(a, b, c) 等。全部标签见 packages/atoms/mockjs/atoms.json。
mock.js 规则(key 内 |):
| 规则 | 示例 | 含义 |
|---|---|---|
|n |
"list|10": [...] |
生成 n 个 |
|min-max |
"age|18-60": 1 / "list|2-4": [...] |
随机 min–max 个 |
|min-max.dmin-dmax |
"price|10-9999.0-2": 1 |
随机浮点,小数位 dmin–dmax |
|+step |
"id|+1": 1 |
从起始值递增 |
DSL({{...}} 表达式,见下文规范):Faker 引用、请求上下文注入。字符串内可混合拼接(如上例的 address)。
export default (ctx: MockContext) => {
const { params, headers, body } = ctx.request;
if (headers["x-role"] !== "admin") {
return { status: 403, body: { message: "forbidden" } };
}
return {
status: 200,
delay: 500, // 模拟慢网络(毫秒)
headers: { "x-mock": "ok" },
body: {
id: params.id,
name: "{{faker.person.fullName}}", // DSL 字符串照常可用
updatedAt: "@now",
},
};
};- 模板必须
export default一个接收MockContext的函数(全局类型,无需 import) - 返回值三种形式:
- 直接返回对象 →
200+application/json { status, headers, body, delay }→ 完整响应控制Response→ 完全接管(重定向、流式响应、cookie 设置等任意能力)
- 直接返回对象 →
- 模板内不要直接调用 faker/mockjs 函数(会破坏静态分析),数据值一律用 DSL 字符串
ctx.request:method / url / params / query / headers / cookies / body(JSON 自动解析,原文在bodyText)
{{domain.path[:type]}}
| 域 | 说明 | 示例 |
|---|---|---|
ctx |
请求上下文(保留域) | {{ctx.params.id}}、{{ctx.query.page:number}}、{{ctx.headers.x-token}}、{{ctx.cookies.session}}、{{ctx.body.field}} |
faker |
Faker.js | {{faker.person.fullName}}、{{faker.number.int(1, 99)}} |
@ 前缀 |
mock.js 标签(例外通道,原语法) | @cname、@email |
- 类型转换:
:number(v1),预留:string/:boolean - 转义:
\{{...}}、\@xxx按字面量输出 - 新 mock 库接入协议:域注册 + 原子注册 + 解析器插件(见
docs/requirements.md第 4.2 节)
| 模块 | API | 示范能力 |
|---|---|---|
| 基础 | GET /api/health |
静态 JSON |
| 登录 | POST /api/login |
body 读取、条件返回、错误状态码 |
| 用户 | GET /api/users |
query 分页注入 |
| 用户 | GET /api/users/[id] |
params 注入、混合字符串 |
| 用户 | PUT /api/users/[id] |
body 回显更新 |
| 用户 | DELETE /api/users/[id] |
header 检查、500 模拟 |
| 商品 | GET /api/products |
Faker 商品数据 |
| 商品 | GET /api/products/[id] |
浮点规则、数组规则 |
| 订单 | GET /api/orders |
|n、|+1、@pick |
| 订单 | GET /api/orders/[id] |
{ delay } 慢网络 |
mocks/ # mock 模板(你编辑的目录)
app/api/ # 生成的 Route Handlers(构建产物,勿手改)
app/docs/ # /docs 页面(Swagger UI + 样例预览)
packages/atoms/mockjs/ # mock.js 全量标签原子库(注册表 atoms.json)
packages/atoms/faker/ # Faker 原子封装(注册表 faker-atoms.json)
packages/compiler/ # DSL 解析 + JSON/TS 模板编译器 + OpenAPI 生成
scripts/build-mocks.mjs # 编译入口(hash 缓存 / 产物清理 / 样例渲染)
lib/mock-runtime.ts # 运行时胶水(ctx 组装 / 返回值适配)
types/mock-context.d.ts # MockContext 全局类型
docs/requirements.md # 需求文档(决策记录见第 9 章)
mocks/** 模板源文件
│ CI(Node 环境)
│ ① 模板 hash 比对(未变更跳过)
│ ② 静态分析标签集合
│ ③ mock.js 规则翻译 + DSL 替换为原子调用
│ ④ 生成 1:1 route.ts(TS 模板代码原样保留)
▼
app/api/**/route.ts 生成物(tree-shaking 按需打包原子)
│ next build + @cloudflare/next-on-pages
▼
Cloudflare Pages 运行时每次请求原子现场生成,随机性保留
- 更多 mock 库接入(DSL 接入协议已就绪)
- 可视化编辑管理界面(触发条件:非开发者需要改 mock 数据的真实需求)
- TS 模板的可选
export const meta声明(OpenAPI schema 增强)