mcpField
F
frappe-syntax-whitelisted

frappe-syntax-whitelisted

github.com/impertio-studio開発198110 回インストール30 週間前に更新
リポジトリ

frappe-syntax-whitelisted について

Frappe の Python メソッドを HTTP API エンドポイントとして公開するためのリファレンススキルです。設定、入力検証、権限確認、クライアントからの呼び出し方を扱います。

主な機能

  1. 01ホワイトリストデコレーターの設定
  2. 02権限確認と入力検証
  3. 03エラーとレスポンス形式の処理
  4. 04JavaScript や REST からの API 呼び出し
  5. 05ゲスト用エンドポイントのレート制限

活用シーン

  1. 01Python 関数を API エンドポイントとして公開
  2. 02ドキュメントにアクセスするメソッドの保護
  3. 03トークン認証を使った外部クライアント接続

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/frappe_claude_skill_package --skill frappe-syntax-whitelisted

インストール後、Claude Code で/frappe-syntax-whitelistedを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
anti-patterns.md
client-calls.md
decorator-options.md
error-handling.md
examples.md
hooks.md
methods.md
parameter-handling.md
patterns.md
permission-patterns.md
response-patterns.md
syntax.md

詳細

Frappe の構文:ホワイトリスト登録されたメソッド

ホワイトリスト登録されたメソッドを使うと、/api/method/ 経由で Python 関数を HTTP API エンドポイントとして公開できます。

クイックリファレンス

import frappe
from frappe import _

# Authenticated endpoint (default)
@frappe.whitelist()
def get_customer_summary(customer):
    frappe.has_permission("Customer", "read", throw=True)
    return frappe.get_doc("Customer", customer).as_dict()

# Public endpoint — ALWAYS validate input thoroughly
@frappe.whitelist(allow_guest=True, methods=["POST"])
def submit_contact(name, email, message):
    if not name or not email:
        frappe.throw(_("Name and email required"), frappe.ValidationError)
    return {"success": True}

# Controller method — called via frm.call('method_name')
class SalesOrder(Document):
    @frappe.whitelist()
    def calculate_taxes(self, include_shipping=False):
        return {"tax": self.grand_total * 0.21}

エンドポイント URL:/api/method/myapp.module.function_name


デコレーターのシグネチャ[v14+]

@frappe.whitelist(
    allow_guest=False,   # True = accessible without login
    xss_safe=False,      # True = do NOT escape HTML in response
    methods=None,        # ["GET"], ["POST"], or ["GET","POST"] — default: all
    force_types=None     # True = require type annotations [v15+]
)
パラメーターデフォルト効果
allow_guestFalseTrue = ゲストロールで呼び出し可能。必ず追加の入力検証を行ってください
xss_safeFalseTrue = HTML がエスケープされません。サニタイズ済みの出力なしに絶対に使用しないでください
methodsNone(すべて)許可する HTTP メソッドを制限します
force_typesNoneTrue = すべてのパラメーターに型アノテーションが必須です[v15+]

詳細:decorator-options.md


判断フロー

What kind of endpoint?
|
+-- Standalone API (utility, integration, dashboard)?
|   --> @frappe.whitelist() on a module-level function
|   --> Call via: frappe.call('myapp.api.function')
|   --> URL: /api/method/myapp.api.function
|
+-- Document-specific action?
|   --> @frappe.whitelist() on a Document class method
|   --> Call via: frm.call('method_name')
|   --> URL: /api/method/run_doc_method (internal)
|
+-- Server Script (no-code)?
    --> Use Server Script DocType instead (no decorator needed)

Who may call the API?
|
+-- Anyone (including guests)?
|   --> allow_guest=True + thorough input validation + rate limiting
|
+-- Logged-in users only?
    +-- Specific role? --> frappe.only_for("RoleName")
    +-- DocType-level? --> frappe.has_permission(doctype, ptype, throw=True)
    +-- Document-level? --> frappe.has_permission(doctype, ptype, doc, throw=True)

