@commercengine/storefront-sdk-nextjs 0.1.0-alpha.1 → 1.0.0-alpha.2
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 +312 -438
- package/dist/client.cjs +68 -93
- package/dist/client.d.cts +40 -57
- package/dist/client.d.ts +40 -57
- package/dist/client.js +68 -88
- package/dist/index.cjs +107 -92
- package/dist/index.d.cts +29 -95
- package/dist/index.d.ts +29 -95
- package/dist/index.js +106 -83
- package/dist/server-ByBPoXJG.d.cts +182 -0
- package/dist/server-ByBPoXJG.d.ts +182 -0
- package/dist/server-CwxgXezP.d.cts +115 -0
- package/dist/server-CwxgXezP.d.ts +115 -0
- package/dist/server.cjs +57 -88
- package/dist/server.d.cts +1 -2
- package/dist/server.d.ts +1 -2
- package/dist/server.js +57 -84
- package/dist/storefront.cjs +93 -21
- package/dist/storefront.d.cts +2 -40
- package/dist/storefront.d.ts +2 -40
- package/dist/storefront.js +93 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
# CommerceEngine Next.js SDK
|
|
2
2
|
|
|
3
|
-
**Production-ready Next.js wrapper** for the CommerceEngine Storefront SDK.
|
|
3
|
+
**Production-ready Next.js wrapper** for the CommerceEngine Storefront SDK. Provides the perfect developer experience with automatic context detection, universal API, and zero configuration complexity.
|
|
4
4
|
|
|
5
|
-
**✨
|
|
6
|
-
- **🎯
|
|
5
|
+
**✨ Perfect DX Pattern:**
|
|
6
|
+
- **🎯 One Config File** - Single `lib/storefront.ts` using `createStorefront()`
|
|
7
|
+
- **🌍 Universal API** - Same `storefront()` import works everywhere
|
|
7
8
|
- **🔥 Automatic Tokens** - Creates and manages tokens automatically
|
|
8
|
-
- **🧠 Smart
|
|
9
|
+
- **🧠 Smart Context Detection** - Detects Server vs Client vs Build contexts
|
|
9
10
|
- **🍪 Cookie-based State** - Shared authentication via Next.js cookies
|
|
10
11
|
- **⚡ Request Isolation** - Proper per-request SDK instances on server
|
|
11
|
-
- **🛠 Zero
|
|
12
|
+
- **🛠 Zero Complexity** - Environment variables + one lib file = done
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
@@ -20,35 +21,79 @@ pnpm add @commercengine/storefront-sdk-nextjs
|
|
|
20
21
|
|
|
21
22
|
## Quick Start
|
|
22
23
|
|
|
23
|
-
### 1. Environment Variables
|
|
24
|
+
### 1. Environment Variables (Required)
|
|
24
25
|
|
|
25
26
|
Add your store configuration to `.env.local`:
|
|
26
27
|
|
|
27
28
|
```bash
|
|
28
29
|
NEXT_PUBLIC_STORE_ID=your-store-id
|
|
29
|
-
NEXT_PUBLIC_ENVIRONMENT=staging # or "production"
|
|
30
30
|
NEXT_PUBLIC_API_KEY=your-api-key
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
**Optional environment variables:**
|
|
34
|
+
```bash
|
|
35
|
+
# Environment (defaults to "staging")
|
|
36
|
+
NEXT_PUBLIC_ENVIRONMENT=staging # or "production"
|
|
37
|
+
|
|
38
|
+
# Custom API endpoint (overrides environment default)
|
|
39
|
+
NEXT_PUBLIC_API_BASE_URL=https://your-custom-api.example.com
|
|
40
|
+
|
|
41
|
+
# Request timeout in milliseconds
|
|
42
|
+
NEXT_PUBLIC_API_TIMEOUT=10000
|
|
43
|
+
|
|
44
|
+
# Debug mode (defaults to false)
|
|
45
|
+
NEXT_PUBLIC_DEBUG_MODE=true
|
|
46
|
+
|
|
47
|
+
# Default customer group for pricing and promotions
|
|
48
|
+
NEXT_PUBLIC_DEFAULT_CUSTOMER_GROUP_ID=01JHS28V83KDWTRBXXJQRTEKA0
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Create Your Storefront Configuration
|
|
52
|
+
|
|
53
|
+
Create `lib/storefront.ts` in your project:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// lib/storefront.ts
|
|
57
|
+
import {
|
|
58
|
+
createStorefront,
|
|
59
|
+
type StorefrontRuntimeConfig,
|
|
60
|
+
} from "@commercengine/storefront-sdk-nextjs";
|
|
61
|
+
|
|
62
|
+
// Optional advanced configuration (everything not in environment variables)
|
|
63
|
+
const storefrontConfig: StorefrontRuntimeConfig = {
|
|
64
|
+
debug: true,
|
|
65
|
+
timeout: 15000,
|
|
66
|
+
logger: (msg: string, ...args: any[]) =>
|
|
67
|
+
console.log("[STOREFRONT]", msg, ...args),
|
|
68
|
+
onTokensUpdated: (access: string, refresh: string) => {
|
|
69
|
+
console.log("🔥 TOKENS UPDATED:", {
|
|
70
|
+
access: access.slice(0, 20) + "...",
|
|
71
|
+
refresh: refresh.slice(0, 20) + "...",
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
onTokensCleared: () => {
|
|
75
|
+
console.log("🔄 TOKENS CLEARED");
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Create the configured storefront function
|
|
80
|
+
export const storefront = createStorefront(storefrontConfig);
|
|
81
|
+
|
|
82
|
+
// Re-export types for convenience
|
|
83
|
+
export type { StorefrontRuntimeConfig };
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 3. Initialize in Root Layout
|
|
34
87
|
|
|
35
88
|
```typescript
|
|
36
89
|
// app/layout.tsx
|
|
37
|
-
import {
|
|
90
|
+
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
|
|
38
91
|
|
|
39
92
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
40
93
|
return (
|
|
41
94
|
<html lang="en">
|
|
42
95
|
<body>
|
|
43
|
-
<StorefrontSDKInitializer
|
|
44
|
-
config={{
|
|
45
|
-
storeId: process.env.NEXT_PUBLIC_STORE_ID || "",
|
|
46
|
-
environment: process.env.NEXT_PUBLIC_ENVIRONMENT === "production"
|
|
47
|
-
? Environment.Production
|
|
48
|
-
: Environment.Staging,
|
|
49
|
-
apiKey: process.env.NEXT_PUBLIC_API_KEY,
|
|
50
|
-
}}
|
|
51
|
-
/>
|
|
96
|
+
<StorefrontSDKInitializer />
|
|
52
97
|
{children}
|
|
53
98
|
</body>
|
|
54
99
|
</html>
|
|
@@ -56,57 +101,39 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
56
101
|
}
|
|
57
102
|
```
|
|
58
103
|
|
|
59
|
-
###
|
|
104
|
+
### 4. Use Anywhere with Universal Import
|
|
60
105
|
|
|
61
106
|
```typescript
|
|
62
|
-
|
|
63
|
-
import {
|
|
107
|
+
// Import your configured storefront everywhere
|
|
108
|
+
import { storefront } from "@/lib/storefront";
|
|
109
|
+
import { cookies } from "next/headers"; // Only for server contexts
|
|
64
110
|
|
|
65
|
-
// Client Component - No cookies needed
|
|
111
|
+
// ✅ Client Component - No cookies needed
|
|
66
112
|
const products = await storefront().catalog.listProducts();
|
|
67
113
|
|
|
68
|
-
// Server Component, Server Action, or API Route
|
|
114
|
+
// ✅ Server Component, Server Action, or API Route - MUST pass cookies
|
|
69
115
|
const products = await storefront(cookies()).catalog.listProducts();
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Usage in Different Next.js Contexts
|
|
73
|
-
|
|
74
|
-
### Server Components
|
|
75
116
|
|
|
76
|
-
|
|
117
|
+
// ✅ Root Layout - Special exception with explicit flag
|
|
118
|
+
const products = await storefront({ isRootLayout: true }).catalog.listProducts();
|
|
77
119
|
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
import { cookies } from "next/headers";
|
|
82
|
-
|
|
83
|
-
export default async function ProductsPage() {
|
|
84
|
-
const sdk = storefront(cookies());
|
|
85
|
-
|
|
86
|
-
// This will generate a new anonymous token for this request
|
|
87
|
-
// Note: Token cannot be persisted (cookies are read-only in Server Components)
|
|
88
|
-
const { data: products } = await sdk.catalog.listProducts();
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<div>
|
|
92
|
-
{products?.products.map(product => (
|
|
93
|
-
<div key={product.id}>{product.name}</div>
|
|
94
|
-
))}
|
|
95
|
-
</div>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
120
|
+
// ✅ SSG/ISR (build contexts) - Automatic fallback to memory storage
|
|
121
|
+
// (NEXT_BUILD_CACHE_TOKENS=true enables this)
|
|
122
|
+
const products = await storefront().catalog.listProducts();
|
|
98
123
|
```
|
|
99
124
|
|
|
125
|
+
## Usage in Different Next.js Contexts
|
|
126
|
+
|
|
100
127
|
### Client Components
|
|
101
128
|
|
|
102
|
-
Client Components run in the browser and can
|
|
129
|
+
Client Components run in the browser and can persist tokens via cookies:
|
|
103
130
|
|
|
104
131
|
```typescript
|
|
105
132
|
// components/ProductList.tsx
|
|
106
133
|
"use client";
|
|
107
134
|
|
|
108
135
|
import { useState, useEffect } from "react";
|
|
109
|
-
import { storefront } from "
|
|
136
|
+
import { storefront } from "@/lib/storefront";
|
|
110
137
|
|
|
111
138
|
export default function ProductList() {
|
|
112
139
|
const [products, setProducts] = useState([]);
|
|
@@ -115,7 +142,7 @@ export default function ProductList() {
|
|
|
115
142
|
async function loadProducts() {
|
|
116
143
|
const sdk = storefront(); // No cookies() needed on client-side
|
|
117
144
|
|
|
118
|
-
//
|
|
145
|
+
// Tokens are automatically managed by StorefrontSDKInitializer
|
|
119
146
|
const { data } = await sdk.catalog.listProducts();
|
|
120
147
|
if (data) setProducts(data.products);
|
|
121
148
|
}
|
|
@@ -133,51 +160,62 @@ export default function ProductList() {
|
|
|
133
160
|
}
|
|
134
161
|
```
|
|
135
162
|
|
|
163
|
+
### Server Components
|
|
164
|
+
|
|
165
|
+
Server Components run on the server and can read cookies:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// app/products/page.tsx
|
|
169
|
+
import { storefront } from "@/lib/storefront";
|
|
170
|
+
import { cookies } from "next/headers";
|
|
171
|
+
|
|
172
|
+
export default async function ProductsPage() {
|
|
173
|
+
const sdk = storefront(cookies());
|
|
174
|
+
|
|
175
|
+
const { data: products } = await sdk.catalog.listProducts();
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<div>
|
|
179
|
+
{products?.products.map(product => (
|
|
180
|
+
<div key={product.id}>{product.name}</div>
|
|
181
|
+
))}
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
136
187
|
### Server Actions
|
|
137
188
|
|
|
138
|
-
Server Actions
|
|
189
|
+
Server Actions can both read and write cookies, perfect for authentication:
|
|
139
190
|
|
|
140
191
|
```typescript
|
|
141
192
|
// app/actions.ts
|
|
142
193
|
"use server";
|
|
143
194
|
|
|
144
|
-
import { storefront } from "
|
|
195
|
+
import { storefront } from "@/lib/storefront";
|
|
145
196
|
import { cookies } from "next/headers";
|
|
146
197
|
|
|
147
198
|
export async function loginWithEmail(email: string, password: string) {
|
|
148
199
|
const sdk = storefront(cookies());
|
|
149
200
|
|
|
150
|
-
// This will create and persist tokens via cookies
|
|
151
201
|
const { data, error } = await sdk.auth.loginWithPassword({ email, password });
|
|
152
202
|
|
|
153
203
|
if (data) {
|
|
154
|
-
// Tokens are automatically saved to cookies
|
|
204
|
+
// Tokens are automatically saved to cookies
|
|
155
205
|
return { success: true, user: data.user };
|
|
156
206
|
}
|
|
157
207
|
|
|
158
208
|
return { success: false, error: error?.message };
|
|
159
209
|
}
|
|
160
|
-
|
|
161
|
-
export async function addToCart(productId: string, quantity: number) {
|
|
162
|
-
const sdk = storefront(cookies());
|
|
163
|
-
|
|
164
|
-
// Uses existing authentication from cookies
|
|
165
|
-
const { data } = await sdk.cart.addItem({
|
|
166
|
-
product_id: productId,
|
|
167
|
-
quantity
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
return data;
|
|
171
|
-
}
|
|
172
210
|
```
|
|
173
211
|
|
|
174
212
|
### API Routes
|
|
175
213
|
|
|
176
|
-
API Routes work identically to Server Actions
|
|
214
|
+
API Routes work identically to Server Actions:
|
|
177
215
|
|
|
178
216
|
```typescript
|
|
179
217
|
// app/api/products/route.ts
|
|
180
|
-
import { storefront } from "
|
|
218
|
+
import { storefront } from "@/lib/storefront";
|
|
181
219
|
import { cookies } from "next/headers";
|
|
182
220
|
import { NextResponse } from "next/server";
|
|
183
221
|
|
|
@@ -198,16 +236,110 @@ export async function GET() {
|
|
|
198
236
|
);
|
|
199
237
|
}
|
|
200
238
|
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Root Layout (Special Case)
|
|
242
|
+
|
|
243
|
+
Root Layout requires explicit flag since it's outside request context:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// app/layout.tsx
|
|
247
|
+
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
|
|
248
|
+
import { storefront } from "@/lib/storefront";
|
|
249
|
+
|
|
250
|
+
// Root Layout requires explicit flag - no request context available
|
|
251
|
+
const sdk = storefront({ isRootLayout: true });
|
|
252
|
+
const { data: storeConfig } = await sdk.store.getStoreConfig();
|
|
253
|
+
|
|
254
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
255
|
+
return (
|
|
256
|
+
<html lang="en">
|
|
257
|
+
<body>
|
|
258
|
+
<StorefrontSDKInitializer />
|
|
259
|
+
<h1>Welcome to {storeConfig?.store_config?.brand.name}</h1>
|
|
260
|
+
{children}
|
|
261
|
+
</body>
|
|
262
|
+
</html>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Static Site Generation (SSG) & Build-Time Optimization
|
|
268
|
+
|
|
269
|
+
The SDK provides powerful build-time optimizations through intelligent token caching.
|
|
270
|
+
|
|
271
|
+
### Enable Build-Time Token Caching
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# Enable caching during builds
|
|
275
|
+
NEXT_BUILD_CACHE_TOKENS=true pnpm build
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### SSG with generateStaticParams
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// app/products/[slug]/page.tsx
|
|
282
|
+
import { storefront } from "@/lib/storefront";
|
|
283
|
+
import { notFound } from "next/navigation";
|
|
284
|
+
|
|
285
|
+
interface ProductPageProps {
|
|
286
|
+
params: Promise<{ slug: string }>;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export default async function ProductPage({ params }: ProductPageProps) {
|
|
290
|
+
const { slug } = await params;
|
|
291
|
+
const sdk = storefront(); // No cookies() - uses build-time storage
|
|
292
|
+
|
|
293
|
+
const { data, error } = await sdk.catalog.getProductDetail({
|
|
294
|
+
product_id_or_slug: slug
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
if (error || !data) {
|
|
298
|
+
notFound();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<div>
|
|
303
|
+
<h1>{data.product.name}</h1>
|
|
304
|
+
<p>SKU: {data.product.sku}</p>
|
|
305
|
+
<p>Description: {data.product.short_description}</p>
|
|
306
|
+
</div>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
201
309
|
|
|
310
|
+
// Generate static params from real API data
|
|
311
|
+
export async function generateStaticParams() {
|
|
312
|
+
const sdk = storefront(); // Token will be cached and reused
|
|
313
|
+
|
|
314
|
+
const { data: productsData, error } = await sdk.catalog.listProducts({
|
|
315
|
+
limit: 100
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
if (error || !productsData) {
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return productsData.products.map(product => ({
|
|
323
|
+
slug: product.slug || product.id
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Build Performance Benefits
|
|
329
|
+
|
|
330
|
+
With token caching enabled:
|
|
331
|
+
- ✅ Token created once and reused across all pages
|
|
332
|
+
- ✅ 100 pages = ~1 anonymous token API call total
|
|
333
|
+
- ✅ Faster builds, dramatically lower API usage
|
|
202
334
|
|
|
203
335
|
## Authentication Patterns
|
|
204
336
|
|
|
205
337
|
### Anonymous Users
|
|
206
338
|
|
|
207
|
-
The SDK automatically creates anonymous tokens
|
|
339
|
+
The SDK automatically creates anonymous tokens:
|
|
208
340
|
|
|
209
341
|
```typescript
|
|
210
|
-
// This works
|
|
342
|
+
// This works everywhere - creates anonymous token automatically
|
|
211
343
|
const { data: products } = await storefront(cookies()).catalog.listProducts();
|
|
212
344
|
```
|
|
213
345
|
|
|
@@ -235,7 +367,7 @@ export async function loginUser(email: string, password: string) {
|
|
|
235
367
|
```typescript
|
|
236
368
|
// Server Action - Step 1: Send OTP
|
|
237
369
|
export async function sendOTP(phone: string, country_code: string) {
|
|
238
|
-
const sdk = storefront(cookies());
|
|
370
|
+
const sdk = storefront(await cookies());
|
|
239
371
|
|
|
240
372
|
return await sdk.auth.loginWithPhone({
|
|
241
373
|
phone,
|
|
@@ -246,7 +378,7 @@ export async function sendOTP(phone: string, country_code: string) {
|
|
|
246
378
|
|
|
247
379
|
// Server Action - Step 2: Verify OTP
|
|
248
380
|
export async function verifyOTP(otp: string, otp_token: string, otp_action: string) {
|
|
249
|
-
const sdk = storefront(cookies());
|
|
381
|
+
const sdk = storefront(await cookies());
|
|
250
382
|
|
|
251
383
|
const { data, error } = await sdk.auth.verifyOtp({
|
|
252
384
|
otp,
|
|
@@ -263,354 +395,165 @@ export async function verifyOTP(otp: string, otp_token: string, otp_action: stri
|
|
|
263
395
|
}
|
|
264
396
|
```
|
|
265
397
|
|
|
266
|
-
## Token Management & Cookie Behavior
|
|
267
|
-
|
|
268
|
-
### How It Works
|
|
269
|
-
|
|
270
|
-
1. **Server Components**: Generate tokens but can't persist them (read-only cookies)
|
|
271
|
-
2. **Client Components**: Generate and persist tokens via `document.cookie`
|
|
272
|
-
3. **Server Actions/API Routes**: Generate and persist tokens via Next.js `cookies()`
|
|
273
|
-
4. **Shared State**: All contexts read from the same cookie storage
|
|
274
|
-
|
|
275
|
-
### Cookie Configuration
|
|
276
|
-
|
|
277
|
-
The SDK uses secure cookies with sensible defaults:
|
|
278
|
-
|
|
279
|
-
```typescript
|
|
280
|
-
// Advanced cookie configuration (optional)
|
|
281
|
-
<StorefrontSDKInitializer
|
|
282
|
-
config={{
|
|
283
|
-
storeId: "your-store-id",
|
|
284
|
-
apiKey: "your-api-key",
|
|
285
|
-
tokenStorageOptions: {
|
|
286
|
-
prefix: "ce_", // Cookie name prefix
|
|
287
|
-
maxAge: 30 * 24 * 60 * 60, // 30 days
|
|
288
|
-
path: "/",
|
|
289
|
-
secure: true, // HTTPS only (auto-detected)
|
|
290
|
-
sameSite: "Lax",
|
|
291
|
-
}
|
|
292
|
-
}}
|
|
293
|
-
/>
|
|
294
|
-
```
|
|
295
|
-
|
|
296
398
|
## API Reference
|
|
297
399
|
|
|
298
|
-
### `
|
|
400
|
+
### `createStorefront(config?)`
|
|
299
401
|
|
|
300
|
-
|
|
402
|
+
Creates a configured storefront function that works universally across all Next.js contexts.
|
|
301
403
|
|
|
302
404
|
```typescript
|
|
303
|
-
import {
|
|
304
|
-
import { cookies } from "next/headers";
|
|
305
|
-
|
|
306
|
-
// ✅ Client-side (browser)
|
|
307
|
-
const sdk = storefront();
|
|
308
|
-
|
|
309
|
-
// ✅ Server-side (requires cookies for token persistence)
|
|
310
|
-
const sdk = storefront(cookies());
|
|
311
|
-
|
|
312
|
-
// ❌ Server-side without cookies (helpful error message)
|
|
313
|
-
const sdk = storefront(); // Throws clear error with instructions
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
### `StorefrontSDKInitializer`
|
|
317
|
-
|
|
318
|
-
**Client-side initializer** component (must be imported from `/client`):
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
import { Environment, StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
|
|
322
|
-
|
|
323
|
-
<StorefrontSDKInitializer
|
|
324
|
-
config={{
|
|
325
|
-
storeId: "your-store-id",
|
|
326
|
-
environment: Environment.Production, // or Environment.Staging
|
|
327
|
-
apiKey: "your-api-key",
|
|
328
|
-
|
|
329
|
-
// Optional: Advanced configuration
|
|
330
|
-
tokenStorageOptions: {
|
|
331
|
-
prefix: "ce_", // Cookie prefix (default)
|
|
332
|
-
maxAge: 30 * 24 * 60 * 60, // 30 days
|
|
333
|
-
secure: true, // Auto-detected from environment
|
|
334
|
-
sameSite: "Lax",
|
|
335
|
-
},
|
|
336
|
-
}}
|
|
337
|
-
/>
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
## Error Handling
|
|
341
|
-
|
|
342
|
-
All SDK methods return a consistent error structure:
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
const { data, error, response } = await storefront(cookies()).catalog.listProducts();
|
|
405
|
+
import { createStorefront } from "@commercengine/storefront-sdk-nextjs";
|
|
346
406
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
console.log(
|
|
350
|
-
}
|
|
351
|
-
console.log("Products:", data.products);
|
|
352
|
-
}
|
|
407
|
+
export const storefront = createStorefront({
|
|
408
|
+
debug: true,
|
|
409
|
+
logger: (msg, ...args) => console.log('[DEBUG]', msg, ...args)
|
|
410
|
+
});
|
|
353
411
|
```
|
|
354
412
|
|
|
355
|
-
|
|
413
|
+
**Parameters:**
|
|
414
|
+
- `config` (optional): `StorefrontRuntimeConfig` - Advanced configuration options
|
|
356
415
|
|
|
357
|
-
|
|
416
|
+
**Returns:**
|
|
417
|
+
- Universal `storefront()` function that works in all Next.js contexts
|
|
358
418
|
|
|
359
|
-
###
|
|
419
|
+
### `StorefrontRuntimeConfig`
|
|
360
420
|
|
|
361
|
-
|
|
421
|
+
Optional configuration object for `createStorefront()`:
|
|
362
422
|
|
|
363
423
|
```typescript
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
424
|
+
interface StorefrontRuntimeConfig {
|
|
425
|
+
// Override environment variables
|
|
426
|
+
storeId?: string;
|
|
427
|
+
apiKey?: string;
|
|
428
|
+
environment?: Environment;
|
|
429
|
+
baseUrl?: string;
|
|
430
|
+
timeout?: number;
|
|
431
|
+
debug?: boolean;
|
|
370
432
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
433
|
+
// Advanced options (not available via environment variables)
|
|
434
|
+
accessToken?: string;
|
|
435
|
+
refreshToken?: string;
|
|
436
|
+
defaultHeaders?: SupportedDefaultHeaders;
|
|
437
|
+
logger?: (message: string, ...args: any[]) => void;
|
|
438
|
+
onTokensUpdated?: (accessToken: string, refreshToken: string) => void;
|
|
439
|
+
onTokensCleared?: () => void;
|
|
440
|
+
tokenStorageOptions?: NextJSTokenStorageOptions;
|
|
441
|
+
}
|
|
380
442
|
```
|
|
381
443
|
|
|
382
|
-
###
|
|
444
|
+
### Universal `storefront()` Function
|
|
383
445
|
|
|
384
|
-
|
|
446
|
+
The function returned by `createStorefront()` works in all contexts with strict enforcement:
|
|
385
447
|
|
|
386
448
|
```typescript
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
import { notFound } from "next/navigation";
|
|
449
|
+
// Client-side (browser)
|
|
450
|
+
const sdk = storefront();
|
|
390
451
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
452
|
+
// Server-side (requires cookies for user continuity)
|
|
453
|
+
const sdk = storefront(cookies());
|
|
394
454
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const sdk = storefront(); // No cookies() - uses build-time storage
|
|
398
|
-
|
|
399
|
-
// Get product detail by slug or ID
|
|
400
|
-
const { data, error } = await sdk.catalog.getProductDetail({
|
|
401
|
-
product_id_or_slug: slug
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
if (error || !data) {
|
|
405
|
-
notFound();
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const { product } = data;
|
|
409
|
-
|
|
410
|
-
return (
|
|
411
|
-
<div>
|
|
412
|
-
<h1>{product.name}</h1>
|
|
413
|
-
<p>SKU: {product.sku}</p>
|
|
414
|
-
<p>Type: {product.product_type}</p>
|
|
415
|
-
<p>Description: {product.short_description}</p>
|
|
416
|
-
</div>
|
|
417
|
-
);
|
|
418
|
-
}
|
|
455
|
+
// Root Layout (special exception with explicit flag)
|
|
456
|
+
const sdk = storefront({ isRootLayout: true });
|
|
419
457
|
|
|
420
|
-
//
|
|
421
|
-
|
|
422
|
-
const sdk = storefront(); // No cookies() - uses build-time storage
|
|
423
|
-
|
|
424
|
-
const { data: productsData, error } = await sdk.catalog.listProducts({
|
|
425
|
-
limit: 100 // Generate pages for first 100 products
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
if (error || !productsData) {
|
|
429
|
-
console.error("Error fetching products for static generation:", error);
|
|
430
|
-
return [];
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return productsData.products.map(product => ({
|
|
434
|
-
slug: product.slug || product.id
|
|
435
|
-
}));
|
|
436
|
-
}
|
|
458
|
+
// Server-side without cookies - throws helpful error to protect user sessions
|
|
459
|
+
const sdk = storefront(); // ❌ Throws error in server contexts
|
|
437
460
|
```
|
|
438
461
|
|
|
439
|
-
###
|
|
440
|
-
|
|
441
|
-
Create a page that makes multiple API calls during build to test token reuse:
|
|
462
|
+
### Function Signatures
|
|
442
463
|
|
|
443
464
|
```typescript
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const sdk = storefront(); // No cookies() - uses build-time storage
|
|
449
|
-
|
|
450
|
-
// Make multiple API calls - token will be created once and reused
|
|
451
|
-
const { data: products, error: productsError } = await sdk.catalog.listProducts({
|
|
452
|
-
limit: 10
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
const { data: categories, error: categoriesError } = await sdk.catalog.listCategories({
|
|
456
|
-
nested_level: 1
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
const { data: anonymousToken, error: tokenError } = await sdk.auth.getAnonymousToken();
|
|
460
|
-
|
|
461
|
-
const buildTime = new Date().toISOString();
|
|
462
|
-
|
|
463
|
-
return (
|
|
464
|
-
<div>
|
|
465
|
-
<h1>SSG Test Page</h1>
|
|
466
|
-
<p><strong>Built at:</strong> {buildTime}</p>
|
|
467
|
-
|
|
468
|
-
<div>
|
|
469
|
-
<h2>Build-time API Results:</h2>
|
|
470
|
-
|
|
471
|
-
<div>
|
|
472
|
-
<h3>Products Call:</h3>
|
|
473
|
-
{productsError ? (
|
|
474
|
-
<p style={{ color: 'red' }}>Error: {productsError.message}</p>
|
|
475
|
-
) : (
|
|
476
|
-
<p>✅ Success: Found {products?.products?.length || 0} products</p>
|
|
477
|
-
)}
|
|
478
|
-
</div>
|
|
479
|
-
|
|
480
|
-
<div>
|
|
481
|
-
<h3>Categories Call:</h3>
|
|
482
|
-
{categoriesError ? (
|
|
483
|
-
<p style={{ color: 'red' }}>Error: {categoriesError.message}</p>
|
|
484
|
-
) : (
|
|
485
|
-
<p>✅ Success: Found {categories?.categories?.length || 0} categories</p>
|
|
486
|
-
)}
|
|
487
|
-
</div>
|
|
488
|
-
|
|
489
|
-
<div>
|
|
490
|
-
<h3>Anonymous Token Call:</h3>
|
|
491
|
-
{tokenError ? (
|
|
492
|
-
<p style={{ color: 'red' }}>Error: {tokenError.message}</p>
|
|
493
|
-
) : (
|
|
494
|
-
<p>✅ Success: User ID {anonymousToken?.user?.id}</p>
|
|
495
|
-
)}
|
|
496
|
-
</div>
|
|
497
|
-
</div>
|
|
498
|
-
</div>
|
|
499
|
-
);
|
|
500
|
-
}
|
|
465
|
+
function storefront(): StorefrontSDK;
|
|
466
|
+
function storefront(cookieStore: NextCookieStore): StorefrontSDK;
|
|
467
|
+
function storefront(options: { isRootLayout: true }): StorefrontSDK;
|
|
468
|
+
function storefront(cookieStore: NextCookieStore, options: { isRootLayout?: boolean }): StorefrontSDK;
|
|
501
469
|
```
|
|
502
470
|
|
|
503
|
-
###
|
|
504
|
-
|
|
505
|
-
With token caching enabled, you'll see dramatic performance improvements:
|
|
506
|
-
|
|
507
|
-
**Without Caching:**
|
|
508
|
-
- 🔴 Each page creates its own token
|
|
509
|
-
- 🔴 100 pages = 100+ anonymous token API calls
|
|
510
|
-
- 🔴 Slower builds, higher API usage
|
|
511
|
-
|
|
512
|
-
**With Caching:**
|
|
513
|
-
- ✅ Token created once and reused across all pages
|
|
514
|
-
- ✅ 100 pages = ~3 anonymous token API calls total
|
|
515
|
-
- ✅ Faster builds, dramatically lower API usage
|
|
471
|
+
### `StorefrontSDKInitializer`
|
|
516
472
|
|
|
517
|
-
|
|
473
|
+
Client-side initializer component (must be imported from `/client`):
|
|
518
474
|
|
|
519
|
-
|
|
475
|
+
```typescript
|
|
476
|
+
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
|
|
520
477
|
|
|
521
|
-
|
|
522
|
-
🚀 [BuildCache] Using BuildCachingMemoryTokenStorage with key: store:staging
|
|
523
|
-
🟡 [BuildCache] No cached token found for key: store:staging
|
|
524
|
-
🟠 [BuildCache] Caching new access token for key: store:staging
|
|
525
|
-
🔵 [BuildCache] Using instance token for key: store:staging
|
|
526
|
-
🟢 [BuildCache] Using cached token for key: store:staging
|
|
478
|
+
<StorefrontSDKInitializer />
|
|
527
479
|
```
|
|
528
480
|
|
|
529
|
-
|
|
530
|
-
- ✅ Only 1-3 "Automatically created anonymous session" messages
|
|
531
|
-
- ✅ Many "Using instance token" or "Using cached token" messages
|
|
532
|
-
- ✅ Minimal "Caching new access token" messages
|
|
481
|
+
No props needed - configuration comes from environment variables and your `lib/storefront.ts` file.
|
|
533
482
|
|
|
534
|
-
|
|
483
|
+
## Error Handling
|
|
535
484
|
|
|
536
|
-
|
|
485
|
+
All SDK methods return a consistent error structure:
|
|
537
486
|
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
NEXT_BUILD_CACHE_TOKENS=true pnpm build
|
|
487
|
+
```typescript
|
|
488
|
+
const { data, error, response } = await storefront(cookies()).catalog.listProducts();
|
|
541
489
|
|
|
542
|
-
|
|
543
|
-
|
|
490
|
+
if (error) {
|
|
491
|
+
console.error("API Error:", error.message, error.code);
|
|
492
|
+
console.log("Status:", response.status);
|
|
493
|
+
} else {
|
|
494
|
+
console.log("Products:", data.products);
|
|
495
|
+
}
|
|
544
496
|
```
|
|
545
497
|
|
|
546
498
|
## Best Practices
|
|
547
499
|
|
|
548
500
|
### ✅ Do's
|
|
549
501
|
|
|
502
|
+
- **Set required environment variables**: `NEXT_PUBLIC_STORE_ID` and `NEXT_PUBLIC_API_KEY` are mandatory
|
|
503
|
+
- **Create one lib/storefront.ts file**: Use `createStorefront()` to configure advanced options
|
|
550
504
|
- **Initialize once**: Call `StorefrontSDKInitializer` only in your root layout
|
|
551
505
|
- **Use `storefront(cookies())`**: Always pass cookies in server contexts
|
|
552
506
|
- **Handle errors**: Always check the error property in responses
|
|
553
507
|
- **Use Server Actions**: For authentication flows that need to persist tokens
|
|
554
|
-
- **Environment variables**: Store sensitive config in environment variables
|
|
555
508
|
|
|
556
509
|
### ❌ Don'ts
|
|
557
510
|
|
|
558
511
|
- **Don't call `cookies()` in Client Components**: It will throw an error
|
|
559
512
|
- **Don't initialize multiple times**: The SDK handles singleton behavior
|
|
560
513
|
- **Don't forget error handling**: API calls can fail for various reasons
|
|
561
|
-
- **Don't
|
|
514
|
+
- **Don't skip environment variables**: Missing required variables will cause errors
|
|
562
515
|
|
|
563
516
|
## Troubleshooting
|
|
564
517
|
|
|
565
518
|
### Common Issues
|
|
566
519
|
|
|
567
|
-
1. **"
|
|
520
|
+
1. **"Server context requires cookies for user continuity!"**
|
|
568
521
|
```typescript
|
|
569
|
-
// ❌ Wrong
|
|
570
|
-
storefront(
|
|
522
|
+
// ❌ Wrong - server context without cookies (breaks user sessions)
|
|
523
|
+
storefront()
|
|
571
524
|
|
|
572
|
-
// ✅ Correct
|
|
573
|
-
storefront()
|
|
525
|
+
// ✅ Correct - server context with cookies
|
|
526
|
+
storefront(cookies())
|
|
527
|
+
|
|
528
|
+
// ✅ Root Layout exception
|
|
529
|
+
storefront({ isRootLayout: true })
|
|
574
530
|
```
|
|
575
531
|
|
|
576
|
-
2. **
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
|
|
532
|
+
2. **Missing Environment Variables**
|
|
533
|
+
```bash
|
|
534
|
+
# Ensure your .env.local has the required variables:
|
|
535
|
+
NEXT_PUBLIC_STORE_ID=your-store-id
|
|
536
|
+
NEXT_PUBLIC_API_KEY=your-api-key
|
|
580
537
|
```
|
|
581
538
|
|
|
582
|
-
3. **"
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
```
|
|
587
|
-
🚨 Server Environment Detected!
|
|
539
|
+
3. **"Cookie store passed in client environment"**
|
|
540
|
+
```typescript
|
|
541
|
+
// ❌ Wrong - client component with cookies
|
|
542
|
+
storefront(cookies())
|
|
588
543
|
|
|
589
|
-
|
|
590
|
-
|
|
544
|
+
// ✅ Correct - client component without cookies
|
|
545
|
+
storefront()
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
4. **Server Actions and async cookies()**
|
|
549
|
+
```typescript
|
|
550
|
+
// ✅ Correct - Server Actions need await cookies()
|
|
551
|
+
const sdk = storefront(await cookies());
|
|
591
552
|
|
|
592
|
-
✅
|
|
593
|
-
import { cookies } from 'next/headers';
|
|
553
|
+
// ✅ Server Components and API Routes use cookies() directly
|
|
594
554
|
const sdk = storefront(cookies());
|
|
595
|
-
|
|
596
|
-
❌ Your current usage:
|
|
597
|
-
const sdk = storefront(); // Missing cookies!
|
|
598
555
|
```
|
|
599
556
|
|
|
600
|
-
### Debug Mode
|
|
601
|
-
|
|
602
|
-
Enable detailed logging during development:
|
|
603
|
-
|
|
604
|
-
```typescript
|
|
605
|
-
<StorefrontSDKInitializer
|
|
606
|
-
config={{
|
|
607
|
-
storeId: "your-store-id",
|
|
608
|
-
apiKey: "your-api-key",
|
|
609
|
-
debug: true, // Enable debug logging
|
|
610
|
-
}}
|
|
611
|
-
/>
|
|
612
|
-
```
|
|
613
|
-
|
|
614
557
|
## Migration Guide
|
|
615
558
|
|
|
616
559
|
### From Core SDK
|
|
@@ -622,105 +565,36 @@ const sdk = new StorefrontSDK({
|
|
|
622
565
|
storeId: "...",
|
|
623
566
|
tokenStorage: new BrowserTokenStorage()
|
|
624
567
|
});
|
|
625
|
-
await sdk.auth.getAnonymousToken(); // Manual token creation
|
|
626
568
|
|
|
627
569
|
// After (Next.js SDK)
|
|
628
|
-
import { storefront } from "
|
|
629
|
-
const sdk = storefront(cookies());
|
|
570
|
+
import { storefront } from "@/lib/storefront";
|
|
571
|
+
const sdk = storefront(cookies());
|
|
630
572
|
```
|
|
631
573
|
|
|
632
|
-
### From Previous
|
|
574
|
+
### From Previous SDK Versions
|
|
633
575
|
|
|
634
576
|
```typescript
|
|
635
577
|
// Before (old pattern)
|
|
636
|
-
import { getStorefrontSDK } from "@commercengine/storefront-sdk-nextjs";
|
|
578
|
+
import { getStorefrontSDK, storefront } from "@commercengine/storefront-sdk-nextjs";
|
|
637
579
|
const sdk = getStorefrontSDK(cookies());
|
|
580
|
+
const sdk2 = storefront(cookies(), { debug: true });
|
|
638
581
|
|
|
639
|
-
// After (
|
|
640
|
-
import { storefront } from "
|
|
582
|
+
// After (createStorefront pattern)
|
|
583
|
+
import { storefront } from "@/lib/storefront"; // Your configured function
|
|
641
584
|
const sdk = storefront(cookies());
|
|
642
585
|
```
|
|
643
586
|
|
|
644
|
-
##
|
|
645
|
-
|
|
646
|
-
The SDK provides complete TypeScript support with auto-generated types:
|
|
647
|
-
|
|
648
|
-
```typescript
|
|
649
|
-
import type {
|
|
650
|
-
StorefrontSDK,
|
|
651
|
-
ApiResult,
|
|
652
|
-
UserInfo,
|
|
653
|
-
NextJSSDKConfig,
|
|
654
|
-
Environment
|
|
655
|
-
} from "@commercengine/storefront-sdk-nextjs";
|
|
656
|
-
|
|
657
|
-
// All API responses are properly typed
|
|
658
|
-
const { data }: ApiResult<ProductListResponse> = await storefront(cookies()).catalog.listProducts();
|
|
659
|
-
|
|
660
|
-
// IntelliSense works for all nested properties
|
|
661
|
-
console.log(data?.products[0].name);
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
## User Information
|
|
665
|
-
|
|
666
|
-
Get current user information from tokens:
|
|
667
|
-
|
|
668
|
-
```typescript
|
|
669
|
-
const sdk = storefront(cookies());
|
|
670
|
-
|
|
671
|
-
const userInfo = await sdk.getUserInfo();
|
|
672
|
-
const isLoggedIn = await sdk.isLoggedIn();
|
|
673
|
-
const isAnonymous = await sdk.isAnonymous();
|
|
674
|
-
const userId = await sdk.getUserId();
|
|
675
|
-
const customerId = await sdk.getCustomerId();
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
## Advanced Usage
|
|
679
|
-
|
|
680
|
-
### Custom Token Storage
|
|
681
|
-
|
|
682
|
-
```typescript
|
|
683
|
-
import { TokenStorage } from "@commercengine/storefront-sdk-nextjs";
|
|
684
|
-
|
|
685
|
-
class RedisTokenStorage implements TokenStorage {
|
|
686
|
-
async getAccessToken(): Promise<string | null> {
|
|
687
|
-
// Your Redis implementation
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
async setAccessToken(token: string): Promise<void> {
|
|
691
|
-
// Your Redis implementation
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// ... implement other methods
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
// Use with the core SDK directly if needed
|
|
698
|
-
import { StorefrontSDK } from "@commercengine/storefront-sdk-nextjs";
|
|
699
|
-
|
|
700
|
-
const sdk = new StorefrontSDK({
|
|
701
|
-
storeId: "your-store-id",
|
|
702
|
-
apiKey: "your-api-key",
|
|
703
|
-
tokenStorage: new RedisTokenStorage(),
|
|
704
|
-
});
|
|
705
|
-
```
|
|
706
|
-
|
|
707
|
-
## Why This Wrapper Exists
|
|
708
|
-
|
|
709
|
-
**The core SDK is powerful but complex for Next.js:**
|
|
710
|
-
- Manual token storage setup and configuration
|
|
711
|
-
- Server/client runtime environment handling
|
|
712
|
-
- Cookie configuration for SSR/SSG
|
|
713
|
-
- Request isolation and per-request instances
|
|
714
|
-
- Next.js-specific execution model complexities
|
|
587
|
+
## Why This Pattern?
|
|
715
588
|
|
|
716
|
-
**
|
|
717
|
-
-
|
|
718
|
-
-
|
|
719
|
-
-
|
|
720
|
-
-
|
|
721
|
-
-
|
|
589
|
+
**The Perfect DX Pattern provides:**
|
|
590
|
+
- **One Config File**: All advanced configuration in `lib/storefront.ts`
|
|
591
|
+
- **Environment Variables**: Basic config (storeId, apiKey) via env vars
|
|
592
|
+
- **Universal Import**: Same `storefront()` import everywhere
|
|
593
|
+
- **Strict User Continuity**: Enforces cookie passing to protect user sessions and analytics
|
|
594
|
+
- **Explicit Exceptions**: Clear patterns for special cases like Root Layout
|
|
595
|
+
- **Zero Guesswork**: Helpful errors guide developers to correct patterns
|
|
722
596
|
|
|
723
|
-
**Result: Production-ready e-commerce with
|
|
597
|
+
**Result: Production-ready e-commerce with bulletproof user session management!** 🛡️
|
|
724
598
|
|
|
725
599
|
## API Reference
|
|
726
600
|
|