frappe-syntax-whitelisted について
Frappe の Python メソッドを HTTP API エンドポイントとして公開するためのリファレンススキルです。設定、入力検証、権限確認、クライアントからの呼び出し方を扱います。
主な機能
- 01ホワイトリストデコレーターの設定
- 02権限確認と入力検証
- 03エラーとレスポンス形式の処理
- 04JavaScript や REST からの API 呼び出し
- 05ゲスト用エンドポイントのレート制限
活用シーン
- 01Python 関数を API エンドポイントとして公開
- 02ドキュメントにアクセスするメソッドの保護
- 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.mdclient-calls.mddecorator-options.mderror-handling.mdexamples.mdhooks.mdmethods.mdparameter-handling.mdpatterns.mdpermission-patterns.mdresponse-patterns.mdsyntax.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_guest | False | True = ゲストロールで呼び出し可能。必ず追加の入力検証を行ってください |
xss_safe | False | True = HTML がエスケープされません。サニタイズ済みの出力なしに絶対に使用しないでください |
methods | None(すべて) | 許可する HTTP メソッドを制限します |
force_types | None | True = すべてのパラメーターに型アノテーションが必須です[v15+] |
判断フロー
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が発生します
クライアントからの呼び出し
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.ValidationError | 417 | 入力検証の失敗 |
frappe.PermissionError | 403 | アクセス拒否 |
frappe.DoesNotExistError | 404 | ドキュメントが見つからない |
frappe.DuplicateEntryError | 409 | レコードの重複 |
frappe.AuthenticationError | 401 | 未ログイン |
パターンの詳細: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 を適用してください。
バージョンによる違い
| 機能 | v14 | v15+ | v16+ |
|---|---|---|---|
@frappe.whitelist() | はい | はい | はい |
allow_guest、xss_safe、methods | はい | はい | はい |
| 型アノテーションの検証 | いいえ | はい(Pydantic による自動検証) | はい |
force_types パラメーター | いいえ | はい | はい |
require_type_annotated_api_methods フック | いいえ | はい | はい |
@rate_limit() デコレーター | はい | はい | はい |
アノテーションがない場合の FrappeTypeError | いいえ | はい | はい |
重要なルール
- 権限チェックを絶対に省略しないでください —
@frappe.whitelist()が確認するのはログインのみで、認可は確認しません - ユーザー入力を生の SQL に絶対に使用しないでください — 必ずパラメーター化クエリまたは ORM を使用してください
- スタックトレースを絶対に漏えいさせないでください —
frappe.log_error()でログに記録し、一般的なメッセージを表示してください - 入力の型を必ず検証してください — HTTP から渡されるパラメーターは文字列です
- ゲスト向けエンドポイントには必ず
@rate_limitを適用してください — 不正利用を防止します - 事前のロールチェックなしに
ignore_permissions=Trueを絶対に使用しないでください - 複雑な 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.md | frappe.call()、frm.call()、REST API、fetch パターン |
| permission-patterns.md | 権限チェック、ロールガード、カスタムロジック |
| error-handling.md | 例外の型、frappe.throw()、ログ記録 |
| examples.md | 完全に動作する API の例 |
| anti-patterns.md | セキュリティ上の誤りとパフォーマンス上の落とし穴 |
| hooks.md | hooks.py で許可リスト登録済みメソッドを宣言する方法 |
| syntax.md | デコレーターの基本構文と登録の仕組み |