Which HTTP methods?
|
+-- Read only? --> methods=["GET"]
+-- Write only? --> methods=["POST"]
+-- Both? --> methods=["GET","POST"] or default

権限パターン

すべてのホワイトリスト登録されたメソッド内で、必ず権限を確認してください。@frappe.whitelist() デコレーターが確認するのはユーザーがログインしていることだけであり、DocType またはドキュメントレベルの権限は確認しません。

# DocType-level permission (throw=True raises PermissionError automatically)
@frappe.whitelist()
def get_orders():
    frappe.has_permission("Sales Order", "read", throw=True)
    return frappe.get_all("Sales Order", limit=20)

# Document-level permission
@frappe.whitelist()
def get_order(name):
    frappe.has_permission("Sales Order", "read", name, throw=True)
    return frappe.get_doc("Sales Order", name).as_dict()

# Role-based restriction
@frappe.whitelist()
def admin_action():
    frappe.only_for("System Manager")  # throws if user lacks role
    return {"secret": "data"}

パターンの詳細:permission-patterns.md


パラメーターの処理

HTTP リクエストから渡されるパラメーターは文字列です。必ず明示的に変換してください。

@frappe.whitelist()
def calculate(amount, quantity, items=None):
    amount = float(amount)          # ALWAYS cast numeric params
    quantity = int(quantity)
    if isinstance(items, str):      # ALWAYS parse JSON strings
        items = frappe.parse_json(items)
    return amount * quantity

すべてのリクエストパラメーターには frappe.form_dict 経由でアクセスします:

@frappe.whitelist()
def dynamic_handler():
    all_params = frappe.form_dict
    customer = frappe.form_dict.get("customer")
型アノテーション[v15+]

Frappe v15+ は、リクエスト時に Pydantic を介して型アノテーションを自動検証します:

@frappe.whitelist()
def get_orders(customer: str, limit: int = 10, active: bool = True) -> dict:
    # Frappe auto-validates: limit MUST be convertible to int
    return {"orders": frappe.get_all("Sales Order", limit=limit)}
force_types と require_type_annotated_api_methods[v15+]
  • @frappe.whitelist(force_types=True) — すべてのパラメーターに型アノテーションが必須です
  • hooks.py によるアプリレベルの強制:require_type_annotated_api_methods = 1
  • アノテーションがない場合は FrappeTypeError が発生します

詳細:parameter-handling.md


クライアントからの呼び出し

frappe.call():スタンドアロン API
// Promise-based (ALWAYS prefer this)
frappe.call({
    method: 'myapp.api.get_summary',
    args: { customer: 'CUST-001' },
    freeze: true,
    freeze_message: __('Loading...')
}).then(r => {
    console.log(r.message);  // return value is in r.message
}).catch(err => {
    frappe.show_alert({ message: __('Error'), indicator: 'red' });
});
frm.call():コントローラーメソッド
frm.call('calculate_taxes', { include_shipping: true })
    .then(r => frm.set_value('tax_amount', r.message.tax_amount));
REST API(外部クライアント)
# Token auth (ALWAYS use for external integrations)
curl -H "Authorization: token api_key:api_secret" \
     -H "Content-Type: application/json" \
     -X POST https://site.com/api/method/myapp.api.create_order \
     -d '{"customer": "CUST-001"}'

パターンの詳細:client-calls.md


エラー処理

@frappe.whitelist()
def process_order(order_id):
    if not order_id:
        frappe.throw(_("Order ID required"), frappe.ValidationError)

    if not frappe.has_permission("Sales Order", "write", order_id):
        frappe.throw(_("Not permitted"), frappe.PermissionError)

    try:
        result = heavy_operation(order_id)
        return {"success": True, "data": result}
    except Exception:
        frappe.log_error(frappe.get_traceback(), "process_order")
        frappe.throw(_("Operation failed. Contact support."))
例外HTTP コード使用する状況
frappe.ValidationError417入力検証の失敗
frappe.PermissionError403アクセス拒否
frappe.DoesNotExistError404ドキュメントが見つからない
frappe.DuplicateEntryError409レコードの重複
frappe.AuthenticationError401未ログイン

