frappe-impl-integrations について
Frappe 開発者向けの連携手順ガイドです。OAuth、Webhook、外部 API 呼び出し、データのインポートとエクスポートを扱い、用途に応じた連携方式の選択も支援します。
主な機能
- 01OAuth プロバイダーとクライアントの設定
- 02署名検証付き Webhook の作成
- 03外部 API 呼び出しと非同期処理
- 04UI またはコードによるデータ取り込み
- 05UI、CLI、コードによるデータ出力
活用シーン
- 01OAuth で外部アプリから Frappe にアクセス
- 02ドキュメントイベントを外部サービスへ通知
- 03Frappe から外部 API を呼び出す
- 04DocType データの一括インポートまたはエクスポート
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ impertio-studio/ frappe_claude_skill_package --skill frappe-impl-integrationsインストール後、Claude Code で/frappe-impl-integrationsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/.gitkeepanti-patterns.mddecision-tree.mdexamples.mdworkflows.md詳細
Frappe 連携
OAuth、Webhook、決済ゲートウェイ、データのインポート/エクスポート、外部 API 呼び出しの手順を紹介します。
バージョン:v14/v15/v16
判断ツリー:どの連携パターンを選ぶか
WHAT ARE YOU INTEGRATING?
│
├─► External service needs to call YOUR Frappe site?
│ ├─► On document events → Webhook (push to external)
│ ├─► External sends data to you → Whitelisted API endpoint
│ └─► External needs user auth → OAuth 2.0 Provider
│
├─► YOUR Frappe site calls an external service?
│ ├─► Needs user-level OAuth consent → Connected App
│ ├─► Server-to-server with API key → make_request / requests
│ └─► Recurring sync → Scheduler + API calls
│
├─► Bulk data in/out?
│ ├─► Import CSV/XLSX → Data Import DocType
│ ├─► Export data → Report Builder / export-csv / API
│ └─► Programmatic bulk → frappe.get_doc().insert()
│
├─► Payment processing?
│ └─► Payment Request + Payment Gateway controller
│
└─► Real-time vs batch?
├─► Real-time → Webhook or API endpoint
├─► Near real-time → frappe.enqueue() after event
└─► Batch → Scheduler task (hourly/daily)
ワークフロー 1:OAuth 2.0:プロバイダーとしての Frappe
外部アプリケーションで「Frappe でサインイン」を利用する場合や、ユーザーに代わって API にアクセスする場合に使用します。
ステップ 1:OAuth プロバイダーの設定を構成する
Setup > Integrations > OAuth Provider Settings に移動します。
- Force:常にユーザーに確認を求めます
- Auto:有効なトークンが存在しない場合にのみ確認を求めます
ステップ 2:OAuth クライアントを作成する
Setup > Integrations > OAuth Client に移動します。
| フィールド | 値 |
|---|---|
| App Name | 外部アプリの識別子 |
| Scopes | スペース区切り(例:openid all) |
| Redirect URIs | スペース区切りのコールバック URL |
| Default Redirect URI | メインのコールバック URL |
| Grant Type | Authorization Code(推奨)または Implicit |
| Response Type | Code(Auth Code の場合)または Token(Implicit の場合) |
| Skip Authorization | 信頼できるファーストパーティアプリの場合のみチェックします |
ステップ 3:生成されたエンドポイントを使用する
| エンドポイント | URL |
|---|---|
| 認可 | /api/method/frappe.integrations.oauth2.authorize |
| トークン | /api/method/frappe.integrations.oauth2.get_token |
| プロフィール | /api/method/frappe.integrations.oauth2.openid_profile |
ステップ 4:外部アプリを構成する
# Example: Grafana generic_oauth config
client_id = <generated_client_id>
client_secret = <generated_client_secret>
auth_url = https://your-frappe.com/api/method/frappe.integrations.oauth2.authorize
token_url = https://your-frappe.com/api/method/frappe.integrations.oauth2.get_token
api_url = https://your-frappe.com/api/method/frappe.integrations.oauth2.openid_profile
scopes = openid all
重要なルール
- サーバーサイドアプリでは、
Implicitグラントタイプは絶対に使用せず、Authorization Codeを使用してください - 本番環境では、すべての OAuth エンドポイントで必ず HTTPS を使用してください
- クライアントサイドの JavaScript で
client_secretを絶対に公開しないでください
ワークフロー 2:Connected App:OAuth クライアントとしての Frappe
Frappe インスタンスから、ユーザーに代わって外部サービス(Google、Microsoft など)にアクセスする場合に使用します。
ステップ 1:Connected App DocType を作成する
| フィールド | 目的 |
|---|---|
| Name | 接続の識別子 |
| OpenID Configuration URL | エンドポイントを自動取得します(例:/.well-known/openid-configuration) |
| Authorization URI | 同意画面の URL(OpenID から自動入力) |
| Token URI | トークン交換 URL(OpenID から自動入力) |
| Redirect URI | 自動生成されます。これを外部プロバイダーにコピーしてください |
| Client ID | 外部プロバイダーから取得 |
| Client Secret | 外部プロバイダーから取得 |
| Scopes | 必要な権限(例:https://mail.google.com/) |
ステップ 2:プロバイダーにリダイレクト URI を登録する
自動生成された Redirect URI をコピーし、外部プロバイダーの OAuth コンソールに登録します。
ステップ 3:追加パラメーターを追加する(必要な場合)
access_type=offline # Google: enables refresh tokens
prompt=consent # Google: forces re-consent for refresh token
ステップ 4:コードで使用する
import frappe
connected_app = frappe.get_doc("Connected App", "My Google App")
# Initiates OAuth flow — user clicks "Connect to..." button
# After consent, tokens are stored automatically
# Making authenticated calls:
session = connected_app.get_oauth2_session()
response = session.get("https://www.googleapis.com/gmail/v1/users/me/messages")
重要なルール
- Google API でリフレッシュトークンを取得するには、
access_type=offlineを必ず追加してください - トークンを手動で保存することは絶対に避けてください。トークンのライフサイクルは Connected App が管理します
TokenExpiredErrorを必ず処理してください。session.refresh_token()を呼び出すか、再接続してください
ワークフロー 3:Webhook:外部サービスへのプッシュ通知
ステップ 1:Webhook DocType を作成する
Integrations > Webhook に移動します。
| フィールド | 値 |
|---|---|
| DocType | 対象のドキュメントタイプ |
| Doc Event | on_update、after_insert、on_submit、on_cancel、on_trash |
| Request URL | 外部エンドポイント |
| Request Method | POST(デフォルト) |
| Conditions | 任意の Jinja フィルター(例:doc.status == "Approved") |
| Enabled | 有効にするにはチェックします |
ステップ 2:ヘッダーを構成する
認証用のカスタムヘッダーを追加します。
Authorization: Bearer <api_token>
Content-Type: application/json
ステップ 3:データ形式を選択する
フォーム URL エンコード:テーブルから特定のフィールドを選択します。
JSON:構造化ペイロードには Jinja テンプレートを使用します。
{
"id": "{{ doc.name }}",
"total": "{{ doc.grand_total }}",
"items": {{ doc.items | tojson }},
"event": "{{ event }}"
}
ステップ 4:Webhook Secret を有効にする(HMAC 検証)
Webhook Secret を設定します。Frappe は、ペイロードの HMAC-SHA256 ハッシュを Base64 エンコードした値を X-Frappe-Webhook-Signature ヘッダーに追加します。
受信側での検証(Python の例):
import hmac, hashlib, base64
def verify_webhook(payload_body, secret, signature_header):
expected = base64.b64encode(
hmac.new(secret.encode(), payload_body, hashlib.sha256).digest()
).decode()
return hmac.compare_digest(expected, signature_header)
重要なルール
- 本番環境のWebhookでは、必ずWebhook Secretを有効にしてください
- 配信が保証される手段としてWebhookに絶対に依存しないでください。受信側で冪等性を実装してください
- JSONペイロード内の子テーブルデータには、必ず
| tojsonフィルターを使用してください - 配信ごとにWebhookログが作成されます。デバッグにはWebhook Request Logを確認してください
ワークフロー 4:Frappe からの外部 API 呼び出し
frappe.integrations.utils の使用
from frappe.integrations.utils import make_get_request, make_post_request
# GET request
response = make_get_request(
"https://api.example.com/data",
headers={"Authorization": "Bearer token123"}
)
# POST request
response = make_post_request(
"https://api.example.com/submit",
data={"key": "value"},
headers={"Content-Type": "application/json"}
)
requests ライブラリを直接使用
import requests
import frappe
def sync_to_external():
try:
response = requests.post(
"https://api.example.com/endpoint",
json={"data": "value"},
timeout=30
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
frappe.log_error(f"API call failed: {e}", "Integration Error")
raise
重要なルール
- 外部リクエストには必ず
timeoutを設定してください(30秒を推奨) - 外部呼び出しは必ずtry/exceptで囲み、
frappe.log_error()でエラーを記録してください validateやbefore_save内で外部APIを絶対に呼び出さないでください。on_updateとfrappe.enqueue()を使用してください- Webリクエストをブロックしないよう、時間のかかる外部呼び出しには必ず
frappe.enqueue()を使用してください
ワークフロー 5:データのインポート
UI 経由(Data Import DocType)
- Home > Data Import > Newに移動します
- DocTypeとImport Type(
InsertまたはUpdate)を選択します - テンプレートCSV/XLSXをダウンロードします
- テンプレートの形式に従ってデータを入力します
- アップロードしてプレビューします
- インポートを開始します
CSV 形式のルール
ID,Item Name,Item Group,Stock UOM
,Widget A,Products,Nos
,Widget B,Raw Material,Kg
- 1行目:フィールドラベルまたはAPIフィールド名
- Insertの場合、
ID/nameは空欄にします(自動生成) - Updateの場合、
ID列には既存のドキュメント名を必ず含めてください - 子テーブル:親行のデータを繰り返し、子フィールドを追加列として加えます
プログラムによるインポート
import frappe
from frappe.core.doctype.data_import.data_import import DataImport
# Create Data Import document
di = frappe.get_doc({
"doctype": "Data Import",
"reference_doctype": "Item",
"import_type": "Insert New Records",
"import_file": "/path/to/file.csv"
})
di.insert()
di.start_import()
重要なルール
- テンプレートを必ずダウンロードして使用してください。列の順序と名前は完全に一致している必要があります
- 一度に5,000行を超えてインポートしないでください。バッチに分割してください
- 一括インポートの前に、必ず5~10行でテストしてください
- インポート完了後、必ずImport Logで行ごとのエラーを確認してください
ワークフロー 6:データのエクスポート
Report Builder 経由
- 任意のDocTypeリストビューを開きます
- フィルターを適用します
- メニュー > Export(CSV/Excel)を選択します
CLI 経由
bench --site mysite export-csv "Sales Invoice"
bench --site mysite export-doc "Sales Invoice" "INV-001"
bench --site mysite export-json "Sales Invoice" "INV-001"
bench --site mysite export-fixtures --app myapp
プログラムによるエクスポート
import frappe
# Export filtered data
data = frappe.get_all("Sales Invoice",
filters={"status": "Paid", "posting_date": [">", "2024-01-01"]},
fields=["name", "customer", "grand_total", "posting_date"],
order_by="posting_date desc",
limit_page_length=0 # No limit
)
# Convert to CSV
import csv, io
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=["name", "customer", "grand_total", "posting_date"])
writer.writeheader()
writer.writerows(data)
csv_content = output.getvalue()
ワークフロー 7:Frappe REST API の認証
API Key + Secret(サーバー間)
# Generate via User > API Access > Generate Keys
curl -H "Authorization: token api_key:api_secret" \
https://your-site.com/api/resource/Sales%20Invoice
OAuth Bearer Token
curl -H "Authorization: Bearer access_token" \
https://your-site.com/api/resource/Sales%20Invoice
セッションベース(ログイン)
# Login first
curl -X POST https://your-site.com/api/method/login \
-d "[email protected]&pwd=password"
# Subsequent requests use session cookie
統合パターン:同期と非同期
| パターン | 使用する場面 | 実装 |
|---|---|---|
| 同期 | レスポンスがすぐに必要な場合 | コントローラー内で直接APIを呼び出す |
| 非同期(enqueue) | 外部呼び出しに5秒以上かかる場合 | frappe.enqueue("myapp.api.sync_record", doc_name=doc.name) |
| Webhook | イベント発生時にプッシュする場合 | Webhook DocTypeの設定 |
| スケジュール同期 | 定期的なバッチ処理 | hooks.pyのscheduler_events |
| リアルタイム | ライブ更新 | Socket.IO + frappe.publish_realtime() |
リトライパターン
import frappe
from frappe.utils.background_jobs import get_jobs
def sync_with_retry(doc_name, retry_count=0, max_retries=3):
try:
result = call_external_api(doc_name)
frappe.db.set_value("Sales Invoice", doc_name, "sync_status", "Success")
frappe.db.commit()
except Exception as e:
if retry_count < max_retries:
frappe.enqueue(
"myapp.integrations.sync_with_retry",
doc_name=doc_name,
retry_count=retry_count + 1,
queue="short",
enqueue_after_commit=True
)
else:
frappe.log_error(f"Sync failed after {max_retries} retries: {e}")
frappe.db.set_value("Sales Invoice", doc_name, "sync_status", "Failed")
frappe.db.commit()
バージョンごとの差異
| 機能 | V14 | V15 | V16 |
|---|---|---|---|
| Webhook DocType | あり | あり | あり |
| Connected App | あり | あり | あり |
| OAuth 2.0 Provider | あり | あり | あり |
| Data Import(新しいUI) | あり | あり | あり |
| Print Designer | なし | あり | あり |
make_get_request | あり | あり | あり |
| Webhook HMAC | あり | あり | あり |
参考ファイル
| ファイル | 内容 |
|---|---|
| workflows.md | 統合ワークフローの完全なパターン集 |
| examples.md | すべての統合タイプに対応した動作するコード例 |
| anti-patterns.md | よくある統合の間違いと修正方法 |
| decision-tree.md | 統合方法の選択に関する詳細な判断ツリー |