关于 frappe-core-api
面向 Frappe 开发者的 REST、RPC 与 Webhook 集成模式指南,涉及 API 认证、文档 CRUD、自定义方法和事件通知。内容还覆盖版本差异、分页、权限检查及签名验证等安全实践。
主要功能
- 01REST 文档 CRUD 与版本端点
- 02Token、OAuth 2.0 和会话认证
- 03RPC 自定义方法调用
- 04Webhook 事件与签名验证
- 05分页、权限及输入校验指引
适用场景
- 01从外部客户端读写 Frappe 文档
- 02为集成配置 API 认证
- 03通过 RPC 调用自定义服务端逻辑
- 04接收并验证文档变更 Webhook
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ impertio-studio/ frappe_claude_skill_package --skill frappe-core-api安装后在 Claude Code 中输入/frappe-core-api确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/anti-patterns.mdauthentication-methods.mdexamples.mdrest-api-reference.mdrpc-api-reference.mdwebhooks-reference.md详细介绍
Frappe API 模式
用于与 Frappe 集成的 REST、RPC 和 Webhook 确定性模式。
决策树
What do you need?
├── CRUD on documents (external client)
│ ├── v14: REST /api/resource/{doctype}
│ └── v15+: REST /api/v2/document/{doctype} (new) or /api/resource/ (still works)
│
├── Call custom server logic (external client)
│ └── RPC: POST /api/method/{dotted.path.to.function}
│
├── Notify external systems on document events
│ └── Webhooks (configured in UI or via DocType)
│
├── Client-side calls (JavaScript in Frappe desk)
│ ├── frappe.xcall() — async/await (RECOMMENDED)
│ └── frappe.call() — callback/promise pattern
│
└── Authentication method?
├── Server-to-server integration → Token Auth (RECOMMENDED)
├── Third-party app / mobile → OAuth 2.0
├── Browser session (short-lived) → Session/Cookie Auth
└── Quick scripting / testing → Token Auth
身份验证方法
Token 身份验证(集成推荐使用)
headers = {
'Authorization': 'token api_key:api_secret',
'Accept': 'application/json',
'Content-Type': 'application/json'
}
生成密钥:User > Settings > API Access > Generate Keys。务必立即保存 API secret——它只会显示一次。
Basic 身份验证(另一种 Token 格式)
import base64
credentials = base64.b64encode(b'api_key:api_secret').decode()
headers = {'Authorization': f'Basic {credentials}'}
OAuth 2.0(第三方应用)
# Step 1: Authorization redirect
GET /api/method/frappe.integrations.oauth2.authorize
?client_id={id}&response_type=code&scope=openid all
&redirect_uri={uri}&state={random}
# Step 2: Exchange code for token
POST /api/method/frappe.integrations.oauth2.get_token
grant_type=authorization_code&code={code}
&redirect_uri={uri}&client_id={id}
# Step 3: Use bearer token
Authorization: Bearer {access_token}
# Refresh token
POST /api/method/frappe.integrations.oauth2.get_token
grant_type=refresh_token&refresh_token={token}&client_id={id}
会话/Cookie 身份验证
session = requests.Session()
session.post(url + '/api/method/login', json={'usr': 'email', 'pwd': 'pass'})
# Subsequent requests use session cookie automatically
会话 Cookie 会在约 3 天后过期。切勿用于长时间运行的集成。
REST API:资源 CRUD
端点
| 操作 | 方法 | v14 端点 | v15+ v2 端点 |
|---|---|---|---|
| 列出 | GET | /api/resource/{doctype} | /api/v2/document/{doctype} |
| 创建 | POST | /api/resource/{doctype} | /api/v2/document/{doctype} |
| 读取 | GET | /api/resource/{doctype}/{name} | /api/v2/document/{doctype}/{name} |
| 更新 | PUT | /api/resource/{doctype}/{name} | PATCH /api/v2/document/{doctype}/{name} |
| 删除 | DELETE | /api/resource/{doctype}/{name} | DELETE /api/v2/document/{doctype}/{name} |
| 复制 | — | — | GET /api/v2/document/{doctype}/{name}/copy [v15+] |
| 文档方法 | — | — | POST /api/v2/document/{doctype}/{name}/method/{method} [v15+] |
务必包含 Accept: application/json 标头——否则 Frappe 可能会返回 HTML。
列表参数
| 参数 | 类型 | 描述 | 默认值 |
|---|---|---|---|
fields | JSON 数组 | 要返回的字段 | ["name"] |
filters | JSON 数组 | AND 条件 | 无 |
or_filters | JSON 数组 | OR 条件 | 无 |
order_by | string | 排序表达式 | modified desc |
limit_start | int | 分页偏移量 | 0 |
limit_page_length | int | 页面大小 | 20 |
limit | int | limit_page_length 的别名 [v15+] | — |
debug | bool | 在响应中显示 SQL | false |
筛选运算符
filters = [["status", "=", "Open"]]
filters = [["amount", ">", 1000]]
filters = [["status", "in", ["Open", "Pending"]]]
filters = [["date", "between", ["2024-01-01", "2024-12-31"]]]
filters = [["reference", "is", "set"]] # NOT NULL
filters = [["reference", "is", "not set"]] # IS NULL
filters = [["name", "like", "%INV%"]]
filters = [["status", "not in", ["Cancelled"]]]
完整运算符列表:=、!=、>、<、>=、<=、like、not like、in、not in、is、between。
分页模式
import json, requests
def get_all_records(doctype, headers, base_url, page_size=100):
all_data, offset = [], 0
while True:
params = {
'fields': json.dumps(["name", "modified"]),
'limit_start': offset,
'limit_page_length': page_size
}
resp = requests.get(f'{base_url}/api/resource/{doctype}',
params=params, headers=headers)
data = resp.json().get('data', [])
if not data:
break
all_data.extend(data)
if len(data) < page_size:
break
offset += page_size
return all_data
创建包含子表的文档
requests.post(f'{base_url}/api/resource/Sales Order', json={
"customer": "CUST-001",
"items": [
{"item_code": "ITEM-001", "qty": 5, "rate": 100},
{"item_code": "ITEM-002", "qty": 2, "rate": 250}
]
}, headers=headers)
更新(部分)
# Only specified fields are changed
requests.put(f'{base_url}/api/resource/Customer/CUST-001',
json={"customer_group": "Premium"}, headers=headers)
文件上传
requests.post(f'{base_url}/api/method/upload_file',
files={'file': ('doc.pdf', open('doc.pdf', 'rb'), 'application/pdf')},
data={'doctype': 'Customer', 'docname': 'CUST-001', 'is_private': 1},
headers={'Authorization': 'token api_key:api_secret'})
# NOTE: Do NOT set Content-Type header — requests sets multipart boundary automatically
RPC API:自定义方法
服务端端点
@frappe.whitelist()
def get_balance(customer):
"""GET /api/method/myapp.api.get_balance?customer=CUST-001"""
return frappe.db.get_value("Customer", customer, "outstanding_amount")
@frappe.whitelist(methods=["POST"])
def create_payment(customer, amount):
"""POST /api/method/myapp.api.create_payment"""
if not frappe.has_permission("Payment Entry", "create"):
frappe.throw(_("Not permitted"), frappe.PermissionError)
pe = frappe.new_doc("Payment Entry")
pe.party_type = "Customer"
pe.party = customer
pe.paid_amount = float(amount)
pe.insert()
return pe.name
@frappe.whitelist(allow_guest=True)
def public_status():
"""No authentication required."""
return {"status": "ok"}
装饰器选项
| 选项 | 效果 | 版本 |
|---|---|---|
allow_guest=True | 无需身份验证 | 全部 |
methods=["POST"] | 限制 HTTP 方法 | [v14+] |
xss_safe=True | 跳过响应的 XSS 转义 | 全部 |
响应结构
// RPC success
{"message": "return_value"}
// REST success
{"data": {...}}
// Error
{"exc_type": "ValidationError", "_server_messages": "[{\"message\": \"...\"}]"}
客户端调用(JavaScript)
// RECOMMENDED: async/await with frappe.xcall
const result = await frappe.xcall('myapp.api.get_balance', {
customer: 'CUST-001'
});
// Alternative: frappe.call with promise
frappe.call({
method: 'myapp.api.get_balance',
args: {customer: 'CUST-001'},
freeze: true,
freeze_message: __('Loading...')
}).then(r => console.log(r.message));
// Document method (frm.call)
frm.call('get_linked_doc', {throw_if_missing: true})
.then(r => console.log(r.message));
标准 frappe.client 方法
| 方法 | 端点 | 用途 |
|---|---|---|
frappe.client.get_value | POST | 获取单个字段值 |
frappe.client.get_list | POST | 根据筛选条件列出文档 |
frappe.client.get | POST | 获取完整文档 |
frappe.client.insert | POST | 创建文档 |
frappe.client.save | POST | 更新文档 |
frappe.client.delete | POST | 删除文档 |
frappe.client.submit | POST | 提交文档 |
frappe.client.cancel | POST | 取消文档 |
frappe.client.get_count | POST | 统计文档数量 |
Webhook
通过 UI 中的 Webhook DocType 进行配置。事件:
| 事件 | 触发条件 |
|---|---|
after_insert | 创建新文档后 |
on_update | 每次保存时 |
on_submit | 提交后(docstatus=1) |
on_cancel | 取消后(docstatus=2) |
on_trash | 删除前 |
on_update_after_submit | 修订后 |
on_change | 每次更改时 |
安全性:务必设置 Webhook Secret。Frappe 会添加 X-Frappe-Webhook-Signature 标头,其中包含经 base64 编码的负载 HMAC-SHA256。请在接收端验证。
条件:使用 Jinja2——{{ doc.grand_total > 10000 }}。
完整的处理程序示例请参阅 references/webhooks-reference.md。
HTTP 状态码
| 代码 | 含义 | 常见原因 |
|---|---|---|
200 | 成功 | — |
400 | 错误请求 | 验证错误 |
401 | 未授权 | 缺少或无效的身份验证信息 |
403 | 禁止访问 | 无权执行此操作 |
404 | 未找到 | 文档不存在 |
417 | 期望失败 | 服务端异常(frappe.throw) |
429 | 请求频率受限 | 请求过多 |
500 | 服务端错误 | 未处理的异常 |
关键规则
- 始终在 API 请求中包含
Accept: application/json标头 - 始终在
@frappe.whitelist()方法中添加权限检查 - 始终验证并清理白名单方法中的输入
- 始终使用参数化查询 — 绝不通过字符串插值构造 SQL
- 始终为外部
requests调用设置timeout=30 - 始终将凭据存储在
frappe.conf或环境变量中 — 绝不硬编码 - 始终使用 HMAC-SHA256 验证 Webhook 签名
- 始终对列表响应进行分页 — 绝不返回无界结果集
- 绝不在会更改状态的端点上使用
allow_guest=True - 绝不记录凭据或敏感数据
- 绝不将 Administrator API 密钥用于集成 — 应创建专用 API 用户
反模式
| 不要这样做 | 应这样做 |
|---|---|
| 白名单中不检查权限 | 操作前调用 frappe.has_permission() |
frappe.db.sql(f"...{user_input}") | 使用参数化 %s 查询 |
allow_guest=True + 更改状态 | 要求进行身份验证 |
| 不限量地返回所有记录 | 使用 limit_page_length 分页 |
| 硬编码 API 凭据 | frappe.conf.get("api_key") |
| 同步执行繁重处理 | 对长时间任务使用 frappe.enqueue() |
| 外部调用未设置超时 | requests.get(url, timeout=30) |
| 响应格式不一致 | 始终返回 {"status": "...", "data": ...} |
版本差异
| 功能 | v14 | v15 | v16 |
|---|---|---|---|
/api/resource/(v1) | 是 | 是 | 是 |
/api/v2/document/(v2) | 否 | 是 | 是 |
/api/v2/doctype/{dt}/meta | 否 | 是 | 是 |
/api/v2/doctype/{dt}/count | 否 | 是 | 是 |
limit 别名参数 | 否 | 是 | 是 |
| OAuth2 的 PKCE | 有限支持 | 是 | 是 |
| Server Script 速率限制 | 否 | 是 | 是 |
| 通过 v2 URL 调用文档方法 | 否 | 是 | 是 |
参考文件
| 文件 | 内容 |
|---|---|
| authentication-methods.md | Token、Session、OAuth2,附代码示例 |
| rest-api-reference.md | 完整的 REST CRUD,包含筛选和分页 |
| rpc-api-reference.md | 白名单方法、frappe.call、frappe.xcall |
| webhooks-reference.md | Webhook 配置、安全性和处理程序示例 |
| anti-patterns.md | 常见错误及修复方法 |
| examples.md | Python/JS/cURL 客户端实现 |
相关技能
frappe-core-permissions— API 端点的权限系统frappe-core-database— API 方法背后的数据库查询frappe-syntax-hooks— Webhook 的 Hook 配置frappe-syntax-controllers— 通过 API 调用的控制器方法
已根据 Frappe 文档验证,日期为 2026-03-20 | Frappe v14/v15/v16
评论
支持 Markdown