パターンの詳細:error-handling.md


レスポンスパターン

# Return value auto-wraps as {"message": <return_value>}
@frappe.whitelist()
def get_data():
    return {"key": "value"}   # Client receives: {"message": {"key": "value"}}

# Custom HTTP status
@frappe.whitelist()
def create_item(data):
    doc = frappe.get_doc(data).insert()
    frappe.local.response["http_status_code"] = 201
    return {"name": doc.name}

# File download
@frappe.whitelist()
def download_report(name):
    content = generate_pdf(name)
    frappe.response.filename = f"{name}.pdf"
    frappe.response.filecontent = content
    frappe.response.type = "download"

パターンの詳細:response-patterns.md


レート制限[v14+]

from frappe.rate_limiter import rate_limit

@frappe.whitelist(allow_guest=True)
@rate_limit(limit=5, seconds=60)  # 5 requests per 60 seconds per IP
def public_endpoint():
    return {"status": "ok"}

rate_limit のシグネチャ:

rate_limit(key=None, limit=5, seconds=86400, methods="ALL", ip_based=True)

不正利用を防ぐため、allow_guest=True のエンドポイントには必ず @rate_limit を適用してください。


バージョンによる違い

機能v14v15+v16+
@frappe.whitelist()はいはいはい
allow_guest、xss_safe、methodsはいはいはい
型アノテーションの検証いいえはい(Pydantic による自動検証)はい
force_types パラメーターいいえはいはい
require_type_annotated_api_methods フックいいえはいはい
@rate_limit() デコレーターはいはいはい
アノテーションがない場合の FrappeTypeErrorいいえはいはい

重要なルール

  1. 権限チェックを絶対に省略しないでください — @frappe.whitelist() が確認するのはログインのみで、認可は確認しません
  2. ユーザー入力を生の SQL に絶対に使用しないでください — 必ずパラメーター化クエリまたは ORM を使用してください
  3. スタックトレースを絶対に漏えいさせないでください — frappe.log_error() でログに記録し、一般的なメッセージを表示してください
  4. 入力の型を必ず検証してください — HTTP から渡されるパラメーターは文字列です
  5. ゲスト向けエンドポイントには必ず @rate_limit を適用してください — 不正利用を防止します
  6. 事前のロールチェックなしに ignore_permissions=True を絶対に使用しないでください
  7. 複雑な JS 引数には必ず JSON.stringify() を使用してください — 配列とオブジェクト

アンチパターンの詳細:anti-patterns.md


セキュリティチェックリスト

許可リストに登録されたすべてのメソッドについて、以下を確認してください。

  • 権限チェックがある(frappe.has_permission() または frappe.only_for())
  • 入力が検証されている(型、範囲、形式)
  • SQL クエリがパラメーター化されている(文字列補間は絶対に使用しない)
  • エラーメッセージに内部情報が含まれていない
  • allow_guest=True を使用するのは、明確な理由とレート制限がある場合のみ
  • ignore_permissions=True を使用するのは、事前にロールチェックを行っている場合のみ
  • 可能な場合、HTTP メソッドが制限されている
  • レスポンスに必要なフィールドのみが含まれている(機密データが漏洩しない)

参考ファイル

ファイル内容
decorator-options.mdすべての @frappe.whitelist() パラメーターと force_types
parameter-handling.mdリクエストパラメーター、型変換、frappe.form_dict
response-patterns.md戻り値の型、ファイルのダウンロード、ストリーミング、HTTP ステータス
client-calls.mdfrappe.call()、frm.call()、REST API、fetch パターン
permission-patterns.md権限チェック、ロールガード、カスタムロジック
error-handling.md例外の型、frappe.throw()、ログ記録
examples.md完全に動作する API の例
anti-patterns.mdセキュリティ上の誤りとパフォーマンス上の落とし穴
hooks.mdhooks.py で許可リスト登録済みメソッドを宣言する方法
syntax.mdデコレーターの基本構文と登録の仕組み

コメント