@spree/docs 0.1.15 → 0.1.17
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/dist/api-reference/webhooks-events.md +1 -1
- package/dist/developer/contributing/developing-spree.md +1 -2
- package/dist/developer/core-concepts/architecture.md +0 -1
- package/dist/developer/core-concepts/webhooks.md +2 -20
- package/dist/developer/customization/metadata.md +0 -2
- package/dist/developer/deployment/emails.md +1 -1
- package/dist/developer/storefront/nextjs/architecture.md +6 -6
- package/dist/developer/storefront/nextjs/customization.md +4 -4
- package/dist/developer/storefront/nextjs/quickstart.md +0 -1
- package/package.json +1 -1
- package/dist/developer/storefront/nextjs/spree-next-package.md +0 -247
|
@@ -348,7 +348,7 @@ Variant event payloads include pricing, stock status, and always-included `optio
|
|
|
348
348
|
"is_master": false,
|
|
349
349
|
"options_text": "Size: M, Color: Black",
|
|
350
350
|
"track_inventory": true,
|
|
351
|
-
"
|
|
351
|
+
"media_count": 2,
|
|
352
352
|
"thumbnail": "https://cdn.example.com/images/tote-bag-black.jpg",
|
|
353
353
|
"purchasable": true,
|
|
354
354
|
"in_stock": true,
|
|
@@ -183,7 +183,6 @@ pnpm dev
|
|
|
183
183
|
| Package | Path | Description |
|
|
184
184
|
|---|---|---|
|
|
185
185
|
| `@spree/sdk` | `packages/sdk` | TypeScript SDK for the Spree Storefront API |
|
|
186
|
-
| `@spree/next` | `packages/next` | Next.js integration (server actions, caching, cookie-based auth) |
|
|
187
186
|
|
|
188
187
|
### Common commands
|
|
189
188
|
|
|
@@ -317,7 +316,7 @@ To help us review your PR quickly:
|
|
|
317
316
|
- **Describe your changes.** Explain what you changed and why. Include screenshots for UI changes.
|
|
318
317
|
- **Add tests.** All new features and bug fixes should include appropriate test coverage.
|
|
319
318
|
- **Update documentation.** If your change affects user-facing behavior, update the relevant docs.
|
|
320
|
-
- **Include a changeset** (TypeScript packages only). Run `pnpm changeset` if your change affects `@spree/sdk
|
|
319
|
+
- **Include a changeset** (TypeScript packages only). Run `pnpm changeset` if your change affects `@spree/sdk`.
|
|
321
320
|
- **Ensure CI passes.** PRs with failing CI will not be reviewed.
|
|
322
321
|
|
|
323
322
|
## Reporting Bugs
|
|
@@ -164,7 +164,6 @@ Spree is distributed as a set of packages:
|
|
|
164
164
|
| Package | Purpose |
|
|
165
165
|
|---------|---------|
|
|
166
166
|
| [`@spree/sdk`](../sdk/quickstart.md) | TypeScript SDK for Store API and Admin API |
|
|
167
|
-
| [`@spree/next`](../storefront/nextjs/spree-next-package.md) | Next.js integration — server actions, caching, cookie-based auth |
|
|
168
167
|
|
|
169
168
|
> **INFO:** For headless commerce, you only need the `spree` package. Build your customer-facing frontend with any technology (Next.js, Nuxt, mobile apps) using the Store API and SDK.
|
|
170
169
|
|
|
@@ -165,27 +165,9 @@ Each webhook request includes these headers:
|
|
|
165
165
|
|
|
166
166
|
To ensure webhooks are genuinely from your Spree store, verify the signature.
|
|
167
167
|
|
|
168
|
-
#### Next.js
|
|
168
|
+
#### Next.js
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
// src/app/api/webhooks/spree/route.ts
|
|
174
|
-
import { createWebhookHandler } from '@spree/next/webhooks'
|
|
175
|
-
|
|
176
|
-
export const POST = createWebhookHandler({
|
|
177
|
-
secret: process.env.SPREE_WEBHOOK_SECRET!,
|
|
178
|
-
handlers: {
|
|
179
|
-
'order.completed': async (event) => {
|
|
180
|
-
// event.data is the order payload (same shape as Store API)
|
|
181
|
-
await sendOrderConfirmationEmail(event.data)
|
|
182
|
-
},
|
|
183
|
-
'order.canceled': async (event) => {
|
|
184
|
-
await sendCancellationEmail(event.data)
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
})
|
|
188
|
-
```
|
|
170
|
+
The [Spree Storefront](https://github.com/spree/storefront) includes a ready-made webhook route handler with signature verification and event routing. See the [storefront email docs](../storefront/nextjs/customization.md#transactional-emails) for details.
|
|
189
171
|
|
|
190
172
|
#### Any JavaScript/TypeScript framework
|
|
191
173
|
|
|
@@ -35,7 +35,7 @@ RESEND_API_KEY=re_your_resend_api_key
|
|
|
35
35
|
EMAIL_FROM=Your Store <orders@your-domain.com>
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
3. **The storefront handles everything else** — signature verification, event routing, email rendering, and delivery are built
|
|
38
|
+
3. **The storefront handles everything else** — signature verification, event routing, email rendering, and delivery are built in. See the [Next.js storefront email docs](../storefront/nextjs/customization.md#transactional-emails) for template customization.
|
|
39
39
|
|
|
40
40
|
### Supported Events
|
|
41
41
|
|
|
@@ -9,10 +9,10 @@ The storefront follows a **server-first architecture** where all API calls are m
|
|
|
9
9
|
|
|
10
10
|
```
|
|
11
11
|
Browser → Server Action → @spree/sdk → Spree API
|
|
12
|
-
(with httpOnly cookies via
|
|
12
|
+
(with httpOnly cookies via src/lib/spree helpers)
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
- **Server Actions** (`src/lib/data/`) — call `@spree/sdk` directly with auth/cookie helpers from
|
|
15
|
+
- **Server Actions** (`src/lib/data/`) — call `@spree/sdk` directly with auth/cookie helpers from `src/lib/spree`
|
|
16
16
|
- **httpOnly Cookies** — auth tokens, cart tokens, and locale are stored securely
|
|
17
17
|
- **No Client-Side API Calls** — the Spree API key stays on the server
|
|
18
18
|
- **Auto-Localization** — locale and country are read from cookies via `getLocaleOptions()`
|
|
@@ -85,13 +85,13 @@ src/
|
|
|
85
85
|
|
|
86
86
|
1. User submits login form
|
|
87
87
|
2. Server action calls `@spree/sdk` to authenticate
|
|
88
|
-
3. JWT token is stored in an httpOnly cookie via
|
|
88
|
+
3. JWT token is stored in an httpOnly cookie via `src/lib/spree` cookie helpers
|
|
89
89
|
4. Subsequent requests use `withAuthRefresh()` which reads the token from cookies automatically
|
|
90
90
|
5. Token is never accessible to client-side JavaScript
|
|
91
91
|
|
|
92
92
|
```typescript
|
|
93
93
|
// src/lib/data/customer.ts
|
|
94
|
-
import { getClient, withAuthRefresh, setAccessToken, setRefreshToken } from '
|
|
94
|
+
import { getClient, withAuthRefresh, setAccessToken, setRefreshToken } from '@/lib/spree'
|
|
95
95
|
|
|
96
96
|
export async function login(email: string, password: string) {
|
|
97
97
|
const result = await getClient().auth.login({ email, password })
|
|
@@ -117,11 +117,11 @@ The storefront supports multiple countries and currencies via URL segments:
|
|
|
117
117
|
/uk/en/products # UK store, English
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
A middleware (`src/proxy.ts`) uses `createSpreeMiddleware` from
|
|
120
|
+
A middleware (`src/proxy.ts`) uses `createSpreeMiddleware` from `src/lib/spree` to detect the visitor's country and locale, then redirects to the correct URL prefix. The `CountrySwitcher` component lets users change regions manually.
|
|
121
121
|
|
|
122
122
|
## Server Actions
|
|
123
123
|
|
|
124
|
-
All data fetching is done through server actions in `src/lib/data/`. These call `@spree/sdk` directly, using
|
|
124
|
+
All data fetching is done through server actions in `src/lib/data/`. These call `@spree/sdk` directly, using `src/lib/spree` helpers for auth and locale:
|
|
125
125
|
|
|
126
126
|
```typescript
|
|
127
127
|
// Products — use getLocaleOptions() for locale-aware reads
|
|
@@ -111,7 +111,7 @@ To customize API behavior, modify the server actions in `src/lib/data/`. Each fi
|
|
|
111
111
|
| `gift-cards.ts` | Gift card management |
|
|
112
112
|
| `utils.ts` | Shared helpers (error handling, fallbacks) |
|
|
113
113
|
|
|
114
|
-
These server actions call `@spree/sdk` directly, using
|
|
114
|
+
These server actions call `@spree/sdk` directly, using helpers in `src/lib/spree/` for auth cookies and locale resolution. You can add custom logic, caching strategies, or additional transformations as needed.
|
|
115
115
|
|
|
116
116
|
## Adding New Pages
|
|
117
117
|
|
|
@@ -163,10 +163,10 @@ Opens the react-email dev server with mock data for all templates at `http://loc
|
|
|
163
163
|
|
|
164
164
|
### Webhook Handler
|
|
165
165
|
|
|
166
|
-
The webhook route (`src/app/api/webhooks/spree/route.ts`) uses `createWebhookHandler` from
|
|
166
|
+
The webhook route (`src/app/api/webhooks/spree/route.ts`) uses `createWebhookHandler` from `src/lib/spree/webhooks`:
|
|
167
167
|
|
|
168
168
|
```typescript
|
|
169
|
-
import { createWebhookHandler } from '
|
|
169
|
+
import { createWebhookHandler } from '@/lib/spree/webhooks'
|
|
170
170
|
|
|
171
171
|
export const POST = createWebhookHandler({
|
|
172
172
|
secret: process.env.SPREE_WEBHOOK_SECRET!,
|
|
@@ -198,4 +198,4 @@ For full setup details, see [Sending out Emails](../../deployment/emails.md).
|
|
|
198
198
|
|
|
199
199
|
## Building a Custom Storefront
|
|
200
200
|
|
|
201
|
-
If you prefer to build from scratch instead of forking the starter, you can use the `@spree/
|
|
201
|
+
If you prefer to build from scratch instead of forking the starter, you can use the `@spree/sdk` package directly in any Next.js application. The storefront's `src/lib/spree/` directory contains reusable helpers for cookie-based auth, locale resolution, middleware, and webhook verification that you can copy into your own project.
|
|
@@ -12,7 +12,6 @@ The [Spree Storefront](https://github.com/spree/storefront) is a production-read
|
|
|
12
12
|
- **Tailwind CSS 4** - Utility-first styling
|
|
13
13
|
- **TypeScript 5** - Full type safety
|
|
14
14
|
- **[@spree/sdk](https://github.com/spree/spree/tree/main/packages/sdk)** - Official Spree Commerce SDK
|
|
15
|
-
- **[@spree/next](https://github.com/spree/spree/tree/main/packages/next)** - Cookie-based auth, middleware, and webhook helpers
|
|
16
15
|
- **Sentry** - Error tracking and performance monitoring
|
|
17
16
|
|
|
18
17
|
## Features
|
package/package.json
CHANGED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "@spree/next Package"
|
|
3
|
-
description: Next.js integration library — cookie-based auth, middleware, and webhook helpers
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
The `@spree/next` package provides the server-side plumbing for building a Next.js storefront with `@spree/sdk`: JWT token lifecycle, httpOnly cookie management, locale/country middleware, and webhook verification.
|
|
7
|
-
|
|
8
|
-
Your storefront calls `@spree/sdk` directly for all API operations. `@spree/next` handles the Next.js-specific concerns — cookies, auth refresh, and middleware — so you don't have to.
|
|
9
|
-
|
|
10
|
-
## Installation
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
npm install @spree/next @spree/sdk
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Configuration
|
|
17
|
-
|
|
18
|
-
### Auto-initialization
|
|
19
|
-
|
|
20
|
-
Set environment variables and the client initializes automatically:
|
|
21
|
-
|
|
22
|
-
```env
|
|
23
|
-
SPREE_API_URL=https://api.mystore.com
|
|
24
|
-
SPREE_PUBLISHABLE_KEY=spree_pk_xxx
|
|
25
|
-
|
|
26
|
-
# Required for webhook-based emails (see Webhooks section below)
|
|
27
|
-
SPREE_WEBHOOK_SECRET=your_webhook_endpoint_secret_key
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Explicit initialization
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
// lib/storefront.ts
|
|
34
|
-
import { initSpreeNext } from '@spree/next';
|
|
35
|
-
|
|
36
|
-
initSpreeNext({
|
|
37
|
-
baseUrl: process.env.SPREE_API_URL!,
|
|
38
|
-
publishableKey: process.env.SPREE_PUBLISHABLE_KEY!,
|
|
39
|
-
cartCookieName: '_spree_cart_token', // default
|
|
40
|
-
accessTokenCookieName: '_spree_jwt', // default
|
|
41
|
-
defaultLocale: 'en',
|
|
42
|
-
defaultCurrency: 'USD',
|
|
43
|
-
defaultCountry: 'US',
|
|
44
|
-
});
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Architecture
|
|
48
|
-
|
|
49
|
-
`@spree/next` provides four things:
|
|
50
|
-
|
|
51
|
-
1. **`getClient()`** — singleton `@spree/sdk` Client instance (auto-inits from env vars)
|
|
52
|
-
2. **Auth helpers** — `withAuthRefresh()` for JWT lifecycle, cookie read/write for tokens
|
|
53
|
-
3. **Locale resolution** — `getLocaleOptions()` reads country/locale from cookies
|
|
54
|
-
4. **Framework helpers** — `createSpreeMiddleware()` for URL routing, `createWebhookHandler()` for webhooks
|
|
55
|
-
|
|
56
|
-
Your storefront's server actions call `@spree/sdk` directly, using these helpers for auth and cookies:
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
Server Action → getClient().products.list(params, localeOptions)
|
|
60
|
-
Server Action → withAuthRefresh(fn) → getClient().customer.get(options)
|
|
61
|
-
Server Action → getCartOptions() → getClient().carts.items.create(...)
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Auth Helpers
|
|
65
|
-
|
|
66
|
-
### `withAuthRefresh(fn)`
|
|
67
|
-
|
|
68
|
-
Wraps an authenticated SDK call with automatic token management:
|
|
69
|
-
|
|
70
|
-
1. Reads JWT from cookie
|
|
71
|
-
2. Proactively refreshes if expiring within 5 minutes
|
|
72
|
-
3. Executes your function with `{ token }` options
|
|
73
|
-
4. On 401: refreshes token and retries once
|
|
74
|
-
5. On refresh failure: clears cookies and throws
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
'use server';
|
|
78
|
-
|
|
79
|
-
import { getClient, withAuthRefresh } from '@spree/next';
|
|
80
|
-
|
|
81
|
-
export async function getCustomer() {
|
|
82
|
-
return withAuthRefresh(async (options) => {
|
|
83
|
-
return getClient().customer.get(options);
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function listAddresses() {
|
|
88
|
-
return withAuthRefresh(async (options) => {
|
|
89
|
-
return getClient().customer.addresses.list(undefined, options);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### `getAuthOptions()`
|
|
95
|
-
|
|
96
|
-
Lower-level helper that returns `{ token }` or `{}` with proactive refresh. Use when you need the token but want to handle errors yourself.
|
|
97
|
-
|
|
98
|
-
## Cookie Management
|
|
99
|
-
|
|
100
|
-
All cookie helpers are async (they use Next.js `cookies()` API):
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
import {
|
|
104
|
-
// Cart
|
|
105
|
-
getCartToken, getCartId, setCartCookies, clearCartCookies,
|
|
106
|
-
getCartOptions, // { spreeToken, token } for cart SDK calls
|
|
107
|
-
requireCartId, // throws if no cart
|
|
108
|
-
|
|
109
|
-
// Auth
|
|
110
|
-
getAccessToken, setAccessToken, clearAccessToken,
|
|
111
|
-
getRefreshToken, setRefreshToken, clearRefreshToken,
|
|
112
|
-
} from '@spree/next';
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Cart operations pattern
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
'use server';
|
|
119
|
-
|
|
120
|
-
import { getClient, getCartOptions, requireCartId } from '@spree/next';
|
|
121
|
-
|
|
122
|
-
export async function updateCartItem(lineItemId: string, quantity: number) {
|
|
123
|
-
const options = await getCartOptions();
|
|
124
|
-
const cartId = await requireCartId();
|
|
125
|
-
return getClient().carts.items.update(cartId, lineItemId, { quantity }, options);
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Auth operations pattern
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
'use server';
|
|
133
|
-
|
|
134
|
-
import { getClient, setAccessToken, setRefreshToken, getCartToken, getCartId } from '@spree/next';
|
|
135
|
-
|
|
136
|
-
export async function login(email: string, password: string) {
|
|
137
|
-
const result = await getClient().auth.login({ email, password });
|
|
138
|
-
await setAccessToken(result.token);
|
|
139
|
-
await setRefreshToken(result.refresh_token);
|
|
140
|
-
|
|
141
|
-
// Associate guest cart if one exists
|
|
142
|
-
const cartToken = await getCartToken();
|
|
143
|
-
const cartId = await getCartId();
|
|
144
|
-
if (cartToken && cartId) {
|
|
145
|
-
await getClient().carts.associate(cartId, {
|
|
146
|
-
token: result.token,
|
|
147
|
-
spreeToken: cartToken,
|
|
148
|
-
}).catch(() => {}); // non-fatal
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return { success: true, user: result.user };
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
## Locale Resolution
|
|
156
|
-
|
|
157
|
-
`getLocaleOptions()` reads country and locale from cookies (set by middleware or explicitly). All data-fetching server actions should use this:
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
'use server';
|
|
161
|
-
|
|
162
|
-
import { getClient, getLocaleOptions } from '@spree/next';
|
|
163
|
-
|
|
164
|
-
export async function getProducts(params?: { limit?: number }) {
|
|
165
|
-
const options = await getLocaleOptions();
|
|
166
|
-
return getClient().products.list(params, options);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export async function getCategory(idOrPermalink: string) {
|
|
170
|
-
const options = await getLocaleOptions();
|
|
171
|
-
return getClient().categories.get(idOrPermalink, undefined, options);
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## Middleware
|
|
176
|
-
|
|
177
|
-
Handles URL-based country/locale routing. Detects country from cookies, geo headers (Vercel/Cloudflare), or defaults, then redirects to `/{country}/{locale}/...`:
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
// middleware.ts
|
|
181
|
-
import { createSpreeMiddleware } from '@spree/next/middleware';
|
|
182
|
-
|
|
183
|
-
export default createSpreeMiddleware({
|
|
184
|
-
defaultCountry: 'us',
|
|
185
|
-
defaultLocale: 'en',
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
export const config = {
|
|
189
|
-
matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\..*$).*)'],
|
|
190
|
-
};
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## Webhooks
|
|
194
|
-
|
|
195
|
-
Handle Spree webhook events in your Next.js app with `@spree/next/webhooks`:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
// src/app/api/webhooks/spree/route.ts
|
|
199
|
-
import { createWebhookHandler } from '@spree/next/webhooks'
|
|
200
|
-
|
|
201
|
-
export const POST = createWebhookHandler({
|
|
202
|
-
secret: process.env.SPREE_WEBHOOK_SECRET!,
|
|
203
|
-
handlers: {
|
|
204
|
-
'order.completed': async (event) => {
|
|
205
|
-
await sendOrderConfirmationEmail(event.data)
|
|
206
|
-
},
|
|
207
|
-
'order.canceled': async (event) => {
|
|
208
|
-
await sendCancellationEmail(event.data)
|
|
209
|
-
},
|
|
210
|
-
'order.shipped': async (event) => {
|
|
211
|
-
await sendShippingNotification(event.data)
|
|
212
|
-
},
|
|
213
|
-
'customer.password_reset_requested': async (event) => {
|
|
214
|
-
await sendPasswordResetEmail(event.data)
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
})
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
The handler verifies HMAC-SHA256 signatures, rejects replayed requests, routes events to your handlers, and returns 200 immediately (handlers run async).
|
|
221
|
-
|
|
222
|
-
### Local development
|
|
223
|
-
|
|
224
|
-
Webhooks require a publicly accessible URL. Use [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/) for local development:
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
brew install cloudflared
|
|
228
|
-
cloudflared tunnel --url http://localhost:3001
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
Then create a webhook endpoint in **Spree Admin → Settings → Developers → Webhooks** pointing to your tunnel URL + `/api/webhooks/spree`.
|
|
232
|
-
|
|
233
|
-
For more details, see [Webhooks](../../core-concepts/webhooks.md).
|
|
234
|
-
|
|
235
|
-
## TypeScript
|
|
236
|
-
|
|
237
|
-
Import types directly from `@spree/sdk`:
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
import type {
|
|
241
|
-
Product, Order, Cart, LineItem, Variant,
|
|
242
|
-
Category, Country, Currency, Address, Customer,
|
|
243
|
-
CreditCard, GiftCard, Fulfillment, DeliveryRate,
|
|
244
|
-
Payment, PaymentSession, PaginatedResponse,
|
|
245
|
-
AddressParams, SpreeError,
|
|
246
|
-
} from '@spree/sdk';
|
|
247
|
-
```
|