spree-legacy-api-v2 について
spree-legacy-api-v2は、Spree API v2の連携を保守する開発者や、v3へ移行する開発者向けの資料です。旧APIの構成や、認証・レスポンス形式などの段階的な変更点を説明します。
主な機能
- 01Storefront APIとPlatform APIの違い
- 02OAuth2認証とトークンヘッダー
- 03JSON:API形式とページネーション
- 04v2からv3への認証・ID・検索条件の変更
- 05旧SDKとWebhookの移行情報
活用シーン
- 01既存のv2連携の保守
- 02API v3への段階的な移行計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ spree/ agent-skills --skill spree-legacy-api-v2インストール後、Claude Code で/spree-legacy-api-v2を入力して読み込まれたか確認します。
詳細
Spree API v2 — レガシー
デフォルトの推奨事項:v2 をベースに新しいものを構築しないでください。 Spree 5.4 以降には、今後の方向性を示す API として API v3 が搭載されています。v2 は引き続き動作しますが、機能追加は凍結されています。新しいエンドポイント、新しい認証方式(publishable + secret + JWT)、プレフィックス付き ID、expand、型付き SDK はすべて v3 の機能です。新しい連携は v3 から始めてください。
このスキルが存在するのには、実際的な理由が 2 つあります。
- v2 の連携を引き継いだため、保守する必要がある。
- v2 から v3 への移行中で、変更点を把握する必要がある。
どちらにも当てはまらない場合は、代わりに spree-api-v3 スキルを使用してください。
v2 の概要
/api/v2/ 配下には、異なる 2 つの API サーフェスがあります。
| サーフェス | パス | 対象 |
|---|---|---|
| Storefront API | /api/v2/storefront/* | 顧客向け — カート、商品、チェックアウト |
| Platform API | /api/v2/platform/* | 管理/バックオフィス向け — 完全な CRUD |
どちらも JSON:API 形式(https://jsonapi.org/)で、`data`、`attributes`、`relationships`、`included` を含む厳格なエンベロープを使用します。v3 とはエンベロープの形が異なります。
v2 の認証
| サーフェス | ヘッダー | トークン |
|---|---|---|
| Storefront(匿名での閲覧) | なし | パブリックアクセス |
| Storefront(ゲストカート) | X-Spree-Order-Token | カートの注文トークン |
| Storefront(ログイン済み顧客) | Authorization: Bearer <oauth_token> | OAuth2 顧客トークン |
| Platform(管理者) | Authorization: Bearer <oauth_token> | OAuth2 管理者トークン |
v2 では、トークンの発行に OAuth2 Doorkeeper を使用します。password または client_credentials グラントを指定して、/spree_oauth/token エンドポイントを使用します。publishable key や secret key はありません。OAuth スコープはありますが、大まかな区分(admin、write、read)です(Platform API で読み取りを行うには read/admin、書き込みを行うには write/admin が必要です)。v3 の API キーのスコープである read_products/write_orders のような、リソースごとの細かなスコープはありません。
# Get a Platform API token (admin)
curl -X POST https://my-spree.example.com/spree_oauth/token \
--header "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=password&[email protected]&password=…"
# => { "access_token": "…", "token_type": "Bearer", "expires_in": 2592000, ... }
# (tokens last 1 month by default — `access_token_expires_in 1.month`)
# Use it
curl -H "Authorization: Bearer …" \
https://my-spree.example.com/api/v2/platform/orders
エンベロープの形
{
"data": {
"id": "42", // raw integer PK as string
"type": "product",
"attributes": {
"name": "Cool shirt",
"price": "29.99",
"currency": "USD",
"created_at": "2025-01-01T00:00:00Z"
},
"relationships": {
"variants": {
"data": [{ "id": "100", "type": "variant" }]
}
}
},
"included": [
{
"id": "100",
"type": "variant",
"attributes": { "sku": "COOL-S", ... }
}
],
"meta": { "total_count": 152, "total_pages": 7 }
}
v3 との比較:
- v2:整数の ID(
"42")。v3:プレフィックス付き ID("prod_…")。 - v2:ネストされた
attributesとrelationships。v3:トップレベルにフラットなフィールド。 - v2:
includeクエリパラメーターで関連リソースをincluded配列に取得します。v3:expandクエリパラメーターで関連リソースをレスポンスに直接インライン展開します(?expand=variants,media。ドットでネストできるのは最大 4 階層)。 - v2(Platform API):フィルターには
filter[name_cont]=…を使用します。v3:同じ考え方ですが、q[name_cont]=…を使用します(Ransack)。Storefront API ではエンドポイントごとに固定されたフィルター名を使用していました。詳しくは以下のステップ 4 を参照してください。 - v2:ソートには
sort=-created_atを使用します。v3:同じ指定がそのまま使えます。v3 の ResourceController が内部で-field形式の JSON:API 表記を Ransack のsに変換するため、sort=-created_atは変更せずに動作します。
ページネーション
GET /api/v2/storefront/products?page=2&per_page=25
meta.total_count、meta.total_pages。v2 にはキーセット/カーソル方式のサポートがなく、オフセット方式のみです。
v2 を使用している場合
「v2 のレスポンスにフィールドを追加したい」
JSON:API シリアライザー(内部では jsonapi-serializer gem)をデコレートします。
module Spree::V2::Storefront::ProductSerializerDecorator
def self.prepended(base)
base.attribute :custom_field do |product|
product.custom_field_value
end
end
Spree::V2::Storefront::ProductSerializer.prepend self
end
「v2 に新しいエンドポイントが必要」
v2 には追加しないでください。代わりに v3 に追加します。spree:api_resource ジェネレーターについては spree-resource スキルを参照してください。お使いの v2 クライアントがどうしても v3 と通信できない場合は、内部では v3 のリソースからデータを取得しつつ、v2 形式のエンベロープの JSON を返す薄い互換ラッパーを作成してください。
「v2 がエラーを返す。エラーの形式は?」
{
"error": "The resource was not found.",
"errors": { ... } // on 422
}
JSON:API のエラーオブジェクトではなく、通常の JSON です。v3 では構造化された { error: { code, message, details } } エンベロープを使用します。詳しくは spree-api-v3 を参照してください。
v2 から v3 への移行
移行は エンドポイント単位で行います。一度にすべてを切り替える必要はありません。v2 と v3 は共存します。
ステップ 1:認証の移行
| v2 | v3 |
|---|---|
| 顧客ログイン用の OAuth2 password grant | /api/v3/store/auth/login での JWT ログイン(token + refresh_token + user を返します) |
| 管理アプリ用の OAuth2 client_credentials | スコープ付き権限を持つシークレットキー(sk_*) |
Authorization: Bearer <token> | X-Spree-Api-Key: <pk_…|sk_…> + 任意の Authorization: Bearer <jwt> |
ゲストカート用の X-Spree-Order-Token | 引き続き X-Spree-Token(名称変更) |
管理アプリの場合:必要なスコープを設定して、管理 UI(Settings → Developers → API Keys、キータイプは「Secret」)からシークレットキーを生成します。OAuth2 は完全に廃止してください。
顧客フローの場合:OAuth2 ログイン呼び出しを JWT ログインに置き換えます。新しいフローではリフレッシュトークンも返されます。Cookie ベースのリフレッシュパターンについては、管理者認証のドキュメントを参照してください。
ステップ 2:ID の移行
v2 は整数 ID を文字列として返しますが、v3 はプレフィックス付き ID を返します。クライアントで ID を保存している場合:
// Old
const productId = response.data.id // "42"
// New
const productId = response.data.id // "prod_86Rf07xd4z"
v2 の ID を独自の DB に永続化しており、v3 のレスポンスと照合する必要がある場合、整数 PK は同じです。整数を指定して v3 の検索エンドポイントを使用してください。
GET /api/v3/admin/products?q[id_eq]=42
クライアントコードで生の整数 ID を公開するのは v2 のアンチパターンであり、v3 では解消されています。新しい ID は URL セーフで、レコード数が漏れることもありません。
ステップ 3:エンベロープのアンラップ
// v2 JSON:API
const product = response.data
const name = product.attributes.name
const price = product.attributes.price
const variantIds = product.relationships.variants.data.map(v => v.id)
const variants = response.included.filter(r => r.type === 'variant')
// v3 flat
const product = response // single endpoint — no envelope
const name = product.name
const price = product.price
const defaultVariantId = product.default_variant_id // direct field
// With ?expand=variants: product.variants is an array of full variant objects
すべての呼び出し箇所を一度にリファクタリングできない場合は、薄いアダプターを作成してください。
function unwrapV2<T>(response: V2Response<T>): T {
return {
id: response.data.id,
...response.data.attributes,
...buildRelationships(response.data.relationships, response.included),
} as T
}
ステップ 4:フィルター構文
| v2 | v3 |
|---|---|
filter[name_cont]=shirt(Platform API) | q[name_cont]=shirt |
filter[price_gteq]=20(Platform API) | q[price_gteq]=20 |
sort=-created_at | sort=-created_at(変更なし) |
Platform API では、filter[...] キーは Ransack の述語であり、v3 の q[...] に 1 対 1 で対応します。filter[name_cont] は q[name_cont] になります。Storefront API では Ransack を使用していませんでした。各エンドポイントには finder クラスが処理する固定のフィルター名があったため、代わりに次のように変換してください。filter[name] → q[name_cont](または q[search])、filter[taxons] → q[in_categories]、filter[price]=10,50 → q[price_gte]=10&q[price_lte]=50。
@spree/sdk は、型付きリストパラメーターとしてフラットな Ransack 述語を受け付けます(client.products.list({ name_cont: 'shirt' }))。q[name_cont]=... は SDK が生成します。詳細は spree-typescript-sdk を参照してください。
ステップ 5:SDK の切り替え
古い @spree/storefront-api-v2-sdk パッケージ(JS)を使用している場合は、@spree/sdk(Store API)または @spree/admin-sdk(Admin API)に置き換えてください。新しい SDK は型付けされており、組み込みのリトライ機能を備え、v3 の他の部分と同じプロトコルに従います。
ステップ 6:Webhook(該当する場合)
v2 の Webhook を受信している場合:v2 の Webhook は、生の ID とレガシーなペイロード形式で送信されます。v3 の Webhook は、プレフィックス付き ID と {id, name, created_at, data, metadata} のエンベロープ形式を使用します。エンドポイントを v3 に切り替える場合は、Webhook 受信側で新しいペイロード形式を処理できるようにする必要があります。詳細は spree-events-webhooks を参照してください。
v2 の終了時期
v2 は今後 spree_api に同梱されません。Spree 5.5 以降、v2 は独立した非推奨の spree_legacy_api_v2 gem に含まれており、Spree 5 で動作します。既存の v2 統合を引き続き動作させるには、この gem を追加してください(bundle add spree_legacy_api_v2)。新規アプリでは、さらに bin/rails g spree:legacy_api_v2:install を実行してマイグレーションをインストールしてください(以前の Spree バージョンからアップグレードしたアプリには、すでにマイグレーションが含まれています)。API v2 は次のメジャーリリースで削除される予定で、新機能は追加されません。新しいフィールド、エンドポイント、機能(Markets や新しい価格設定エンジンなど)は v3 にのみ追加されます。
重要度の高い v2 統合を維持している場合は、移行を計画してください。そうしなければ、さらに遅れを取ることになります。
さらに詳しく読むには
- v3 API プロトコル:
spree-api-v3スキル — 移行先です。 - v3 TypeScript SDK:
spree-typescript-sdkスキル。 - v3 にカスタムエンドポイントを追加する:
spree-resourceスキル。 - v2 のソース:
spree_legacy_api_v2gem — コントローラーとシリアライザー(jsonapi-serializer ベース)。v2 はspree_apiに同梱されなくなりました。 - v2 OpenAPI 仕様:
docs/api-reference/storefront.yamlとdocs/api-reference/platform.yaml。v3 仕様はdocs/api-reference/store.yamlです(@spree/docsnpm パッケージのnode_modules/@spree/docs/dist/api-reference/store.yamlにも同梱されています)。