docs/developer/customization/api.mdx
Before you start customizing Spree API endpoints, make sure you reviewed all existing API endpoints in the Spree API docs.
For a step-by-step walkthrough of adding a complete new resource (model, serializer, controller, routes), see the Store API tutorial.
Spree uses Alba serializers to build API v3 responses. You can replace any serializer via Spree Dependencies.
Let's say you want to add a my_custom_attribute column to the Product API response.
Create a custom serializer that inherits from the core one:
module MyApp
class ProductSerializer < Spree::Api::V3::ProductSerializer
attribute :my_custom_attribute
end
end
Register it in config/initializers/spree.rb:
Spree::Api::Dependencies.product_serializer = 'MyApp::ProductSerializer'
Restart the server and the Product API will include your new attribute.
Let's say you've created a Spree::Brand model that belongs to Product (see the tutorial for the full example).
Create a serializer for the new model:
module Spree
module Api
module V3
class BrandSerializer < BaseSerializer
typelize name: :string, slug: [:string, nullable: true]
attributes :name, :slug
end
end
end
end
Then subclass the Product serializer to include the brand association:
module MyApp
class ProductSerializer < Spree::Api::V3::ProductSerializer
typelize brand_id: [:string, nullable: true]
attribute :brand_id do |product|
product.brand&.prefixed_id
end
one :brand,
resource: Spree::Api::V3::BrandSerializer,
if: proc { expand?('brand') }
end
end
Register it in config/initializers/spree.rb:
Spree::Api::Dependencies.product_serializer = 'MyApp::ProductSerializer'
The brand data is available via ?expand=brand:
GET /api/v3/store/products/prod_86Rf07xd4z?expand=brand
Here are the most commonly customized v3 serializers:
| Key | Default |
|---|---|
product_serializer | Spree::Api::V3::ProductSerializer |
variant_serializer | Spree::Api::V3::VariantSerializer |
cart_serializer | Spree::Api::V3::CartSerializer |
order_serializer | Spree::Api::V3::OrderSerializer |
line_item_serializer | Spree::Api::V3::LineItemSerializer |
category_serializer | Spree::Api::V3::CategorySerializer |
customer_serializer | Spree::Api::V3::CustomerSerializer |
address_serializer | Spree::Api::V3::AddressSerializer |
payment_serializer | Spree::Api::V3::PaymentSerializer |
fulfillment_serializer | Spree::Api::V3::FulfillmentSerializer |
See Spree::Api::ApiDependencies for the full list.
Inherit from Spree::Api::V3::Store::ResourceController for Store API endpoints:
module Spree
module Api
module V3
module Store
class BrandsController < ResourceController
protected
def model_class
Spree::Brand
end
def serializer_class
Spree::Api::V3::BrandSerializer
end
def scope
Spree::Brand.all
end
end
end
end
end
end
The base ResourceController provides:
| Feature | How |
|---|---|
| Pagination | Pagy — ?page=2&limit=25 |
| Filtering | Ransack — ?q[name_cont]=nike |
| Sorting | JSON:API style — ?sort=-name |
| Authorization | CanCanCan |
| Prefixed IDs | Stripe-style — brand_k5nR8xLq |
Override these methods to customize:
| Method | Purpose |
|---|---|
model_class | ActiveRecord model (required) |
serializer_class | Alba serializer (required) |
scope | Base query — add .where() to filter |
find_resource | Custom ID lookup (slug fallback, etc.) |
permitted_params | Custom strong params |
collection_includes | Eager loading for index |
Add routes in your app's config/routes.rb:
Spree::Core::Engine.add_routes do
namespace :api, defaults: { format: 'json' } do
namespace :v3 do
namespace :store do
resources :brands, only: [:index, :show]
end
end
end
end
For endpoints that accept writes (create/update), add your attributes:
Spree::PermittedAttributes.brand_attributes = [:name, :slug, :description, :logo]
Or override permitted_params in the controller:
def permitted_params
params.permit(:name, :slug, :description, :logo)
end
See the SDK tutorial for how to call custom endpoints from TypeScript and extend the generated types.