saleor-development について
Django と GraphQL を基盤とするヘッドレス EC プラットフォーム、Saleor の開発者向けガイドです。API クエリ、アプリ開発、Webhook 処理、管理ダッシュボードへの独自 UI 追加を扱います。
主な機能
- 01GraphQL による商品・チェックアウトデータの取得
- 02Webhook を受け取る Saleor App の作成
- 03Webhook 署名の検証
- 04Dashboard への独自 UI 追加
- 05Docker Compose によるローカル環境の起動
活用シーン
- 01Saleor バックエンド向けの独自ストアフロント構築
- 02注文や商品ライフサイクルのイベント処理
- 03Dashboard への商品操作パネル追加
- 04Saleor 連携のローカル開発とテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill saleor-developmentインストール後、Claude Code で/saleor-developmentを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
tile.jsonevals/dashboard-extension-with-app-bridge-comm/criteria.jsontask.mdevals/saleor-app-bootstrap-and-webhook-handlin/criteria.jsontask.mdevals/storefront-product-catalog-with-paginati/criteria.jsontask.md詳細
Saleor の開発
概要
Saleor は、Django と Python を基盤とする、ヘッドレスかつ GraphQL を中心に設計された e コマースプラットフォームです。ストアフロントやサードパーティーアプリ向けに完全に型付けされた GraphQL API、ストア管理用の React ベースのダッシュボード、ウェブフック経由でイベントに応答したり、ダッシュボードに UI を追加したりできる拡張システムを提供します。このスキルでは、Saleor API のクエリ、Saleor アプリ(Saleor の外部でホストされるプラグイン)の構築、アプリ拡張機能を使ったダッシュボードのカスタマイズについて説明します。
このスキルを使用する場面
- Saleor バックエンドに接続するカスタムストアフロント(Next.js、Remix、モバイルアプリ)を構築する場合
- 注文や商品ライフサイクルのウェブフックに応答する Saleor アプリを作成する場合
- アプリ拡張機能を使って Saleor ダッシュボードにカスタム UI パネルを追加する場合
- Saleor の商品カタログ、チェックアウト、顧客 API を調べたり拡張したりする場合
- Docker を使ってローカルの Saleor 開発環境をセットアップする場合
前提条件とプラットフォームに関する注意事項
このスキルはカスタム/ヘッドレスストアフロント向け(Node.js、Python、または同様のバックエンド)です。コード例では TypeScript/Node.js を使用していますが、任意のスタックに適合させられます。
Shopify:Shopify Hydrogen は Shopify のヘッドレスフレームワークです。MACH/コンポーザブルのパターンは、カスタムフロントエンドで Shopify をコマースバックエンドとして使用する場合や、Shopify を他の最適なサービスと組み合わせる場合に適用できます。 WooCommerce:WooCommerce は、REST API と WPGraphQL を介してヘッドレスバックエンドとして機能できます。これらのパターンは、フロントエンドを WordPress から切り離す場合に適用できます。 Magento:Magento の GraphQL API と PWA Studio は、ヘッドレスアーキテクチャをサポートします。これらのコンポーザブルパターンは、MACH スタックで Magento をバックエンドサービスとして使用する場合に適用できます。
必要なもの:
- Node.js 18 以降(またはバックエンドの言語に合わせて調整してください)
- PostgreSQL(または任意のリレーショナルデータベース)
- キャッシュ/キュー用の Redis
- Stripe アカウントと API キー
- メール送信サービス(SendGrid、AWS SES、または Postmark)
- コンテナーオーケストレーション用の Docker および/または Kubernetes
- CDN(Cloudflare、CloudFront、または Fastly)
基本手順
-
Docker Compose で Saleor をローカル実行する
git clone https://github.com/saleor/saleor-platform.git cd saleor-platform docker compose up --detach # API: http://localhost:8000/graphql/ # Dashboard: http://localhost:9000最初のスーパーユーザーを作成し、デモデータを登録します。
docker compose run --rm api python manage.py createsuperuser docker compose run --rm api python manage.py populatedb --createsuperuser -
ストアフロントの GraphQL API にクエリを送信する
Saleor CLI または任意の GraphQL クライアント(Apollo、urql、graphql-request)を使用します。
コード生成用に CLI をインストールします。
npm install -g @saleor/cli saleor configure例 — デフォルトのチャネルから最初の 12 件の商品を取得します。
query ProductList($channel: String!) { products(first: 12, channel: $channel) { edges { node { id name slug thumbnail { url alt } pricing { priceRange { start { gross { amount currency } } } } } } pageInfo { hasNextPage endCursor } } }import { createClient } from 'urql'; const client = createClient({ url: process.env.NEXT_PUBLIC_SALEOR_API_URL, fetchOptions: () => ({ headers: { 'Content-Type': 'application/json' }, }), }); const { data } = await client.query(PRODUCT_LIST_QUERY, { channel: 'default-channel' }).toPromise(); -
顧客を認証してチェックアウトを開始する
mutation CustomerLogin($email: String!, $password: String!) { tokenCreate(email: $email, password: $password) { token refreshToken errors { field message } user { id email } } }チェックアウトを作成して明細を追加します。
mutation CheckoutCreate($channel: String!, $lines: [CheckoutLineInput!]!) { checkoutCreate(input: { channel: $channel, lines: $lines }) { checkout { id token totalPrice { gross { amount currency } } } errors { field message } } }決済ゲートウェイのトークン(Stripe Elements などから取得)を使ってチェックアウトを完了します。
mutation CheckoutComplete($checkoutId: ID!, $paymentData: JSONString) { checkoutComplete(id: $checkoutId, paymentData: $paymentData) { order { id number status } errors { field message code } } } -
Saleor アプリをブートストラップする
Saleor アプリは、Saleor に自身を登録し、ウェブフックを受信し、必要に応じて iframe 経由でダッシュボードに UI を表示する Node.js サービスです。
npx @saleor/app-sdk@latest create my-saleor-app cd my-saleor-app npm install npm run dev # Expose with: npx ngrok http 3000ダッシュボードで アプリ → カスタムアプリをインストール に移動し、ngrok URL を入力してアプリを登録します。Saleor は
/api/manifestエンドポイントを呼び出します。// pages/api/manifest.ts import { createManifestHandler } from "@saleor/app-sdk/handlers/next"; import { AppManifest } from "@saleor/app-sdk/types"; const manifest: AppManifest = { id: "my-saleor-app", name: "My Saleor App", version: "1.0.0", about: "Example app", permissions: ["MANAGE_ORDERS"], appUrl: process.env.APP_URL!, tokenTargetUrl: `${process.env.APP_URL}/api/register`, webhooks: [ { name: "Order Created", asyncEvents: ["ORDER_CREATED"], query: `subscription { event { ... on OrderCreated { order { id number } } } }`, targetUrl: `${process.env.APP_URL}/api/webhooks/order-created`, isActive: true, }, ], }; export default createManifestHandler({ manifestFactory: () => manifest }); -
Saleor のウェブフックを安全に処理する
Saleor は、アプリのシークレットトークンを使った HMAC-SHA256 署名で、すべてのウェブフックに署名します。
// pages/api/webhooks/order-created.ts import { SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next"; import { OrderCreatedDocument } from "@/generated/graphql"; const orderCreatedWebhook = new SaleorAsyncWebhook<OrderCreatedPayload>({ name: "Order Created", webhookPath: "api/webhooks/order-created", asyncEvent: "ORDER_CREATED", apl: saleorApp.apl, query: OrderCreatedDocument, }); export default orderCreatedWebhook.createHandler((req, res, ctx) => { const { order } = ctx.payload; console.log(`New order #${order.number} received`); // Trigger fulfillment, email, ERP sync, etc. return res.status(200).end(); }); export const config = { api: { bodyParser: false } }; // required for signature check -
ダッシュボード拡張機能(カスタム UI パネル)を追加する
拡張機能は Saleor ダッシュボード内に iframe を表示します。マニフェストで宣言します。
extensions: [ { label: "Sync to ERP", mount: "PRODUCT_DETAILS_MORE_ACTIONS", target: "POPUP", permissions: ["MANAGE_PRODUCTS"], url: `${process.env.APP_URL}/extension/product-sync`, }, ],拡張機能のページでは、
@saleor/app-sdkを使ってダッシュボードのホストと通信します。import { actions, useAppBridge } from "@saleor/app-sdk/app-bridge"; export default function ProductSyncExtension() { const { appBridge } = useAppBridge(); const handleSync = async () => { appBridge?.dispatch(actions.Notification({ status: "success", title: "Sync started", text: "Product is being synced to ERP.", })); }; return <button onClick={handleSync}>Sync to ERP</button>; }
例
TypeScript と graphql-request を使ったページ分割された商品カタログ
import { GraphQLClient, gql } from 'graphql-request';
const client = new GraphQLClient(process.env.SALEOR_API_URL!, {
headers: { Authorization: `Bearer ${process.env.SALEOR_APP_TOKEN}` },
});
const PRODUCTS_QUERY = gql`
query Products($first: Int!, $after: String, $channel: String!) {
products(first: $first, after: $after, channel: $channel) {
edges { node { id name slug description } }
pageInfo { hasNextPage endCursor }
}
}
`;
async function fetchAllProducts(channel: string) {
const products = [];
let after: string | null = null;
do {
const data = await client.request(PRODUCTS_QUERY, { first: 100, after, channel });
products.push(...data.products.edges.map((e: any) => e.node));
after = data.products.pageInfo.hasNextPage ? data.products.pageInfo.endCursor : null;
} while (after);
return products;
}
Admin API による注文ステータスの更新
mutation FulfillOrder($orderId: ID!, $input: OrderFulfillInput!) {
orderFulfill(orderId: $orderId, input: $input) {
fulfillments {
id
status
trackingNumber
}
errors { field message code }
}
}
await client.request(FULFILL_ORDER_MUTATION, {
orderId: "T3JkZXI6MTIz",
input: {
lines: [{ orderLineId: "T3JkZXJMaW5lOjQ1", stocks: [{ warehouse: "V2FyZWhvdXNlOjE=", quantity: 1 }] }],
notifyCustomer: true,
allowStockToBeExceeded: false,
},
});
ベストプラクティス
- 複数の地域、または B2B/B2C の分離にはチャネルを使用します — すべての商品リスト、価格設定、チェックアウトはチャネルごとに設定されます。商品を複製するのではなく、ロケールや通貨ごとにチャネルを分けて作成します
- スキーマから TypeScript の型を生成します —
saleor app generate-typesを実行するか、graphql-codegenを使用して、クエリを完全に型付けします - アプリのトークンは Saleor の APL(Auth Persistence Layer)に保存します — 開発環境ではデフォルトのファイルベース APL で問題ありません。本番環境では Redis または Upstash APL を使用します
- Webhook の署名は必ず検証します — HMAC 検証を自動的に処理する
SaleorAsyncWebhookラッパーを使用し、認証されていないペイロードは決して処理しないでください - サブスクリプションベースの Webhook クエリを使用します — Saleor の Webhook では GraphQL サブスクリプションをペイロードの定義に使用するため、含めるフィールドを正確に制御できます
- CDN 層で商品カタログのレスポンスをキャッシュします — 商品データが変更されることはほとんどありません。カタログ API ルートに
Cache-Control: s-maxage=300を設定します - 本番環境では Saleor Cloud を使用します — Django、Celery、Redis、PostgreSQL のセルフホストには運用上の成熟度が求められます。Saleor Cloud を利用すれば、この運用を任せられます
よくある落とし穴
| 問題 | 解決策 |
|---|---|
| 許可されていない操作に対する GraphQL エラー | マニフェストと、ダッシュボードの「App settings」の両方で、アプリに適切な権限が付与されていることを確認します |
| Webhook ペイロードが空、またはフィールドが欠落している | Webhook ペイロードはマニフェスト内の GraphQL サブスクリプションクエリで定義します。必要なフィールドを query プロパティに追加します |
ストアフロントで tokenCreate が null を返す | チャネルでストアフロント API が有効になっており、国が割り当てられている必要があります。ダッシュボードでチャネルの設定を確認します |
| ローカルでは動作するが、デプロイ後に動作しない | APP_URL 環境変数には、Saleor がアクセスできる公開 URL を設定する必要があります。デプロイ後にダッシュボードでアプリの URL を更新します |
| ダッシュボード拡張機能の iframe が空白になる | 拡張機能の URL は HTTPS で配信し、ダッシュボードのオリジンに対する Access-Control-Allow-Origin ヘッダーを含める必要があります |
関連スキル
- @shopify-hydrogen
- @composable-commerce
- @webhook-architecture
- @jamstack-storefront
- @commerce-api-gateway