Token导航 LogoToken导航TokenDH.com
待分类敏感数据github未标认证来源可访问clear审计通过

frappe-apifrappe API 文档

Agent Skill

用于辅助 API 设计、接口文档、请求响应结构和服务集成说明。它适合让 Agent 梳理 endpoint、生成 OpenAPI 草稿、检查字段命名、整理错误码或辅助前后端联调。使用时需要确认真实业务语义、鉴权方式、分页和错误处理规则;涉及生成接口文档时,应避免凭空补字段,最好从现有代码、schema 或接口样例中提取事实。

总安装

1,247

周安装

53

GitHub Stars

12

下载量

437
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:frappe-api(frappe API 文档)
来源仓库:https://github.com/sergio-bershadsky/ai
仓库路径:skills/frappe-api
安装命令:
npx skills add https://github.com/sergio-bershadsky/ai --skill frappe-api
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/sergio-bershadsky/ai --skill frappe-api

简介

用于辅助 API 设计和接口文档编写,支持 OpenAPI 草稿生成。

  • 支持字段命名检查、错误码整理和前后端联调辅助,确保语义准确。
  • 需结合现有代码或 schema 提取事实,避免凭空补字段或假设规则。
  • 安装前建议确认权限范围,防止误修改接口定义或触发构建流程。
  • 涉及鉴权、分页等逻辑时应以实际业务为准,不自行设定默认值。

SKILL.md

Frappe REST API Development

Create secure, well-documented REST API endpoints for Frappe Framework v15 following best practices for authentication, permission checking, and input validation.

When to Use

  • Building custom REST API endpoints
  • Exposing service layer methods via HTTP
  • Creating public/private API routes
  • Implementing webhook handlers
  • Building integrations with external systems

Arguments

/frappe-api <endpoint_name> [--doctype <doctype>] [--public]

Examples:

/frappe-api get_dashboard_stats
/frappe-api create_order --doctype "Sales Order"
/frappe-api webhook_handler --public

Procedure

Step 1: Gather API Requirements

Ask the user for:

  1. Endpoint Name (snake_case, e.g., get_dashboard_stats)
  2. HTTP Methods supported (GET, POST, PUT, DELETE)
  3. Authentication Type:

- Token (API Key + Secret) - Session (Cookie-based) - OAuth 2.0 - Public (no auth required - use sparingly)

  1. Parameters - Input parameters with types
  2. Related DocType (if applicable)
  3. Allowed Roles (who can access this endpoint)

Step 2: Design API Contract

Create the API specification:

Endpoint: /api/method/<app>.<module>.api.<endpoint_name>
Methods: GET, POST
Auth: Token | Session
Rate Limit: 100 req/min (if applicable)

Parameters:
  - name: param1
    type: string
    required: true
    description: Description of param1
  - name: param2
    type: integer
    required: false
    default: 10

Response:
  200:
    description: Success
    schema:
      message: object
  400:
    description: Validation Error
  403:
    description: Permission Denied

Step 3: Generate API Module Structure

Create <app>/<module>/api/<endpoint_name>.py:

"""
<Endpoint Name> API

<Brief description of what this API does>

Endpoints:
    GET/POST /api/method/<app>.<module>.api.<endpoint_name>.<method_name>

Authentication:
    Token: Authorization: token api_key:api_secret
    Session: Cookie-based after login

Example:
    curl -X POST "https://site.com/api/method/<app>.<module>.api.<endpoint_name>.create" \
        -H "Authorization: token api_key:api_secret" \
        -H "Content-Type: application/json" \
        -d '{"title": "Test"}'
"""

import frappe
from frappe import _
from frappe.utils import cint, cstr, flt
from typing import Optional, Any
from <app>.<module>.services.<service>_service import <Service>Service

# ──────────────────────────────────────────────────────────────────────────────
# API Endpoints
#
# v15 TYPE ANNOTATION VALIDATION:
# Frappe v15 automatically validates function parameter types based on
# Python type hints. For example, if you declare `limit: int`, passing
# a non-integer will raise a validation error automatically.
#
# TRANSACTION HANDLING:
# Frappe automatically commits on successful POST/PUT requests and
# rolls back on exceptions. Manual frappe.db.commit() is rarely needed.
# ──────────────────────────────────────────────────────────────────────────────

@frappe.whitelist()
def get(name: str) -> dict:
    """
    Get single document by name.

    Args:
        name: Document name/ID

    Returns:
        Document data

    Raises:
        frappe.DoesNotExistError: Document not found
        frappe.PermissionError: No read permission

    Example:
        GET /api/method/<app>.<module>.api.<endpoint>.get?name=DOC-00001
    """
    _check_permission("<DocType>", "read")

    service = <Service>Service()
    return {
        "success": True,
        "data": service.get(name)
    }

