frappe-core-api について
Frappe 開発者向けに、REST、RPC、Webhook の連携パターンをまとめたガイドです。API 認証、ドキュメント CRUD、カスタムメソッド、イベント通知に加え、ページ分割や権限確認、署名検証などの安全対策も扱います。
主な機能
- 01REST ドキュメント CRUD とバージョン別エンドポイント
- 02Token、OAuth 2.0、セッション認証
- 03RPC によるカスタムメソッド呼び出し
- 04Webhook イベントと署名検証
- 05ページ分割、権限、入力検証の指針
活用シーン
- 01外部クライアントから Frappe ドキュメントを読み書き
- 02連携用 API 認証の設定
- 03RPC によるカスタムサーバーロジックの呼び出し
- 04ドキュメント変更 Webhook の受信と検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ impertio-studio/ frappe_claude_skill_package --skill frappe-core-apiインストール後、Claude Code で/frappe-core-apiを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/anti-patterns.mdauthentication-methods.mdexamples.mdrest-api-reference.mdrpc-api-reference.mdwebhooks-reference.md詳細
Frappe API パターン
Frappe との REST、RPC、Webhook 連携における決定論的なパターンです。
判断フローチャート
What do you need?
├── CRUD on documents (external client)
│ ├── v14: REST /api/resource/{doctype}
│ └── v15+: REST /api/v2/document/{doctype} (new) or /api/resource/ (still works)
│
├── Call custom server logic (external client)
│ └── RPC: POST /api/method/{dotted.path.to.function}
│
├── Notify external systems on document events
│ └── Webhooks (configured in UI or via DocType)
│
├── Client-side calls (JavaScript in Frappe desk)
│ ├── frappe.xcall() — async/await (RECOMMENDED)
│ └── frappe.call() — callback/promise pattern
│
└── Authentication method?
├── Server-to-server integration → Token Auth (RECOMMENDED)
├── Third-party app / mobile → OAuth 2.0
├── Browser session (short-lived) → Session/Cookie Auth
└── Quick scripting / testing → Token Auth
認証方法
トークン認証(連携には推奨)
headers = {
'Authorization': 'token api_key:api_secret',
'Accept': 'application/json',
'Content-Type': 'application/json'
}
キーを生成するには、「User > Settings > API Access > Generate Keys」の順に選択します。API シークレットは表示されるのが一度だけなので、必ずすぐに保存してください。
Basic 認証(代替のトークン形式)
import base64
credentials = base64.b64encode(b'api_key:api_secret').decode()
headers = {'Authorization': f'Basic {credentials}'}
OAuth 2.0(サードパーティアプリ)
# Step 1: Authorization redirect
GET /api/method/frappe.integrations.oauth2.authorize
?client_id={id}&response_type=code&scope=openid all
&redirect_uri={uri}&state={random}
# Step 2: Exchange code for token
POST /api/method/frappe.integrations.oauth2.get_token
grant_type=authorization_code&code={code}
&redirect_uri={uri}&client_id={id}
# Step 3: Use bearer token
Authorization: Bearer {access_token}
# Refresh token
POST /api/method/frappe.integrations.oauth2.get_token
grant_type=refresh_token&refresh_token={token}&client_id={id}
セッション/Cookie 認証
session = requests.Session()
session.post(url + '/api/method/login', json={'usr': 'email', 'pwd': 'pass'})
# Subsequent requests use session cookie automatically
セッション Cookie の有効期限は約 3 日です。長時間実行する連携には絶対に使用しないでください。
REST API:リソースの CRUD
エンドポイント
| 操作 | メソッド | v14 エンドポイント | v15 以降の v2 エンドポイント |
|---|---|---|---|
| 一覧 | GET | /api/resource/{doctype} | /api/v2/document/{doctype} |
| 作成 | POST | /api/resource/{doctype} | /api/v2/document/{doctype} |
| 読み取り | GET | /api/resource/{doctype}/{name} | /api/v2/document/{doctype}/{name} |
| 更新 | PUT | /api/resource/{doctype}/{name} | PATCH /api/v2/document/{doctype}/{name} |
| 削除 | DELETE | /api/resource/{doctype}/{name} | DELETE /api/v2/document/{doctype}/{name} |
| コピー | — | — | GET /api/v2/document/{doctype}/{name}/copy [v15+] |
| Doc メソッド | — | — | POST /api/v2/document/{doctype}/{name}/method/{method} [v15+] |
必ず Accept: application/json ヘッダーを含めてください。含めない場合、Frappe が HTML を返すことがあります。
一覧パラメーター
| パラメーター | 型 | 説明 | デフォルト |
|---|---|---|---|
fields | JSON 配列 | 返すフィールド | ["name"] |
filters | JSON 配列 | AND 条件 | なし |
or_filters | JSON 配列 | OR 条件 | なし |
order_by | 文字列 | 並べ替え式 | modified desc |
limit_start | int | ページネーションの開始位置 | 0 |
limit_page_length | int | ページサイズ | 20 |
limit | int | limit_page_length の別名 [v15+] | — |
debug | bool | レスポンスに SQL を表示 | false |
フィルター演算子
filters = [["status", "=", "Open"]]
filters = [["amount", ">", 1000]]
filters = [["status", "in", ["Open", "Pending"]]]
filters = [["date", "between", ["2024-01-01", "2024-12-31"]]]
filters = [["reference", "is", "set"]] # NOT NULL
filters = [["reference", "is", "not set"]] # IS NULL
filters = [["name", "like", "%INV%"]]
filters = [["status", "not in", ["Cancelled"]]]
演算子の全一覧:=, !=, >, <, >=, <=, like, not like, in, not in, is, between。
ページネーションのパターン
import json, requests
def get_all_records(doctype, headers, base_url, page_size=100):
all_data, offset = [], 0
while True:
params = {
'fields': json.dumps(["name", "modified"]),
'limit_start': offset,
'limit_page_length': page_size
}
resp = requests.get(f'{base_url}/api/resource/{doctype}',
params=params, headers=headers)
data = resp.json().get('data', [])
if not data:
break
all_data.extend(data)
if len(data) < page_size:
break
offset += page_size
return all_data
子テーブルを含めて作成
requests.post(f'{base_url}/api/resource/Sales Order', json={
"customer": "CUST-001",
"items": [
{"item_code": "ITEM-001", "qty": 5, "rate": 100},
{"item_code": "ITEM-002", "qty": 2, "rate": 250}
]
}, headers=headers)
更新(部分更新)
# Only specified fields are changed
requests.put(f'{base_url}/api/resource/Customer/CUST-001',
json={"customer_group": "Premium"}, headers=headers)
ファイルのアップロード
requests.post(f'{base_url}/api/method/upload_file',
files={'file': ('doc.pdf', open('doc.pdf', 'rb'), 'application/pdf')},
data={'doctype': 'Customer', 'docname': 'CUST-001', 'is_private': 1},
headers={'Authorization': 'token api_key:api_secret'})
# NOTE: Do NOT set Content-Type header — requests sets multipart boundary automatically
RPC API:カスタムメソッド
サーバーサイドエンドポイント
@frappe.whitelist()
def get_balance(customer):
"""GET /api/method/myapp.api.get_balance?customer=CUST-001"""
return frappe.db.get_value("Customer", customer, "outstanding_amount")
@frappe.whitelist(methods=["POST"])
def create_payment(customer, amount):
"""POST /api/method/myapp.api.create_payment"""
if not frappe.has_permission("Payment Entry", "create"):
frappe.throw(_("Not permitted"), frappe.PermissionError)
pe = frappe.new_doc("Payment Entry")
pe.party_type = "Customer"
pe.party = customer
pe.paid_amount = float(amount)
pe.insert()
return pe.name
@frappe.whitelist(allow_guest=True)
def public_status():
"""No authentication required."""
return {"status": "ok"}
デコレーターのオプション
| オプション | 効果 | バージョン |
|---|---|---|
allow_guest=True | 認証不要 | すべて |
methods=["POST"] | HTTP メソッドを制限 | [v14+] |
xss_safe=True | レスポンスの XSS エスケープを省略 | すべて |
レスポンス構造
// RPC success
{"message": "return_value"}
// REST success
{"data": {...}}
// Error
{"exc_type": "ValidationError", "_server_messages": "[{\"message\": \"...\"}]"}
クライアント側からの呼び出し(JavaScript)
// RECOMMENDED: async/await with frappe.xcall
const result = await frappe.xcall('myapp.api.get_balance', {
customer: 'CUST-001'
});
// Alternative: frappe.call with promise
frappe.call({
method: 'myapp.api.get_balance',
args: {customer: 'CUST-001'},
freeze: true,
freeze_message: __('Loading...')
}).then(r => console.log(r.message));
// Document method (frm.call)
frm.call('get_linked_doc', {throw_if_missing: true})
.then(r => console.log(r.message));
標準の frappe.client メソッド
| メソッド | エンドポイント | 用途 |
|---|---|---|
frappe.client.get_value | POST | 単一フィールドの値を取得 |
frappe.client.get_list | POST | フィルターを指定して一覧を取得 |
frappe.client.get | POST | ドキュメント全体を取得 |
frappe.client.insert | POST | ドキュメントを作成 |
frappe.client.save | POST | ドキュメントを更新 |
frappe.client.delete | POST | ドキュメントを削除 |
frappe.client.submit | POST | ドキュメントを送信 |
frappe.client.cancel | POST | ドキュメントを取り消し |
frappe.client.get_count | POST | ドキュメント数を取得 |
Webhook
UI の Webhook DocType で設定します。イベント:
| イベント | 発生条件 |
|---|---|
after_insert | 新しいドキュメントの作成後 |
on_update | 保存のたび |
on_submit | 送信後(docstatus=1) |
on_cancel | 取り消し後(docstatus=2) |
on_trash | 削除前 |
on_update_after_submit | 修正後 |
on_change | 変更のたび |
セキュリティ:必ず Webhook Secret を設定してください。Frappe は、ペイロードの HMAC-SHA256 を Base64 エンコードした値を含む X-Frappe-Webhook-Signature ヘッダーを追加します。受信側で検証してください。
条件:Jinja2 を使用します — {{ doc.grand_total > 10000 }}。
ハンドラーの完全な例については、references/webhooks-reference.md を参照してください。
HTTP ステータスコード
| コード | 意味 | 一般的な原因 |
|---|---|---|
200 | 成功 | — |
400 | 不正なリクエスト | 検証エラー |
401 | 認証されていません | 認証情報がない、または無効 |
403 | 禁止されています | 操作の権限がありません |
404 | 見つかりません | ドキュメントが存在しません |
417 | 期待に応えられませんでした | サーバー例外(frappe.throw) |
429 | レート制限されています | リクエストが多すぎます |
500 | サーバーエラー | 未処理の例外 |
重要なルール
- API リクエストには必ず
Accept: application/jsonヘッダーを含めてください @frappe.whitelist()メソッドには必ず権限チェックを追加してください- ホワイトリスト登録されたメソッドでは、入力を必ず検証してサニタイズしてください
- 必ずパラメーター化クエリーを使用してください。SQL に文字列を埋め込むことは絶対に避けてください
- 外部への
requests呼び出しでは必ずtimeout=30を使用してください - 認証情報は必ず
frappe.confまたは環境変数に保存してください。ハードコードは絶対に避けてください - Webhook の署名は HMAC-SHA256 で必ず検証してください
- リストのレスポンスは必ずページネーションしてください。件数に上限のない結果セットを返すことは絶対に避けてください
- 状態を変更するエンドポイントで
allow_guest=Trueを使用することは絶対に避けてください - 認証情報や機密データをログに記録することは絶対に避けてください
- 連携には Administrator の API キーを使用することは絶対に避けてください。専用の API ユーザーを作成してください
アンチパターン
| してはいけないこと | 代わりにすること |
|---|---|
| ホワイトリストで権限チェックをしない | アクションの前に frappe.has_permission() を呼び出す |
frappe.db.sql(f"...{user_input}") | パラメーター化された %s クエリー |
allow_guest=True と状態変更の組み合わせ | 認証を必須にする |
| 上限なしですべてのレコードを返す | limit_page_length でページネーションする |
| API 認証情報をハードコードする | frappe.conf.get("api_key") |
| 負荷の高い処理を同期的に行う | 長時間のタスクには frappe.enqueue() を使用する |
| 外部呼び出しにタイムアウトを設定しない | requests.get(url, timeout=30) |
| レスポンス形式に一貫性がない | 必ず {"status": "...", "data": ...} を返す |
バージョンの違い
| 機能 | v14 | v15 | v16 |
|---|---|---|---|
/api/resource/(v1) | はい | はい | はい |
/api/v2/document/(v2) | いいえ | はい | はい |
/api/v2/doctype/{dt}/meta | いいえ | はい | はい |
/api/v2/doctype/{dt}/count | いいえ | はい | はい |
limit エイリアスパラメーター | いいえ | はい | はい |
| OAuth2 の PKCE | 一部対応 | はい | はい |
| Server Script のレート制限 | いいえ | はい | はい |
| v2 URL 経由の Doc メソッド | いいえ | はい | はい |
参照ファイル
| ファイル | 内容 |
|---|---|
| authentication-methods.md | トークン、セッション、OAuth2 のコード例 |
| rest-api-reference.md | フィルターとページネーションを含む REST CRUD の全般 |
| rpc-api-reference.md | ホワイトリスト登録されたメソッド、frappe.call、frappe.xcall |
| webhooks-reference.md | Webhook の設定、セキュリティ、ハンドラーの例 |
| anti-patterns.md | よくあるミスと修正方法 |
| examples.md | Python/JS/cURL のクライアント実装 |
関連スキル
frappe-core-permissions— API エンドポイントの権限システムfrappe-core-database— API メソッドの背後にあるデータベースクエリーfrappe-syntax-hooks— Webhook のフック設定frappe-syntax-controllers— API 経由で呼び出すコントローラーメソッド
Frappe のドキュメントに基づき検証済み 2026-03-20 | Frappe v14/v15/v16