frappe-api-development
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFrappe 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模式
| Need | Pattern |
|---|---|
| DocType CRUD | Use built-in REST API |
| Custom action | RPC with |
| External callback | Webhook DocType |
| Batch operations | Background job + status endpoint |
| 需求 | 模式 |
|---|---|
| DocType CRUD | 使用内置REST API |
| 自定义操作 | 带 |
| 外部回调 | Webhook DocType |
| 批量操作 | 后台任务 + 状态端点 |
1) Built-in REST API (DocType CRUD)
1) 内置REST API(DocType CRUD)
Frappe provides automatic REST endpoints for all DocTypes:
bash
undefinedFrappe为所有DocType提供自动REST端点:
bash
undefinedCreate
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"]]
undefinedGET /api/resource/Customer?filters=[["status","=","Active"]]
undefined2) Custom RPC endpoints
2) 自定义RPC端点
Create whitelisted methods in your app:
python
undefined在你的应用中创建白名单方法:
python
undefinedmy_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
undefinedAPI密钥 + 密钥(集成推荐):
bash
undefinedHeader 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
undefinedUser-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": "..."}
undefinedundefined7) 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: → test endpoint manually
bench --site <site> console
- 端点对有效请求响应正确
- 对未授权访问返回权限错误
- 输入验证拒绝无效数据
- 错误响应结构化且有帮助
- 运行:→ 手动测试端点
bench --site <site> console
Failure modes / debugging
故障模式/调试
- Method not found: Check module path in URL matches Python path
- Permission denied: Verify decorator and user permissions
@frappe.whitelist() - 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 explicitly in whitelisted methods
frappe.has_permission() - Sanitize user input: Use for SQL, avoid
frappe.db.escape()and dynamic code executioneval() - Handle rate limiting: Implement rate limits for public APIs to prevent abuse
- Return structured errors: Use with proper HTTP status codes
frappe.throw()
- 始终验证输入:永远不要信任客户端数据;在服务端验证类型、长度和格式
- 使用权限回调:在白名单方法中显式检查
frappe.has_permission() - 清理用户输入:SQL查询使用,避免
frappe.db.escape()和动态代码执行eval() - 处理速率限制:为公开API实现速率限制以防止滥用
- 返回结构化错误:使用并返回正确的HTTP状态码
frappe.throw()
Common Mistakes
常见错误
| Mistake | Why It Fails | Fix |
|---|---|---|
Missing | Method returns "Method not found" error | Add decorator to expose method via API |
| Using GET for mutations | Violates REST conventions, CSRF issues | Use POST/PUT/DELETE for data changes |
| Not handling errors | 500 errors expose stack traces | Wrap in try/except, use |
| Exposing sensitive data | Security breach | Filter response fields, check permissions |
Missing | Public endpoints return 403 | Add |
| SQL injection in queries | Database compromise | Use Query Builder or |
| 错误 | 失败原因 | 修复方法 |
|---|---|---|
缺少 | 方法返回“Method not found”错误 | 添加装饰器以通过API暴露方法 |
| 使用GET进行数据修改 | 违反REST规范,存在CSRF问题 | 使用POST/PUT/DELETE进行数据变更 |
| 未处理错误 | 500错误暴露堆栈跟踪 | 用try/except包裹,使用 |
| 暴露敏感数据 | 安全漏洞 | 过滤响应字段,检查权限 |
缺少 | 公开端点返回403 | 为未认证访问添加 |
| 查询中存在SQL注入 | 数据库被攻陷 | 使用查询构建器或 |