@spree/docs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/dist/api-reference/platform/authentication.md +38 -0
- package/dist/api-reference/store-api/authentication.md +188 -0
- package/dist/api-reference/store-api/errors.md +277 -0
- package/dist/api-reference/store-api/idempotency.md +129 -0
- package/dist/api-reference/store-api/introduction.md +34 -0
- package/dist/api-reference/store-api/localization.md +279 -0
- package/dist/api-reference/store-api/metadata.md +160 -0
- package/dist/api-reference/store-api/monetary-amounts.md +65 -0
- package/dist/api-reference/store-api/querying.md +399 -0
- package/dist/api-reference/store-api/rate-limitting.md +103 -0
- package/dist/api-reference/store-api/relations.md +185 -0
- package/dist/api-reference/storefront/authentication.md +88 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
- package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
- package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
- package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
- package/dist/api-reference/v2/introduction.md +22 -0
- package/dist/api-reference/v2/pagination.md +37 -0
- package/dist/api-reference/webhooks-events.md +883 -0
- package/dist/developer/admin/admin.md +205 -0
- package/dist/developer/admin/authentication.md +59 -0
- package/dist/developer/admin/components.md +711 -0
- package/dist/developer/admin/custom-css.md +243 -0
- package/dist/developer/admin/custom-javascript.md +116 -0
- package/dist/developer/admin/extending-ui.md +1964 -0
- package/dist/developer/admin/form-builder.md +444 -0
- package/dist/developer/admin/helper-methods.md +531 -0
- package/dist/developer/admin/navigation.md +805 -0
- package/dist/developer/admin/tables.md +491 -0
- package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
- package/dist/developer/cli/quickstart.md +137 -0
- package/dist/developer/contributing/creating-an-extension.md +258 -0
- package/dist/developer/contributing/developing-spree.md +339 -0
- package/dist/developer/contributing/quickstart.md +32 -0
- package/dist/developer/contributing/updating-extensions.md +67 -0
- package/dist/developer/core-concepts/addresses.md +265 -0
- package/dist/developer/core-concepts/adjustments.md +107 -0
- package/dist/developer/core-concepts/architecture.md +177 -0
- package/dist/developer/core-concepts/calculators.md +323 -0
- package/dist/developer/core-concepts/customers.md +230 -0
- package/dist/developer/core-concepts/events.md +624 -0
- package/dist/developer/core-concepts/imports-exports.md +698 -0
- package/dist/developer/core-concepts/inventory.md +191 -0
- package/dist/developer/core-concepts/markets.md +250 -0
- package/dist/developer/core-concepts/media.md +167 -0
- package/dist/developer/core-concepts/metafields.md +187 -0
- package/dist/developer/core-concepts/orders.md +328 -0
- package/dist/developer/core-concepts/payments.md +710 -0
- package/dist/developer/core-concepts/pricing.md +163 -0
- package/dist/developer/core-concepts/products.md +360 -0
- package/dist/developer/core-concepts/promotions.md +322 -0
- package/dist/developer/core-concepts/reports.md +206 -0
- package/dist/developer/core-concepts/search-filtering.md +237 -0
- package/dist/developer/core-concepts/shipments.md +212 -0
- package/dist/developer/core-concepts/slugs.md +111 -0
- package/dist/developer/core-concepts/staff-roles.md +123 -0
- package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
- package/dist/developer/core-concepts/stores.md +117 -0
- package/dist/developer/core-concepts/taxes.md +135 -0
- package/dist/developer/core-concepts/translations.md +120 -0
- package/dist/developer/core-concepts/users.md +299 -0
- package/dist/developer/core-concepts/webhooks.md +378 -0
- package/dist/developer/create-spree-app/quickstart.md +158 -0
- package/dist/developer/customization/api.md +93 -0
- package/dist/developer/customization/authentication.md +88 -0
- package/dist/developer/customization/checkout.md +204 -0
- package/dist/developer/customization/configuration.md +55 -0
- package/dist/developer/customization/decorators.md +523 -0
- package/dist/developer/customization/dependencies.md +232 -0
- package/dist/developer/customization/emails.md +21 -0
- package/dist/developer/customization/extensions.md +92 -0
- package/dist/developer/customization/metadata.md +236 -0
- package/dist/developer/customization/model-preferences.md +130 -0
- package/dist/developer/customization/permissions.md +265 -0
- package/dist/developer/customization/quickstart.md +229 -0
- package/dist/developer/customization/routes.md +24 -0
- package/dist/developer/customization/v4/admin-panel.md +78 -0
- package/dist/developer/customization/v4/authentication.md +210 -0
- package/dist/developer/customization/v4/checkout.md +212 -0
- package/dist/developer/customization/v4/deface.md +251 -0
- package/dist/developer/customization/v4/images.md +86 -0
- package/dist/developer/customization/v4/storefront.md +450 -0
- package/dist/developer/deployment/assets.md +87 -0
- package/dist/developer/deployment/aws.md +335 -0
- package/dist/developer/deployment/caching.md +27 -0
- package/dist/developer/deployment/cdn.md +39 -0
- package/dist/developer/deployment/database.md +155 -0
- package/dist/developer/deployment/docker.md +128 -0
- package/dist/developer/deployment/emails.md +77 -0
- package/dist/developer/deployment/environment_variables.md +111 -0
- package/dist/developer/deployment/heroku.md +51 -0
- package/dist/developer/deployment/render.md +95 -0
- package/dist/developer/getting-started/quickstart.md +82 -0
- package/dist/developer/how-to/custom-payment-method.md +374 -0
- package/dist/developer/how-to/custom-promotion.md +373 -0
- package/dist/developer/how-to/custom-report.md +387 -0
- package/dist/developer/how-to/custom-search-provider.md +230 -0
- package/dist/developer/multi-store/quickstart.md +71 -0
- package/dist/developer/multi-store/setup.md +38 -0
- package/dist/developer/multi-tenant/configuration.md +41 -0
- package/dist/developer/multi-tenant/core-concepts.md +75 -0
- package/dist/developer/multi-tenant/installation.md +96 -0
- package/dist/developer/multi-tenant/quickstart.md +20 -0
- package/dist/developer/multi-vendor/installation.md +45 -0
- package/dist/developer/multi-vendor/quickstart.md +17 -0
- package/dist/developer/sdk/admin/quickstart.md +22 -0
- package/dist/developer/sdk/authentication.md +89 -0
- package/dist/developer/sdk/configuration.md +225 -0
- package/dist/developer/sdk/quickstart.md +82 -0
- package/dist/developer/sdk/store/account.md +67 -0
- package/dist/developer/sdk/store/cart-checkout.md +140 -0
- package/dist/developer/sdk/store/markets.md +151 -0
- package/dist/developer/sdk/store/payments.md +96 -0
- package/dist/developer/sdk/store/products.md +149 -0
- package/dist/developer/sdk/store/wishlists.md +52 -0
- package/dist/developer/security/pci_compliance.md +15 -0
- package/dist/developer/security/security_policy.md +68 -0
- package/dist/developer/storefront/blocks.md +285 -0
- package/dist/developer/storefront/custom-css.md +260 -0
- package/dist/developer/storefront/custom-javascript.md +166 -0
- package/dist/developer/storefront/helper-methods.md +1288 -0
- package/dist/developer/storefront/links.md +298 -0
- package/dist/developer/storefront/nextjs/architecture.md +150 -0
- package/dist/developer/storefront/nextjs/customization.md +141 -0
- package/dist/developer/storefront/nextjs/deployment.md +180 -0
- package/dist/developer/storefront/nextjs/quickstart.md +92 -0
- package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
- package/dist/developer/storefront/pages.md +163 -0
- package/dist/developer/storefront/sections.md +569 -0
- package/dist/developer/storefront/storefront.md +56 -0
- package/dist/developer/storefront/themes.md +161 -0
- package/dist/developer/tutorial/admin.md +134 -0
- package/dist/developer/tutorial/extending-models.md +380 -0
- package/dist/developer/tutorial/file-uploads.md +121 -0
- package/dist/developer/tutorial/introduction.md +33 -0
- package/dist/developer/tutorial/model.md +41 -0
- package/dist/developer/tutorial/page-builder.md +487 -0
- package/dist/developer/tutorial/rich-text.md +73 -0
- package/dist/developer/tutorial/seo.md +332 -0
- package/dist/developer/tutorial/storefront.md +352 -0
- package/dist/developer/tutorial/testing.md +558 -0
- package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
- package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
- package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
- package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
- package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
- package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
- package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
- package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
- package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
- package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
- package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
- package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
- package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
- package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
- package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
- package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
- package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
- package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
- package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
- package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
- package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
- package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
- package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
- package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
- package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
- package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
- package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
- package/dist/developer/upgrades/quickstart.md +36 -0
- package/dist/integrations/analytics/google-analytics.md +64 -0
- package/dist/integrations/analytics/google-tag-manager.md +78 -0
- package/dist/integrations/integrations.md +39 -0
- package/dist/integrations/marketing/klaviyo.md +99 -0
- package/dist/integrations/payments/adyen.md +90 -0
- package/dist/integrations/payments/paypal.md +41 -0
- package/dist/integrations/payments/razorpay.md +45 -0
- package/dist/integrations/payments/stripe.md +109 -0
- package/dist/integrations/search/meilisearch.md +236 -0
- package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
- package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
- package/package.json +27 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Querying"
|
|
3
|
+
sidebarTitle: "Querying"
|
|
4
|
+
description: "How to filter, sort, and paginate Store API results using Ransack"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The Store API uses [Ransack](https://activerecord-hackery.github.io/ransack/) for filtering and sorting collection endpoints. All query parameters are passed via the `q` parameter.
|
|
8
|
+
|
|
9
|
+
## Filtering
|
|
10
|
+
|
|
11
|
+
Pass filter conditions using the `q` parameter with Ransack predicates:
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
```typescript SDK
|
|
15
|
+
const products = await client.products.list({
|
|
16
|
+
name_cont: 'shirt',
|
|
17
|
+
price_gte: 20,
|
|
18
|
+
price_lte: 100,
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```bash cURL
|
|
23
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
24
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
25
|
+
--data-urlencode 'q[name_cont]=shirt' \
|
|
26
|
+
--data-urlencode 'q[price_gte]=20' \
|
|
27
|
+
--data-urlencode 'q[price_lte]=100'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Common Predicates
|
|
32
|
+
|
|
33
|
+
| Predicate | Description | SDK | cURL |
|
|
34
|
+
|-----------|-------------|-----|------|
|
|
35
|
+
| `eq` | Equals | `status_eq: 'active'` | `q[status_eq]=active` |
|
|
36
|
+
| `not_eq` | Not equals | `status_not_eq: 'draft'` | `q[status_not_eq]=draft` |
|
|
37
|
+
| `cont` | Contains (case-insensitive) | `name_cont: 'shirt'` | `q[name_cont]=shirt` |
|
|
38
|
+
| `start` | Starts with | `name_start: 'Spree'` | `q[name_start]=Spree` |
|
|
39
|
+
| `end` | Ends with | `slug_end: 'tote'` | `q[slug_end]=tote` |
|
|
40
|
+
| `lt` | Less than | `price_lt: 50` | `q[price_lt]=50` |
|
|
41
|
+
| `lteq` | Less than or equal | `price_lteq: 50` | `q[price_lteq]=50` |
|
|
42
|
+
| `gt` | Greater than | `price_gt: 10` | `q[price_gt]=10` |
|
|
43
|
+
| `gteq` | Greater than or equal | `price_gteq: 10` | `q[price_gteq]=10` |
|
|
44
|
+
| `in` | In a list | `status_in: ['active', 'draft']` | `q[status_in][]=active&q[status_in][]=draft` |
|
|
45
|
+
| `null` | Is null | `deleted_at_null: true` | `q[deleted_at_null]=true` |
|
|
46
|
+
| `not_null` | Is not null | `completed_at_not_null: true` | `q[completed_at_not_null]=true` |
|
|
47
|
+
| `present` | Is present (not empty) | `description_present: true` | `q[description_present]=true` |
|
|
48
|
+
| `blank` | Is blank (null or empty) | `description_blank: true` | `q[description_blank]=true` |
|
|
49
|
+
| `true` | Is true (boolean) | `purchasable_true: 1` | `q[purchasable_true]=1` |
|
|
50
|
+
| `false` | Is false (boolean) | `purchasable_false: 1` | `q[purchasable_false]=1` |
|
|
51
|
+
|
|
52
|
+
> **NOTE:** The SDK automatically wraps filter keys in `q[...]` and appends `[]` for array values — just pass flat params.
|
|
53
|
+
|
|
54
|
+
### Combining Filters
|
|
55
|
+
|
|
56
|
+
Multiple filters are combined with AND logic:
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
```typescript SDK
|
|
60
|
+
// Products that contain "shirt" AND cost between $20-$100
|
|
61
|
+
const products = await client.products.list({
|
|
62
|
+
name_cont: 'shirt',
|
|
63
|
+
price_gteq: 20,
|
|
64
|
+
price_lteq: 100,
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```bash cURL
|
|
69
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
70
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
71
|
+
--data-urlencode 'q[name_cont]=shirt' \
|
|
72
|
+
--data-urlencode 'q[price_gteq]=20' \
|
|
73
|
+
--data-urlencode 'q[price_lteq]=100'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### Filtering by Association
|
|
78
|
+
|
|
79
|
+
You can filter by associated model attributes using underscore notation:
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
```typescript SDK
|
|
83
|
+
// Products in a specific category
|
|
84
|
+
const products = await client.products.list({
|
|
85
|
+
categories_id_eq: 'ctg_abc123',
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Categories at a specific depth
|
|
89
|
+
const categories = await client.categories.list({
|
|
90
|
+
depth_eq: 1,
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```bash cURL
|
|
95
|
+
# Products in a specific category
|
|
96
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
97
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
98
|
+
--data-urlencode 'q[categories_id_eq]=ctg_abc123'
|
|
99
|
+
|
|
100
|
+
# Top-level categories only
|
|
101
|
+
curl -G 'http://localhost:3000/api/v3/store/categories' \
|
|
102
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
103
|
+
--data-urlencode 'q[depth_eq]=1'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
## Product Scopes
|
|
108
|
+
|
|
109
|
+
Products support additional filter scopes beyond standard Ransack predicates:
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
```typescript SDK
|
|
113
|
+
const products = await client.products.list({
|
|
114
|
+
price_gte: 20, // Minimum price
|
|
115
|
+
price_lte: 100, // Maximum price
|
|
116
|
+
with_option_value_ids: ['optval_abc', 'optval_def'], // Filter by option values
|
|
117
|
+
in_stock: true, // Only in-stock products
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```bash cURL
|
|
122
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
123
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
124
|
+
--data-urlencode 'q[price_gte]=20' \
|
|
125
|
+
--data-urlencode 'q[price_lte]=100' \
|
|
126
|
+
--data-urlencode 'q[with_option_value_ids][]=optval_abc' \
|
|
127
|
+
--data-urlencode 'q[with_option_value_ids][]=optval_def' \
|
|
128
|
+
--data-urlencode 'q[in_stock]=true'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
| Scope | Description | SDK | cURL |
|
|
133
|
+
|-------|-------------|-----|------|
|
|
134
|
+
| `price_gte` | Minimum price | `price_gte: 20` | `q[price_gte]=20` |
|
|
135
|
+
| `price_lte` | Maximum price | `price_lte: 100` | `q[price_lte]=100` |
|
|
136
|
+
| `with_option_value_ids` | Filter by option value IDs | `with_option_value_ids: ['optval_abc']` | `q[with_option_value_ids][]=optval_abc` |
|
|
137
|
+
| `in_stock` | Only in-stock products | `in_stock: true` | `q[in_stock]=true` |
|
|
138
|
+
| `out_of_stock` | Only out-of-stock products | `out_of_stock: true` | `q[out_of_stock]=true` |
|
|
139
|
+
| `search` | Full-text search | `search: 'shirt'` | `q[search]=shirt` |
|
|
140
|
+
|
|
141
|
+
## Sorting
|
|
142
|
+
|
|
143
|
+
Use the `sort` parameter on any list endpoint. Prefix a field with `-` for descending order (ascending is the default). This follows the [JSON:API sorting convention](https://jsonapi.org/format/#fetching-sorting).
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
```typescript SDK
|
|
147
|
+
// Sort by price (low to high)
|
|
148
|
+
const products = await client.products.list({
|
|
149
|
+
sort: 'price',
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// Sort by price (high to low)
|
|
153
|
+
const products = await client.products.list({
|
|
154
|
+
sort: '-price',
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Sort by best selling
|
|
158
|
+
const products = await client.products.list({
|
|
159
|
+
sort: 'best_selling',
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
```bash cURL
|
|
164
|
+
# Sort by price low to high
|
|
165
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
166
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
167
|
+
-d 'sort=price'
|
|
168
|
+
|
|
169
|
+
# Sort by price high to low
|
|
170
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
171
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
172
|
+
-d 'sort=-price'
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
### Product Sort Options
|
|
177
|
+
|
|
178
|
+
| Value | Description |
|
|
179
|
+
|-------|-------------|
|
|
180
|
+
| `manual` | Manual sort order (default, uses taxon position) |
|
|
181
|
+
| `best_selling` | Best selling products first |
|
|
182
|
+
| `price` | Price low to high |
|
|
183
|
+
| `-price` | Price high to low |
|
|
184
|
+
| `name` | Name A-Z |
|
|
185
|
+
| `-name` | Name Z-A |
|
|
186
|
+
| `-available_on` | Newest first |
|
|
187
|
+
| `available_on` | Oldest first |
|
|
188
|
+
|
|
189
|
+
### Sorting Other Resources
|
|
190
|
+
|
|
191
|
+
The `sort` parameter works on all list endpoints. Use any sortable column name:
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
```typescript SDK
|
|
195
|
+
// Categories sorted by name
|
|
196
|
+
const categories = await client.categories.list({
|
|
197
|
+
sort: 'name',
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
// Customer orders sorted by most recent
|
|
201
|
+
const orders = await client.customer.orders.list({
|
|
202
|
+
sort: '-completed_at',
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```bash cURL
|
|
207
|
+
# Categories sorted by name
|
|
208
|
+
curl -G 'http://localhost:3000/api/v3/store/categories' \
|
|
209
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
210
|
+
-d 'sort=name'
|
|
211
|
+
|
|
212
|
+
# Customer orders by most recent
|
|
213
|
+
curl -G 'http://localhost:3000/api/v3/store/customer/orders' \
|
|
214
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
215
|
+
-H 'Authorization: Bearer <token>' \
|
|
216
|
+
-d 'sort=-completed_at'
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
## Expanding Associations
|
|
221
|
+
|
|
222
|
+
Use the `expand` parameter to include associated resources in the response. Pass a comma-separated list of association names:
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
```typescript SDK
|
|
226
|
+
const product = await client.products.get('spree-tote', {
|
|
227
|
+
expand: ['variants', 'images'],
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
```bash cURL
|
|
232
|
+
curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \
|
|
233
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
234
|
+
-d 'expand=variants,images'
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
### Nested Expand
|
|
239
|
+
|
|
240
|
+
Use dot notation to expand nested associations (up to 4 levels deep):
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
```typescript SDK
|
|
244
|
+
// Expand variants and their images in one request
|
|
245
|
+
const product = await client.products.get('spree-tote', {
|
|
246
|
+
expand: ['variants.images'],
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Multiple nested expands
|
|
250
|
+
const product = await client.products.get('spree-tote', {
|
|
251
|
+
expand: ['variants.images', 'variants.metafields', 'option_types'],
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// Category with nested children
|
|
255
|
+
const category = await client.categories.get('clothing/shirts', {
|
|
256
|
+
expand: ['children.children'], // Two levels of subcategories
|
|
257
|
+
})
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```bash cURL
|
|
261
|
+
# Expand variants with their images
|
|
262
|
+
curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \
|
|
263
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
264
|
+
-d 'expand=variants.images'
|
|
265
|
+
|
|
266
|
+
# Multiple nested expands
|
|
267
|
+
curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \
|
|
268
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
269
|
+
-d 'expand=variants.images,variants.metafields,option_types'
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
### Rules
|
|
274
|
+
|
|
275
|
+
- Maximum depth is **4 levels** (e.g., `a.b.c.d`)
|
|
276
|
+
- A nested expand automatically includes its parent — `expand=variants.images` expands both `variants` and their `images`
|
|
277
|
+
- Nested expand only applies to conditional associations (those controlled by `expand`)
|
|
278
|
+
|
|
279
|
+
## Field Selection
|
|
280
|
+
|
|
281
|
+
Use the `fields` parameter to request only specific fields in the response. This reduces payload size for bandwidth-sensitive clients like mobile apps or server-side rendering.
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
```typescript SDK
|
|
285
|
+
// Only return name, slug, and price
|
|
286
|
+
const products = await client.products.list({
|
|
287
|
+
fields: ['name', 'slug', 'price', 'display_price'],
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
// Works on single resources too
|
|
291
|
+
const product = await client.products.get('spree-tote', {
|
|
292
|
+
fields: ['name', 'slug', 'price'],
|
|
293
|
+
})
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
```bash cURL
|
|
297
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
298
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
299
|
+
-d 'fields=name,slug,price,display_price'
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
"data": [
|
|
306
|
+
{
|
|
307
|
+
"id": "prod_86Rf07xd4z",
|
|
308
|
+
"name": "Spree Tote",
|
|
309
|
+
"slug": "spree-tote",
|
|
310
|
+
"price": "15.99",
|
|
311
|
+
"display_price": "$15.99"
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"meta": { ... }
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Rules
|
|
319
|
+
|
|
320
|
+
- The `id` field is **always included**, even if not listed in `fields`
|
|
321
|
+
- Omitting `fields` returns all fields (default behavior)
|
|
322
|
+
- Field selection applies to the top-level resource only — expanded associations always return their full set of fields
|
|
323
|
+
- Expanded associations are **always included** in the response regardless of `fields` — `?fields=name&expand=variants` returns `id`, `name`, and `variants`
|
|
324
|
+
|
|
325
|
+
> **NOTE:** The SDK TypeScript types remain fully typed regardless of field selection. When using `fields`, be aware that only the requested fields will be present at runtime.
|
|
326
|
+
|
|
327
|
+
## Pagination
|
|
328
|
+
|
|
329
|
+
All collection endpoints return paginated results. Control pagination with `page` and `limit` parameters:
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
```typescript SDK
|
|
333
|
+
const { data: products, meta } = await client.products.list({
|
|
334
|
+
page: 2,
|
|
335
|
+
limit: 10,
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// meta contains pagination info
|
|
339
|
+
console.log(meta)
|
|
340
|
+
// {
|
|
341
|
+
// page: 2,
|
|
342
|
+
// limit: 10,
|
|
343
|
+
// count: 85,
|
|
344
|
+
// pages: 9,
|
|
345
|
+
// from: 11,
|
|
346
|
+
// to: 20,
|
|
347
|
+
// in: 10,
|
|
348
|
+
// previous: 1,
|
|
349
|
+
// next: 3
|
|
350
|
+
// }
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
```bash cURL
|
|
354
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
355
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
356
|
+
-d 'page=2' \
|
|
357
|
+
-d 'limit=10'
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
### Pagination Parameters
|
|
362
|
+
|
|
363
|
+
| Parameter | Default | Max | Description |
|
|
364
|
+
|-----------|---------|-----|-------------|
|
|
365
|
+
| `page` | `1` | - | Page number |
|
|
366
|
+
| `limit` | `25` | `100` | Number of records per page |
|
|
367
|
+
|
|
368
|
+
### Pagination Metadata
|
|
369
|
+
|
|
370
|
+
Collection responses include a `meta` object with pagination info:
|
|
371
|
+
|
|
372
|
+
```json
|
|
373
|
+
{
|
|
374
|
+
"data": [...],
|
|
375
|
+
"meta": {
|
|
376
|
+
"page": 1,
|
|
377
|
+
"limit": 25,
|
|
378
|
+
"count": 85,
|
|
379
|
+
"pages": 4,
|
|
380
|
+
"from": 1,
|
|
381
|
+
"to": 25,
|
|
382
|
+
"in": 25,
|
|
383
|
+
"previous": null,
|
|
384
|
+
"next": 2
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
| Field | Description |
|
|
390
|
+
|-------|-------------|
|
|
391
|
+
| `page` | Current page number |
|
|
392
|
+
| `limit` | Records per page |
|
|
393
|
+
| `count` | Total number of records |
|
|
394
|
+
| `pages` | Total number of pages |
|
|
395
|
+
| `from` | Starting record number on this page |
|
|
396
|
+
| `to` | Ending record number on this page |
|
|
397
|
+
| `in` | Number of records returned on this page |
|
|
398
|
+
| `previous` | Previous page number (null if first page) |
|
|
399
|
+
| `next` | Next page number (null if last page) |
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Rate Limiting"
|
|
3
|
+
sidebarTitle: "Rate Limiting"
|
|
4
|
+
description: "Rate limits and throttling for the Store API"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The Store API enforces rate limits to protect against abuse and ensure fair usage. Rate limits are applied per API key or per IP address depending on the endpoint.
|
|
8
|
+
|
|
9
|
+
## Default Limits
|
|
10
|
+
|
|
11
|
+
| Endpoint | Limit | Scope | Window |
|
|
12
|
+
|----------|-------|-------|--------|
|
|
13
|
+
| All endpoints | 300 requests | Per API key | 1 minute |
|
|
14
|
+
| `POST /auth/login` | 5 requests | Per IP | 1 minute |
|
|
15
|
+
| `POST /customers` | 3 requests | Per IP | 1 minute |
|
|
16
|
+
| `POST /auth/refresh` | 10 requests | Per IP | 1 minute |
|
|
17
|
+
| `POST /auth/oauth/callback` | 5 requests | Per IP | 1 minute |
|
|
18
|
+
|
|
19
|
+
The global rate limit is tracked by your publishable API key (`X-Spree-Api-Key`). If the key is not provided, the limit falls back to the client's IP address.
|
|
20
|
+
|
|
21
|
+
Authentication endpoints have stricter per-IP limits to prevent brute-force attacks.
|
|
22
|
+
|
|
23
|
+
## Rate Limit Headers
|
|
24
|
+
|
|
25
|
+
Every Store API response includes headers that show your current rate limit usage:
|
|
26
|
+
|
|
27
|
+
| Header | Description |
|
|
28
|
+
|--------|-------------|
|
|
29
|
+
| `X-RateLimit-Limit` | Maximum number of requests allowed per window |
|
|
30
|
+
| `X-RateLimit-Remaining` | Number of requests remaining in the current window |
|
|
31
|
+
| `Retry-After` | Seconds to wait before retrying (only present when limit is reached) |
|
|
32
|
+
|
|
33
|
+
Example response headers:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
HTTP/1.1 200 OK
|
|
37
|
+
X-RateLimit-Limit: 300
|
|
38
|
+
X-RateLimit-Remaining: 295
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Rate Limit Response
|
|
42
|
+
|
|
43
|
+
When you exceed the rate limit, the API returns a `429 Too Many Requests` response:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"error": {
|
|
48
|
+
"code": "rate_limit_exceeded",
|
|
49
|
+
"message": "Too many requests. Please retry later."
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The response includes rate limit headers and a `Retry-After` header indicating how many seconds to wait before retrying:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
HTTP/1.1 429 Too Many Requests
|
|
58
|
+
Content-Type: application/json
|
|
59
|
+
X-RateLimit-Limit: 300
|
|
60
|
+
X-RateLimit-Remaining: 0
|
|
61
|
+
Retry-After: 60
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## SDK Retry Handling
|
|
65
|
+
|
|
66
|
+
The Spree SDK automatically handles rate-limited responses with built-in retry logic and exponential backoff:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { createClient } from '@spree/sdk'
|
|
70
|
+
|
|
71
|
+
const client = createClient({
|
|
72
|
+
baseUrl: 'http://localhost:3000',
|
|
73
|
+
publishableKey: 'spree_pk_xxx',
|
|
74
|
+
retry: {
|
|
75
|
+
maxRetries: 2, // Number of retry attempts (default: 2)
|
|
76
|
+
baseDelay: 300, // Initial delay in ms (default: 300)
|
|
77
|
+
maxDelay: 10000, // Maximum delay in ms (default: 10000)
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The SDK respects the `Retry-After` header and only retries on `429` status codes for non-GET requests. For GET/HEAD requests, it also retries on `500`, `502`, `503`, and `504` errors.
|
|
83
|
+
|
|
84
|
+
## Configuring Rate Limits
|
|
85
|
+
|
|
86
|
+
If you're self-hosting Spree, you can adjust rate limits in your initializer:
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
# config/initializers/spree.rb
|
|
90
|
+
Spree::Api::Config[:rate_limit_per_key] = 300 # Global limit per API key
|
|
91
|
+
Spree::Api::Config[:rate_limit_window] = 60 # Window in seconds
|
|
92
|
+
Spree::Api::Config[:rate_limit_login] = 5 # Login attempts per IP
|
|
93
|
+
Spree::Api::Config[:rate_limit_register] = 3 # Registration attempts per IP
|
|
94
|
+
Spree::Api::Config[:rate_limit_refresh] = 10 # Token refresh per IP
|
|
95
|
+
Spree::Api::Config[:rate_limit_oauth] = 5 # OAuth callbacks per IP
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Rate limiting uses `Rails.cache` as the backing store. For production environments with multiple application servers, ensure you're using a shared cache store like Redis:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
# config/environments/production.rb
|
|
102
|
+
config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
|
|
103
|
+
```
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Relations"
|
|
3
|
+
sidebarTitle: "Relations"
|
|
4
|
+
description: "How to include related resources in API responses"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
By default, the Store API returns only the primary resource attributes to keep responses fast and lightweight. Use the `expand` parameter to expand related resources inline.
|
|
8
|
+
|
|
9
|
+
## Including Relations
|
|
10
|
+
|
|
11
|
+
Pass a comma-separated list of relation names via the `expand` query parameter:
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
```typescript SDK
|
|
15
|
+
// Product with variants and images
|
|
16
|
+
const product = await client.products.get('spree-tote', {
|
|
17
|
+
expand: ['variants', 'images'],
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// Access included relations directly
|
|
21
|
+
console.log(product.variants) // Array of variant objects
|
|
22
|
+
console.log(product.images) // Array of image objects
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash cURL
|
|
26
|
+
curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \
|
|
27
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
28
|
+
-d 'expand=variants,images'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Response Format
|
|
33
|
+
|
|
34
|
+
Without `expand`, relation fields are omitted from the response:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"id": "prod_86Rf07xd4z",
|
|
39
|
+
"name": "Spree Tote",
|
|
40
|
+
"slug": "spree-tote",
|
|
41
|
+
"price": { "amount": "15.99", "currency": "USD" },
|
|
42
|
+
"created_at": "2025-01-15T10:30:00Z"
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
With `?expand=variants,images`, the related resources are embedded:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"id": "prod_86Rf07xd4z",
|
|
51
|
+
"name": "Spree Tote",
|
|
52
|
+
"slug": "spree-tote",
|
|
53
|
+
"price": { "amount": "15.99", "currency": "USD" },
|
|
54
|
+
"created_at": "2025-01-15T10:30:00Z",
|
|
55
|
+
"variants": [
|
|
56
|
+
{
|
|
57
|
+
"id": "variant_k5nR8xLq",
|
|
58
|
+
"sku": "SPR-TOTE-RED",
|
|
59
|
+
"price": { "amount": "15.99", "currency": "USD" },
|
|
60
|
+
"in_stock": true,
|
|
61
|
+
"option_values": [{ "name": "Red", "option_type_name": "Color" }]
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"images": [
|
|
65
|
+
{
|
|
66
|
+
"id": "img_9xPq2wLm",
|
|
67
|
+
"url": "https://cdn.example.com/spree-tote.jpg",
|
|
68
|
+
"alt": "Spree Tote"
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Available Relations by Resource
|
|
75
|
+
|
|
76
|
+
### Products
|
|
77
|
+
|
|
78
|
+
| Relation | Description |
|
|
79
|
+
|----------|-------------|
|
|
80
|
+
| `variants` | All purchasable variants |
|
|
81
|
+
| `default_variant` | The default variant |
|
|
82
|
+
| `master_variant` | The master variant |
|
|
83
|
+
| `images` | All product images (across all variants) |
|
|
84
|
+
| `option_types` | Option types (e.g., Size, Color) |
|
|
85
|
+
| `categories` | Categories this product belongs to |
|
|
86
|
+
| `metafields` | Public metafields (custom structured data) |
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
```typescript SDK
|
|
90
|
+
const product = await client.products.get('spree-tote', {
|
|
91
|
+
expand: ['variants', 'images', 'option_types', 'metafields'],
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```bash cURL
|
|
96
|
+
curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \
|
|
97
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
98
|
+
-d 'expand=variants,images,option_types,metafields'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
### Categories
|
|
103
|
+
|
|
104
|
+
| Relation | Description |
|
|
105
|
+
|----------|-------------|
|
|
106
|
+
| `ancestors` | Parent categories (for breadcrumbs) |
|
|
107
|
+
| `children` | Direct child categories |
|
|
108
|
+
| `products` | Products in this category |
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
```typescript SDK
|
|
112
|
+
const category = await client.categories.get('clothing/shirts', {
|
|
113
|
+
expand: ['ancestors', 'children'],
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```bash cURL
|
|
118
|
+
curl -G 'http://localhost:3000/api/v3/store/categories/clothing/shirts' \
|
|
119
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
120
|
+
-d 'expand=ancestors,children'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
### Countries
|
|
125
|
+
|
|
126
|
+
| Relation | Description |
|
|
127
|
+
|----------|-------------|
|
|
128
|
+
| `states` | States/provinces within the country |
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
```typescript SDK
|
|
132
|
+
const usa = await client.countries.get('US', {
|
|
133
|
+
expand: ['states'],
|
|
134
|
+
})
|
|
135
|
+
console.log(usa.states) // Array of state objects
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```bash cURL
|
|
139
|
+
curl -G 'http://localhost:3000/api/v3/store/countries/US' \
|
|
140
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
141
|
+
-d 'expand=states'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
## Collections with Includes
|
|
146
|
+
|
|
147
|
+
The `expand` parameter also works on collection endpoints:
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
```typescript SDK
|
|
151
|
+
// List products with their images and default variant
|
|
152
|
+
const { data: products } = await client.products.list({
|
|
153
|
+
expand: ['images', 'default_variant'],
|
|
154
|
+
limit: 12,
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```bash cURL
|
|
159
|
+
curl -G 'http://localhost:3000/api/v3/store/products' \
|
|
160
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
161
|
+
-d 'expand=images,default_variant' \
|
|
162
|
+
-d 'limit=12'
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
## Nested Includes
|
|
167
|
+
|
|
168
|
+
Use dot notation to include nested relations:
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
```typescript SDK
|
|
172
|
+
// Include variants and their nested option values
|
|
173
|
+
const product = await client.products.get('spree-tote', {
|
|
174
|
+
expand: ['variants.option_values'],
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```bash cURL
|
|
179
|
+
curl -G 'http://localhost:3000/api/v3/store/products/spree-tote' \
|
|
180
|
+
-H 'X-Spree-Api-Key: spree_pk_xxx' \
|
|
181
|
+
-d 'expand=variants.option_values'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
> **NOTE:** Only include relations you actually need. Each included relation requires additional database queries, so requesting unnecessary relations will slow down the response.
|