frappe-api-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Frappe API Development

Frappe API 开发

Build secure, well-designed APIs using Frappe's REST and RPC patterns.
使用Frappe的REST和RPC模式构建安全、设计良好的API。

When to use

适用场景

  • Creating custom RPC endpoints (
    @frappe.whitelist
    )
  • Building REST API integrations
  • Implementing webhooks for external systems
  • Setting up API authentication (token, OAuth)
  • Exposing business logic to frontends
  • 创建自定义RPC端点(
    @frappe.whitelist
  • 构建REST API集成
  • 为外部系统实现Webhook
  • 设置API认证(令牌、OAuth)
  • 向前端暴露业务逻辑

Inputs required

所需输入

  • API purpose (CRUD, action, integration)
  • Authentication requirements (public, user, API key)
  • Permission requirements per endpoint
  • Request/response format expectations
  • API用途(CRUD、操作、集成)
  • 认证要求(公开、用户、API密钥)
  • 每个端点的权限要求
  • 请求/响应格式预期

Procedure

实施步骤

0) Choose API pattern

0) 选择API模式

NeedPattern
DocType CRUDUse built-in REST API
Custom actionRPC with
@frappe.whitelist
External callbackWebhook DocType
Batch operationsBackground job + status endpoint
需求模式
DocType CRUD使用内置REST API
自定义操作
@frappe.whitelist
的RPC
外部回调Webhook DocType
批量操作后台任务 + 状态端点

1) Built-in REST API (DocType CRUD)

1) 内置REST API(DocType CRUD)

Frappe provides automatic REST endpoints for all DocTypes:
bash
undefined
Frappe为所有DocType提供自动REST端点:
bash
undefined

Create

Create

POST /api/resource/Customer {"customer_name": "Acme Corp"}
POST /api/resource/Customer {"customer_name": "Acme Corp"}

Read

Read

GET /api/resource/Customer/CUST-001
GET /api/resource/Customer/CUST-001

Update

Update

PUT /api/resource/Customer/CUST-001 {"customer_name": "Acme Corporation"}
PUT /api/resource/Customer/CUST-001 {"customer_name": "Acme Corporation"}

Delete

Delete

DELETE /api/resource/Customer/CUST-001
DELETE /api/resource/Customer/CUST-001

List with filters

List with filters

GET /api/resource/Customer?filters=[["status","=","Active"]]
undefined
GET /api/resource/Customer?filters=[["status","=","Active"]]
undefined

2) Custom RPC endpoints

2) 自定义RPC端点

Create whitelisted methods in your app:
python
undefined
在你的应用中创建白名单方法:
python
undefined

my_app/api.py

my_app/api.py

import frappe
@frappe.whitelist() def process_order(order_id, action): """Process an order with the given action.""" # Always verify permissions doc = frappe.get_doc("Sales Order", order_id) if not frappe.has_permission("Sales Order", "write", doc): frappe.throw("Not permitted", frappe.PermissionError)
# Business logic
if action == "approve":
    doc.status = "Approved"
    doc.save()

return {"status": "success", "order": doc.name}
@frappe.whitelist(allow_guest=True) def public_endpoint(): """Public endpoint - no auth required.""" return {"message": "Hello, World!"}

Call via:
```bash
POST /api/method/my_app.api.process_order
{"order_id": "SO-001", "action": "approve"}
import frappe
@frappe.whitelist() def process_order(order_id, action): """Process an order with the given action.""" # Always verify permissions doc = frappe.get_doc("Sales Order", order_id) if not frappe.has_permission("Sales Order", "write", doc): frappe.throw("Not permitted", frappe.PermissionError)
# Business logic
if action == "approve":
    doc.status = "Approved"
    doc.save()

return {"status": "success", "order": doc.name}
@frappe.whitelist(allow_guest=True) def public_endpoint(): """Public endpoint - no auth required.""" return {"message": "Hello, World!"}

调用方式:
```bash
POST /api/method/my_app.api.process_order
{"order_id": "SO-001", "action": "approve"}