@frappe.whitelist()
def get_list(
    status: Optional[str] = None,
    limit: int = 20,
    offset: int = 0
) -> dict:
    """
    Get list of documents with optional filtering.

    Args:
        status: Filter by status
        limit: Maximum records to return (default: 20, max: 100)
        offset: Skip N records for pagination

    Returns:
        List of documents with pagination info

    Example:
        GET /api/method/<app>.<module>.api.<endpoint>.get_list?status=Draft&limit=10
    """
    _check_permission("<DocType>", "read")

    # Validate and sanitize inputs
    limit = min(cint(limit) or 20, 100)  # Cap at 100
    offset = max(cint(offset), 0)

    service = <Service>Service()
    filters = {}
    if status:
        filters["status"] = status

    data = service.repo.get_list(
        filters=filters,
        fields=["name", "title", "status", "date", "modified"],
        limit=limit,
        offset=offset
    )
    total = service.repo.get_count(filters)

    return {
        "success": True,
        "data": data,
        "pagination": {
            "total": total,
            "limit": limit,
            "offset": offset,
            "has_more": (offset + limit) < total
        }
    }

@frappe.whitelist(methods=["POST"])
def create(
    title: str,
    date: Optional[str] = None,
    description: Optional[str] = None
) -> dict:
    """
    Create new document.

    Args:
        title: Document title (required)
        date: Date in YYYY-MM-DD format
        description: Optional description

    Returns:
        Created document data

    Raises:
        frappe.ValidationError: Invalid input data
        frappe.PermissionError: No create permission

    Example:
        POST /api/method/<app>.<module>.api.<endpoint>.create
        Body: {"title": "New Document", "date": "2024-01-15"}
    """
    _check_permission("<DocType>", "create")

    # Validate required fields
    if not title or not cstr(title).strip():
        frappe.throw(_("Title is required"), frappe.ValidationError)

    service = <Service>Service()
    result = service.create({
        "title": cstr(title).strip(),
        "date": date or frappe.utils.today(),
        "description": description
    })

    frappe.db.commit()

    return {
        "success": True,
        "message": _("Document created successfully"),
        "data": result
    }

@frappe.whitelist(methods=["PUT", "POST"])
def update(
    name: str,
    title: Optional[str] = None,
    status: Optional[str] = None,
    description: Optional[str] = None
) -> dict:
    """
    Update existing document.

    Args:
        name: Document name (required)
        title: New title
        status: New status
        description: New description

    Returns:
        Updated document data

    Example:
        PUT /api/method/<app>.<module>.api.<endpoint>.update
        Body: {"name": "DOC-00001", "title": "Updated Title"}
    """
    _check_permission("<DocType>", "write")

    if not name:
        frappe.throw(_("Document name is required"), frappe.ValidationError)

    # Build update data from provided fields
    update_data = {}
    if title is not None:
        update_data["title"] = cstr(title).strip()
    if status is not None:
        update_data["status"] = status
    if description is not None:
        update_data["description"] = description

    if not update_data:
        frappe.throw(_("No fields to update"), frappe.ValidationError)

    service = <Service>Service()
    result = service.update(name, update_data)

    frappe.db.commit()

    return {
        "success": True,
        "message": _("Document updated successfully"),
        "data": result
    }

@frappe.whitelist(methods=["DELETE", "POST"])
def delete(name: str) -> dict:
    """
    Delete document.

    Args:
        name: Document name to delete

    Returns:
        Success confirmation

    Example:
        DELETE /api/method/<app>.<module>.api.<endpoint>.delete?name=DOC-00001
    """
    _check_permission("<DocType>", "delete")

    if not name:
        frappe.throw(_("Document name is required"), frappe.ValidationError)

    service = <Service>Service()
    service.repo.delete(name)

    frappe.db.commit()

    return {
        "success": True,
        "message": _("Document deleted successfully")
    }

@frappe.whitelist(methods=["POST"])
def submit(name: str) -> dict:
    """
    Submit document for processing.

    Args:
        name: Document name to submit

    Returns:
        Submitted document data

    Example:
        POST /api/method/<app>.<module>.api.<endpoint>.submit
        Body: {"name": "DOC-00001"}
    """
    _check_permission("<DocType>", "submit")

    service = <Service>Service()
    result = service.submit(name)

    frappe.db.commit()

    return {
        "success": True,
        "message": _("Document submitted successfully"),
        "data": result
    }

@frappe.whitelist(methods=["POST"])
def cancel(name: str, reason: Optional[str] = None) -> dict:
    """
    Cancel submitted document.

    Args:
        name: Document name to cancel
        reason: Cancellation reason

    Returns:
        Cancelled document data

    Example:
        POST /api/method/<app>.<module>.api.<endpoint>.cancel
        Body: {"name": "DOC-00001", "reason": "Customer request"}
    """
    _check_permission("<DocType>", "cancel")

    service = <Service>Service()
    result = service.cancel(name, reason)

    frappe.db.commit()

    return {
        "success": True,
        "message": _("Document cancelled successfully"),
        "data": result
    }

