Skip to main content
The dashboard never reaches into Rails models or fetches HTML. All data flows through @spree/admin-sdk — a typed Admin API client. This page covers the patterns: calling existing endpoints, adding new ones, handling errors, and wrapping it all in React-Query hooks for caching.

The adminClient

The SPA boots a single adminClient instance and exposes it from @spree/dashboard-core. Resource methods follow client.<resource>.<verb>():
All IDs are prefixed — pass them in, get them out. The SDK never coerces to integers.

Custom endpoints

When you’ve added an endpoint to spree/api that the SDK doesn’t model yet (host-app routes, an in-development feature, a custom controller), use the public request<T>() escape hatch:
It accepts:
  • method'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'
  • path — relative to /api/v3/admin (so /brands hits /api/v3/admin/brands)
  • body — JSON-stringified for non-GET requests
  • params — query string
Resource methods are just request<T>() calls with the right path + types pre-bound. When you publish your customization or generate types from your serializer, you can promote the inline call to a typed wrapper.

React-Query hooks

The dashboard’s data layer is React-Query — every useFoo hook is useQuery({ queryKey, queryFn }) over an SDK call. Follow the same pattern for your own data:
For mutations, prefer the existing useResourceMutation helper — it wires up the SDK’s error shape, the 422 silencer, and the toast for non-validation errors:
The invalidate array refetches dependent queries on success.

Error handling

The SDK throws SpreeError for non-2xx responses. The error carries status, code, and details — the last contains the Rails error hash for 422s.
In a React Hook Form submit handler, prefer the higher-level helper:
mapSpreeErrorsToForm routes flat field errors onto aria-invalid <FieldError> blocks and :base errors onto errors.root.message for a destructive banner. Returns true if it handled the error; otherwise re-throw so the global toast catches it.

When to add a new endpoint

Pretty much always, for anything non-trivial. If you’re filtering, sorting, or aggregating across multiple resources, do it on the backend — the API can join, scope, and serialize correctly in one round-trip, whereas the dashboard would have to fan out N requests and stitch them together. See the backend customization docs for adding controllers, serializers, and authorization.

Reference