3) Implement authentication

3) 实现认证

API Key + Secret (recommended for integrations):
bash
undefined
API密钥 + 密钥(集成推荐):
bash
undefined

Header format

Header format

Authorization: token api_key:api_secret

**Bearer Token:**
```bash
Authorization: Bearer <token>
Session (for logged-in users): Automatic via cookies.
Authorization: token api_key:api_secret

**Bearer令牌:**
```bash
Authorization: Bearer <token>
会话(针对已登录用户): 通过Cookie自动处理。

4) Permission checks

4) 权限检查

ALWAYS check permissions in RPC methods:
python
@frappe.whitelist()
def sensitive_action(docname):
    doc = frappe.get_doc("My DocType", docname)
    
    # Check document-level permission
    if not frappe.has_permission("My DocType", "write", doc):
        frappe.throw("Not permitted", frappe.PermissionError)
    
    # Check role-based permission
    if "Manager" not in frappe.get_roles():
        frappe.throw("Manager role required")
    
    # Proceed with action
    ...
务必在RPC方法中检查权限:
python
@frappe.whitelist()
def sensitive_action(docname):
    doc = frappe.get_doc("My DocType", docname)
    
    # Check document-level permission
    if not frappe.has_permission("My DocType", "write", doc):
        frappe.throw("Not permitted", frappe.PermissionError)
    
    # Check role-based permission
    if "Manager" not in frappe.get_roles():
        frappe.throw("Manager role required")
    
    # Proceed with action
    ...

5) Input validation

5) 输入验证

python
@frappe.whitelist()
def create_item(name, qty, price):
    # Validate required fields
    if not name:
        frappe.throw("Name is required")
    
    # Validate types
    qty = frappe.utils.cint(qty)
    price = frappe.utils.flt(price)
    
    # Validate ranges
    if qty <= 0:
        frappe.throw("Quantity must be positive")
    
    # Proceed
    ...
python
@frappe.whitelist()
def create_item(name, qty, price):
    # Validate required fields
    if not name:
        frappe.throw("Name is required")
    
    # Validate types
    qty = frappe.utils.cint(qty)
    price = frappe.utils.flt(price)
    
    # Validate ranges
    if qty <= 0:
        frappe.throw("Quantity must be positive")
    
    # Proceed
    ...

6) Response format

6) 响应格式

Success response:
python
return {
    "status": "success",
    "data": {...}
}
Error handling:
python
undefined
成功响应:
python
return {
    "status": "success",
    "data": {...}
}
错误处理:
python
undefined

User-facing error

User-facing error

frappe.throw("Validation failed", title="Error")
frappe.throw("Validation failed", title="Error")

Permission error

Permission error

frappe.throw("Not allowed", frappe.PermissionError)
frappe.throw("Not allowed", frappe.PermissionError)

Standard exceptions become {"exc_type": "...", "exc": "..."}

Standard exceptions become {"exc_type": "...", "exc": "..."}

undefined
undefined

7) Background jobs for long operations

7) 长操作的后台任务

python
@frappe.whitelist()
def start_export(filters):
    job = frappe.enqueue(
        "my_app.jobs.run_export",
        filters=filters,
        queue="long",
        timeout=600
    )
    return {"job_id": job.id}

@frappe.whitelist()
def check_job_status(job_id):
    from frappe.utils.background_jobs import get_job
    job = get_job(job_id)
    return {"status": job.get_status()}
python
@frappe.whitelist()
def start_export(filters):
    job = frappe.enqueue(
        "my_app.jobs.run_export",
        filters=filters,
        queue="long",
        timeout=600
    )
    return {"job_id": job.id}

@frappe.whitelist()
def check_job_status(job_id):
    from frappe.utils.background_jobs import get_job
    job = get_job(job_id)
    return {"status": job.get_status()}

Verification