# ──────────────────────────────────────────────────────────────────────────────
# Bulk Operations
# ──────────────────────────────────────────────────────────────────────────────

@frappe.whitelist(methods=["POST"])
def bulk_update_status(names: list[str], status: str) -> dict:
    """
    Bulk update status for multiple documents.

    Args:
        names: List of document names
        status: New status to set

    Returns:
        Number of documents updated

    Example:
        POST /api/method/<app>.<module>.api.<endpoint>.bulk_update_status
        Body: {"names": ["DOC-001", "DOC-002"], "status": "Completed"}
    """
    _check_permission("<DocType>", "write")

    if not names or not isinstance(names, list):
        frappe.throw(_("Names must be a non-empty list"), frappe.ValidationError)

    valid_statuses = ["Draft", "Pending", "Completed", "Cancelled"]
    if status not in valid_statuses:
        frappe.throw(
            _("Invalid status. Must be one of: {0}").format(", ".join(valid_statuses)),
            frappe.ValidationError
        )

    service = <Service>Service()
    count = service.repo.bulk_update_status(names, status)

    frappe.db.commit()

    return {
        "success": True,
        "message": _("{0} documents updated").format(count),
        "data": {"updated_count": count}
    }

# ──────────────────────────────────────────────────────────────────────────────
# Helper Functions
# ──────────────────────────────────────────────────────────────────────────────

def _check_permission(doctype: str, ptype: str, doc: Any = None) -> None:
    """
    Check if current user has permission.

    Args:
        doctype: DocType to check
        ptype: Permission type (read, write, create, delete, submit, cancel)
        doc: Optional specific document

    Raises:
        frappe.PermissionError: If permission denied
    """
    if not frappe.has_permission(doctype, ptype, doc=doc):
        frappe.throw(
            _("You don't have permission to {0} {1}").format(ptype, doctype),
            frappe.PermissionError
        )

def _validate_request_data(data: dict, required: list[str]) -> None:
    """
    Validate request data has required fields.

    Args:
        data: Request data dict
        required: List of required field names

    Raises:
        frappe.ValidationError: If required fields missing
    """
    missing = [f for f in required if not data.get(f)]
    if missing:
        frappe.throw(
            _("Missing required fields: {0}").format(", ".join(missing)),
            frappe.ValidationError
        )

# ──────────────────────────────────────────────────────────────────────────────
# Public Endpoints (No Auth Required - Use with Caution!)
# ──────────────────────────────────────────────────────────────────────────────

@frappe.whitelist(allow_guest=True)
def ping() -> dict:
    """
    Health check endpoint (public).

    Returns:
        Server status

    Example:
        GET /api/method/<app>.<module>.api.<endpoint>.ping
    """
    return {
        "success": True,
        "message": "pong",
        "timestamp": frappe.utils.now()
    }

Step 4: Generate API v2 Endpoints (Optional)

For v2 REST API pattern, create custom routes in hooks.py:

# hooks.py

# Override standard DocType REST endpoints
override_doctype_dashboards = {
    "<DocType>": "<app>.<module>.api.<endpoint>.get_doctype_dashboard"
}

# Custom website routes for cleaner URLs
website_route_rules = [
    {"from_route": "/api/v2/<app>/<endpoint>", "to_route": "<app>.<module>.api.<endpoint>.handle_v2"},
]

Step 5: Generate API Tests

Create <app>/<module>/api/test_<endpoint_name>.py:

"""
Tests for <Endpoint Name> API
"""

import frappe
from frappe.tests import IntegrationTestCase

