Back to Spree

Store API 5.4 Bridges for 6.0

docs/plans/5.4-store-api-bridges.md

5.4.211.9 KB
Original Source

Store API 5.4 Bridges for 6.0

Status: Implemented (PR #13782) Target: Spree 5.4 (next minor) Depends on: None — all changes are serializer/controller renames on existing models Author: Damian + Claude Last updated: 2026-03-17

Summary

Introduce 6.0 Store API naming NOW in 5.4, with breaking changes. No aliases, no deprecation periods — clean breaks. This gives storefront developers the final API shape before models are renamed in 6.0.

The approach: change the API surface (serializers, response keys, endpoint paths) in 5.4 while the models stay unchanged until 6.0. Serializers are the translation layer.

Scope

Only changes where the API field/endpoint name changes between 5.4 and 6.0. Purely additive features (new endpoints, new fields, new resources) ship with their 6.0 plan and don't need bridging.

Changes

1. Remove master_variant, use default_variant only

Plan: 6.0-remove-master-variant.md

Already done in current API:

  • default_variant_id field on ProductSerializer ✓
  • expand=default_variant on ProductSerializer ✓

Remove now (5.4):

  • Remove expand=master_variant from ProductSerializer
  • Remove is_master field from VariantSerializer
ruby
# ProductSerializer — REMOVE:
one :master,
    key: :master_variant,
    resource: Spree.api.variant_serializer,
    if: proc { expand?('master_variant') }

# VariantSerializer — REMOVE:
# is_master attribute (no longer exposed)

SDK: Remove StoreVariant.is_master from TypeScript types. Remove expand=master_variant from SDK methods.

2. Shipments → Fulfillments, Shipping → Delivery

Plan: 6.0-fulfillment-and-delivery.md

Rename in serializers (model stays Shipment until 6.0):

Before (5.4)After (5.4)Serializer change
shipments (nested in Cart/Order)fulfillmentsChange key: on association
shipping_rates (nested in Shipment)delivery_ratesChange key: on association
shipping_method (nested in ShipmentSerializer)delivery_methodChange key: on association
ShipmentSerializer fields: state, tracking, shipped_atstatus, tracking, fulfilled_atRename attributes
ShippingMethodSerializerRename to DeliveryMethodSerializerNew file, delegates to same model
ShippingRateSerializerRename to DeliveryRateSerializerNew file, delegates to same model
shipment_state (on Order)fulfillment_statusRename attribute
ship_total / display_ship_total (on Cart/Order)delivery_total / display_delivery_totalRename attributes

New fields added:

  • fulfillment_type on ShipmentSerializer (returns 'shipping' or 'digital' based on current shipping method)
  • fulfilled_at (alias for shipped_at)

Endpoint renames:

# Before
GET/PATCH /api/v3/store/carts/:id/shipments
# After
GET/PATCH /api/v3/store/carts/:id/fulfillments

Rename controller file: Carts::ShipmentsControllerCarts::FulfillmentsController.

ruby
# routes
namespace :carts do
  resources :fulfillments, only: [:index, :update]
end

Cart/Order serializer changes:

ruby
# CartSerializer — rename keys
many :shipments,  # model association unchanged
     key: :fulfillments,  # API key changes
     resource: Spree.api.shipment_serializer  # serializer handles field renames

# Rename money fields
attribute :delivery_total do |order|
  order.shipment_total
end
attribute :display_delivery_total do |order|
  Spree::Money.new(order.shipment_total, currency: order.currency).to_s
end
# Remove: ship_total, display_ship_total

SDK: Rename StoreShipmentStoreFulfillment, StoreShippingMethodStoreDeliveryMethod, StoreShippingRateStoreDeliveryRate. Update client.carts.shipmentsclient.carts.fulfillments.

3. State → Status everywhere

Plan: 6.0-normalize-state-to-status.md

Models still use state column until 6.0. Serializers translate:

SerializerBefore (5.4)After (5.4)
ShipmentSerializerstatestatus (reads object.state)
PaymentSerializerstatestatus (reads object.state)
GiftCardSerializerstatestatus (reads object.state)
OrderSerializershipment_statefulfillment_status (reads object.shipment_state)
OrderSerializerpayment_statepayment_status (reads object.payment_state)
ruby
# ShipmentSerializer (becoming FulfillmentSerializer in API)
attribute :status do |shipment|
  shipment.state
end
# Remove: state attribute

# OrderSerializer
attribute :fulfillment_status do |order|
  order.shipment_state
end
attribute :payment_status do |order|
  order.payment_state
end
# Remove: shipment_state, payment_state attributes

SDK: Rename statestatus on StoreFulfillment, StorePayment, StoreGiftCard. Rename shipment_statefulfillment_status, payment_statepayment_status on StoreOrder.

4. Adjustment totals naming

Plan: 6.0-split-adjustments.md

The model split (Adjustment → TaxLine, Discount, Fee) happens in 6.0. But the serialized totals can be renamed now for clarity:

Before (5.4)After (5.4)On
adjustment_totaladjustment_totalCart/Order — keep as-is (sum of all adjustments)
ship_total / display_ship_totaldelivery_total / display_delivery_totalCart/Order (covered in #2 above)

Add new field:

  • fee_total / display_fee_total on Cart/Order (returns 0 until Fee model exists in 6.0)

adjustment_total stays — it's a genuine total across all adjustment types and renaming it would break every storefront's price breakdown display without adding clarity.

5. Classification → ProductCategory (naming only)

Plan: 6.0-replace-taxons-with-categories.md

The Store API already uses categories naming. No serializer changes needed. The Classification model rename is internal.

Implementation Status

All changes implemented in PR #13782. Versions bumped to 0.12.0 for both @spree/sdk and @spree/next.

Files created

  • api/app/serializers/spree/api/v3/fulfillment_serializer.rb
  • api/app/serializers/spree/api/v3/delivery_method_serializer.rb
  • api/app/serializers/spree/api/v3/delivery_rate_serializer.rb
  • api/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb
  • api/app/serializers/spree/api/v3/admin/delivery_method_serializer.rb
  • api/app/serializers/spree/api/v3/admin/delivery_rate_serializer.rb
  • api/app/controllers/spree/api/v3/store/carts/fulfillments_controller.rb
  • api/spec/controllers/spree/api/v3/store/carts/fulfillments_controller_spec.rb
  • api/spec/integration/spree/api/v3/store/fulfillments_spec.rb

Files deleted

  • api/app/serializers/spree/api/v3/shipment_serializer.rb
  • api/app/serializers/spree/api/v3/shipping_method_serializer.rb
  • api/app/serializers/spree/api/v3/shipping_rate_serializer.rb
  • api/app/serializers/spree/api/v3/admin/shipment_serializer.rb
  • api/app/serializers/spree/api/v3/admin/shipping_method_serializer.rb
  • api/app/serializers/spree/api/v3/admin/shipping_rate_serializer.rb
  • api/app/controllers/spree/api/v3/store/carts/shipments_controller.rb
  • api/spec/controllers/spree/api/v3/store/carts/shipments_controller_spec.rb
  • api/spec/integration/spree/api/v3/store/shipments_spec.rb

Files modified

  • cart_serializer.rbship_totaldelivery_total, shipmentsfulfillments
  • order_serializer.rbshipment_statefulfillment_status, payment_statepayment_status, ship_totaldelivery_total, shipmentsfulfillments
  • admin/order_serializer.rbshipmentsfulfillments, user_idcustomer_id, expand=userexpand=customer
  • admin/address_serializer.rbuser_idcustomer_id
  • admin/store_credit_serializer.rbuser_idcustomer_id
  • admin/credit_card_serializer.rbuser_idcustomer_id
  • newsletter_subscriber_serializer.rbuser_idcustomer_id
  • product_serializer.rb — removed expand=master_variant
  • admin/product_serializer.rb — removed expand=master_variant
  • variant_serializer.rb — removed is_master
  • payment_serializer.rbstatestatus
  • gift_card_serializer.rbstatestatus
  • routes.rb/shipments/fulfillments
  • dependencies.rb — added new serializer registrations, pointed old keys to new classes ✓
  • core/app/models/spree/shipment.rb — prefix shipful
  • core/app/models/spree/shipping_method.rb — prefix shpmdm
  • core/app/models/spree/shipping_rate.rb — prefix shprdr
  • core/app/models/spree/inventory_unit.rb — prefix iufi

Auto-generated (regenerated)

  • packages/sdk/src/types/generated/ — all TS types ✓
  • packages/sdk/src/zod/generated/ — all Zod schemas ✓
  • packages/admin-sdk/src/types/generated/ — all admin TS types ✓
  • docs/api-reference/store.yaml — OpenAPI spec ✓

SDK/Next packages

  • packages/sdk/src/store-client.tsshipmentsfulfillments
  • packages/sdk/src/types/index.ts — type re-exports updated ✓
  • packages/sdk/tests/ — tests + mocks updated ✓
  • packages/next/src/actions/cart.tsgetShipmentsgetFulfillments, selectShippingRateselectDeliveryRate
  • packages/next/src/index.ts — re-exports updated ✓
  • packages/next/tests/ — tests updated ✓
  • Both packages bumped to 0.12.0 with changesets ✓

Docs updated

  • packages/sdk/README.md
  • packages/next/README.md
  • docs/developer/sdk/store/cart-checkout.mdx
  • docs/developer/sdk/quickstart.mdx
  • docs/developer/sdk/store/payments.mdx
  • docs/developer/sdk/configuration.mdx
  • docs/developer/storefront/nextjs/spree-next-package.mdx
  • docs/developer/storefront/nextjs/quickstart.mdx

Test results

  • Backend integration tests: 149/149 pass ✓
  • SDK tests: 140/140 pass ✓
  • Next.js tests: 71/71 pass ✓
  • TypeScript typecheck: all packages pass ✓
  • Admin controller/feature specs: pass ✓

What stays unchanged (bridges already done or not needed)

FeatureStatus
imagesmediaDone in 5.4 (PR #13778) ✓
taxonscategoriesAlready using categories in API ✓
expand=default_variantAlready exists ✓
default_variant_id on productsAlready exists ✓
ProductType, Collections, Stock Reservations, Returns/Claims, Channels/CatalogsPurely additive — ship with 6.0

Prefixed IDs — changed in 5.4

Prefixed IDs are updated to match the new vocabulary. The find_by_prefix_id! method ignores the prefix during decoding (it splits on _ and decodes only the Sqids portion), so old IDs with old prefixes still resolve correctly.

ModelOld prefixNew prefix
Shipmentship_ful_
ShippingMethodshpm_dm_
ShippingRateshpr_dr_
InventoryUnitiu_fi_

NOT changed in 5.4 (deferred to 6.0 model rename)

Event names

Event names (shipment.shipped, shipment.canceled) are not changed in 5.4. Webhook subscribers and event listeners depend on these strings. They change in 6.0 to fulfillment.fulfilled, fulfillment.canceled with old names emitted alongside for one release.

Constraints on Current Work

  • All new Store API code must use the 5.4 naming. No shipment, shipping_method, state, master_variant in new endpoints.
  • SDK examples and docs must use new naming after this ships.
  • Admin API is not affected by this plan. Admin serializers may keep old naming until 6.0 model renames happen (or can be bridged separately).

References

  • Prior art: 5.4-6.0-product-media-system.md (same bridge pattern: API naming changed in 5.4, model renamed in 6.0)
  • All 6.0 plans in docs/plans/6.0-*.md
  • Store API inventory: 32 endpoint groups, 56 serializers