验证

  • Endpoint responds correctly to valid requests
  • Permission errors returned for unauthorized access
  • Input validation rejects invalid data
  • Error responses are structured and helpful
  • Run:
    bench --site <site> console
    → test endpoint manually
  • 端点对有效请求响应正确
  • 对未授权访问返回权限错误
  • 输入验证拒绝无效数据
  • 错误响应结构化且有帮助
  • 运行:
    bench --site <site> console
    → 手动测试端点

Failure modes / debugging

故障模式/调试

  • Method not found: Check module path in URL matches Python path
  • Permission denied: Verify
    @frappe.whitelist()
    decorator and user permissions
  • CSRF error: Use proper auth headers for API calls
  • 500 error: Check error logs:
    bench --site <site> show-log
  • 方法未找到:检查URL中的模块路径是否与Python路径匹配
  • 权限被拒绝:验证
    @frappe.whitelist()
    装饰器和用户权限
  • CSRF错误:API调用使用正确的认证头
  • 500错误:查看错误日志:
    bench --site <site> show-log

Escalation

扩展参考

  • For OAuth integration, see references/oauth.md
  • For webhook patterns, see references/webhooks.md
  • For rate limiting, see references/rate-limiting.md
  • 关于OAuth集成,请查看 references/oauth.md
  • 关于Webhook模式,请查看 references/webhooks.md
  • 关于速率限制,请查看 references/rate-limiting.md

References

参考文档

  • references/rest-api.md - REST API details
  • references/authentication.md - Auth patterns
  • references/permissions.md - Permission system
  • references/webhooks.md - Outbound webhooks
  • references/rest-api.md - REST API详情
  • references/authentication.md - 认证模式
  • references/permissions.md - 权限系统
  • references/webhooks.md - 出站Webhook

Guardrails

防护准则

  • Always validate input: Never trust client data; validate type, length, and format server-side
  • Use permission callbacks: Check
    frappe.has_permission()
    explicitly in whitelisted methods
  • Sanitize user input: Use
    frappe.db.escape()
    for SQL, avoid
    eval()
    and dynamic code execution
  • Handle rate limiting: Implement rate limits for public APIs to prevent abuse
  • Return structured errors: Use
    frappe.throw()
    with proper HTTP status codes
  • 始终验证输入:永远不要信任客户端数据;在服务端验证类型、长度和格式
  • 使用权限回调:在白名单方法中显式检查
    frappe.has_permission()
  • 清理用户输入:SQL查询使用
    frappe.db.escape()
    ,避免
    eval()
    和动态代码执行
  • 处理速率限制:为公开API实现速率限制以防止滥用
  • 返回结构化错误:使用
    frappe.throw()
    并返回正确的HTTP状态码

Common Mistakes

常见错误

MistakeWhy It FailsFix
Missing
@frappe.whitelist()
Method returns "Method not found" errorAdd decorator to expose method via API
Using GET for mutationsViolates REST conventions, CSRF issuesUse POST/PUT/DELETE for data changes
Not handling errors500 errors expose stack tracesWrap in try/except, use
frappe.throw()
Exposing sensitive dataSecurity breachFilter response fields, check permissions
Missing
allow_guest=True
Public endpoints return 403Add
@frappe.whitelist(allow_guest=True)
for unauthenticated access
SQL injection in queriesDatabase compromiseUse Query Builder or
frappe.db.escape()
错误失败原因修复方法
缺少
@frappe.whitelist()
方法返回“Method not found”错误添加装饰器以通过API暴露方法
使用GET进行数据修改违反REST规范,存在CSRF问题使用POST/PUT/DELETE进行数据变更
未处理错误500错误暴露堆栈跟踪用try/except包裹,使用
frappe.throw()
暴露敏感数据安全漏洞过滤响应字段,检查权限
缺少
allow_guest=True
公开端点返回403为未认证访问添加
@frappe.whitelist(allow_guest=True)
查询中存在SQL注入数据库被攻陷使用查询构建器或
frappe.db.escape()