class TestAPI<EndpointName>(IntegrationTestCase):
    """API integration tests."""

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.test_user = cls._create_test_user()
        cls.test_doc = cls._create_test_document()

    @classmethod
    def _create_test_user(cls):
        """Create test user with API access."""
        if frappe.db.exists("User", "test_api@example.com"):
            return frappe.get_doc("User", "test_api@example.com")

        user = frappe.get_doc({
            "doctype": "User",
            "email": "test_api@example.com",
            "first_name": "Test",
            "last_name": "API User",
            "send_welcome_email": 0
        }).insert(ignore_permissions=True)
        user.add_roles("System Manager")
        return user

    @classmethod
    def _create_test_document(cls):
        """Create test document."""
        return frappe.get_doc({
            "doctype": "<DocType>",
            "title": "API Test Document",
            "date": frappe.utils.today()
        }).insert()

    def test_get_returns_document(self):
        """Test GET endpoint returns document."""
        from <app>.<module>.api.<endpoint_name> import get

        frappe.set_user(self.test_user.name)
        result = get(self.test_doc.name)

        self.assertTrue(result.get("success"))
        self.assertIsNotNone(result.get("data"))

    def test_get_list_with_pagination(self):
        """Test GET list with pagination."""
        from <app>.<module>.api.<endpoint_name> import get_list

        frappe.set_user(self.test_user.name)
        result = get_list(limit=5, offset=0)

        self.assertTrue(result.get("success"))
        self.assertIn("pagination", result)
        self.assertLessEqual(len(result["data"]), 5)

    def test_create_validates_input(self):
        """Test CREATE validates required fields."""
        from <app>.<module>.api.<endpoint_name> import create

        frappe.set_user(self.test_user.name)

        with self.assertRaises(frappe.ValidationError):
            create(title="")  # Empty title should fail

    def test_create_returns_document(self):
        """Test CREATE returns new document."""
        from <app>.<module>.api.<endpoint_name> import create

        frappe.set_user(self.test_user.name)
        result = create(title="New Test Doc", date=frappe.utils.today())

        self.assertTrue(result.get("success"))
        self.assertIsNotNone(result["data"].get("name"))

    def test_unauthorized_access_denied(self):
        """Test unauthenticated access is denied."""
        from <app>.<module>.api.<endpoint_name> import get

        frappe.set_user("Guest")

        with self.assertRaises(frappe.PermissionError):
            get(self.test_doc.name)

    def test_ping_public_access(self):
        """Test ping endpoint is publicly accessible."""
        from <app>.<module>.api.<endpoint_name> import ping

        frappe.set_user("Guest")
        result = ping()

        self.assertTrue(result.get("success"))
        self.assertEqual(result.get("message"), "pong")

Step 6: Show API Documentation Preview

## API Endpoint Preview

**Module:** <app>.<module>.api.<endpoint_name>
**Base URL:** /api/method/<app>.<module>.api.<endpoint_name>

### Endpoints:

| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| GET | .get | Token/Session | Get single document |
| GET | .get_list | Token/Session | List with pagination |
| POST | .create | Token/Session | Create document |
| PUT | .update | Token/Session | Update document |
| DELETE | .delete | Token/Session | Delete document |
| POST | .submit | Token/Session | Submit for processing |
| POST | .cancel | Token/Session | Cancel document |
| POST | .bulk_update_status | Token/Session | Bulk status update |
| GET | .ping | Public | Health check |

### Authentication:

Token auth (recommended for integrations)

curl -H "Authorization: token api_key:api_secret" \ https://site.com/api/method/<endpoint>

Session auth (for browser clients)

First login, then use session cookie


### Files to Create:

📁 /api/ ├── 📄 **init**.py ├── 📄 <endpoint_name>.py └── 📄 test_<endpoint_name>.py

---

Create this API module?

Step 7: Execute and Verify

After approval, create files and run tests:

bench --site <site> run-tests --module "<app>.<module>.api.test_<endpoint_name>"

Output Format

## API Created

**Module:** <app>.<module>.api.<endpoint_name>
**Endpoints:** 9

### Files Created:
- ✅ <endpoint_name>.py (API endpoints)
- ✅ test_<endpoint_name>.py (API tests)
- ✅ Updated __init__.py

### cURL Examples:

Get document

curl -X GET "https://site.com/api/method/<app>.<module>.api.<endpoint>.get?name=DOC-001" \ -H "Authorization: token api_key:api_secret"

Create document

curl -X POST "https://site.com/api/method/<app>.<module>.api.<endpoint>.create" \ -H "Authorization: token api_key:api_secret" \ -H "Content-Type: application/json" \ -d '{"title": "New Document"}'


### Next Steps:

1. Create API keys: Setup > User > API Access
2. Test endpoints with curl or Postman
3. Run API tests: `bench --site <site> run-tests --module <test_module>`

Rules

  1. Always Check Permissions — Every endpoint must call _check_permission() first
  2. Validate All Input — Never trust user input, validate and sanitize everything
  3. Type Annotations — Use Python type hints for v15 auto-validation
  4. Transaction Handling — Frappe auto-commits on successful requests; manual frappe.db.commit() rarely needed except in background jobs
  5. Public Endpoints — Use allow_guest=True sparingly, only for truly public data
  6. Error Handling — Use frappe.throw() with appropriate exception types
  7. Documentation — Every endpoint must have docstring with Args/Returns/Example
  8. ALWAYS Confirm — Never create files without explicit user approval

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

windsurf

29.68%
按下载量换算130

OpenCode

23.63%
按下载量换算103

Cursor

18.97%
按下载量换算83

Codex

10.86%
按下载量换算47

github-copilot

7.35%
按下载量换算32

Claude Code

3.51%
按下载量换算15

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills