mcpField
S
saleor-development

saleor-development

github.com/finsilabs開発6074 回インストール6 日前に更新
リポジトリ

saleor-development について

Django と GraphQL を基盤とするヘッドレス EC プラットフォーム、Saleor の開発者向けガイドです。API クエリ、アプリ開発、Webhook 処理、管理ダッシュボードへの独自 UI 追加を扱います。

主な機能

  1. 01GraphQL による商品・チェックアウトデータの取得
  2. 02Webhook を受け取る Saleor App の作成
  3. 03Webhook 署名の検証
  4. 04Dashboard への独自 UI 追加
  5. 05Docker Compose によるローカル環境の起動

活用シーン

  1. 01Saleor バックエンド向けの独自ストアフロント構築
  2. 02注文や商品ライフサイクルのイベント処理
  3. 03Dashboard への商品操作パネル追加
  4. 04Saleor 連携のローカル開発とテスト

インストール

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

$npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill saleor-development

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

同梱ファイル

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

tile.json
evals/dashboard-extension-with-app-bridge-comm/
criteria.json
task.md
evals/saleor-app-bootstrap-and-webhook-handlin/
criteria.json
task.md
evals/storefront-product-catalog-with-paginati/
criteria.json
task.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)

基本手順

  1. 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
    
  2. ストアフロントの 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();
    
  3. 顧客を認証してチェックアウトを開始する

    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 }
      }
    }
    
  4. 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 });
    
  5. 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
    
  6. ダッシュボード拡張機能(カスタム 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

コメント