@commercengine/storefront-sdk-nextjs
v0.2.5
Published
Next.js wrapper for the Commerce Engine Storefront SDK
Maintainers
Readme
CommerceEngine Next.js SDK
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.
Built on @commercengine/ssr-utils for robust server-side token management.
✨ Perfect DX Pattern:
- 🎯 One Config File - Single
lib/storefront.tsusingcreateStorefront() - 🌍 Universal API - Same
storefront()import works everywhere - 🔥 Automatic Tokens - Creates and manages tokens automatically
- 🧠 Smart Context Detection - Detects Server vs Client vs Build contexts
- 🍪 Cookie-based State - Shared authentication via Next.js cookies
- ⚡ Request Isolation - Proper per-request SDK instances on server
- 🛠 Zero Complexity - Environment variables + one lib file = done
Installation
npm install @commercengine/storefront-sdk-nextjs
# or
pnpm add @commercengine/storefront-sdk-nextjsQuick Start
1. Environment Variables (Required)
Add your store configuration to .env.local:
NEXT_PUBLIC_STORE_ID=your-store-id
NEXT_PUBLIC_API_KEY=your-api-key
# Environment (defaults to "staging")
NEXT_PUBLIC_ENVIRONMENT=staging # or "production"2. Create Your Storefront Configuration
Create lib/storefront.ts in your project:
// lib/storefront.ts
import {
createStorefront,
type StorefrontRuntimeConfig,
} from "@commercengine/storefront-sdk-nextjs";
// Optional advanced configuration (everything not in environment variables)
const storefrontConfig: StorefrontRuntimeConfig = {
debug: true,
timeout: 15000,
logger: (msg: string, ...args: any[]) =>
console.log("[STOREFRONT]", msg, ...args),
onTokensUpdated: (access: string, refresh: string) => {
console.log("🔥 TOKENS UPDATED:", {
access: access.slice(0, 20) + "...",
refresh: refresh.slice(0, 20) + "...",
});
},
onTokensCleared: () => {
console.log("🔄 TOKENS CLEARED");
},
};
// Create the configured storefront function (storefrontConfig is OPTIONAL)
export const storefront = createStorefront(storefrontConfig);
// Re-export types for convenience
export type { StorefrontRuntimeConfig };All Core SDK Types Available: The Next.js SDK re-exports all types from the core SDK, so you can import them directly:
// Import any core SDK types you need
import type {
UserInfo,
SupportedDefaultHeaders,
StorefrontSDKOptions,
components,
operations
} from "@commercengine/storefront-sdk-nextjs";
// Use imported types
const headers: SupportedDefaultHeaders = {
customer_group_id: "01ABC123..."
};3. Initialize in Root Layout
// app/layout.tsx
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<StorefrontSDKInitializer />
{children}
</body>
</html>
);
}4. Use Anywhere with Universal Import
// Import your configured storefront everywhere
import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers"; // Only for server contexts
// ✅ Client Component - No cookies needed
const products = await storefront().catalog.listProducts();
// ✅ Server Component, Server Action, or API Route - MUST pass cookies
const products = await storefront(cookies()).catalog.listProducts();
// ✅ Root Layout - Special exception with explicit flag
const products = await storefront({ isRootLayout: true }).catalog.listProducts();
// ✅ SSG/ISR (build contexts) - Automatic fallback to memory storage
// (NEXT_BUILD_CACHE_TOKENS=true enables this)
const products = await storefront().catalog.listProducts();Usage in Different Next.js Contexts
Client Components
Client Components run in the browser and can persist tokens via cookies:
// components/ProductList.tsx
"use client";
import { useState, useEffect } from "react";
import { storefront } from "@/lib/storefront";
export default function ProductList() {
const [products, setProducts] = useState([]);
useEffect(() => {
async function loadProducts() {
const sdk = storefront(); // No cookies() needed on client-side
// Tokens are automatically managed by StorefrontSDKInitializer
const { data } = await sdk.catalog.listProducts();
if (data) setProducts(data.products);
}
loadProducts();
}, []);
return (
<div>
{products.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
);
}Server Components
Server Components run on the server and can read cookies:
// app/products/page.tsx
import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers";
export default async function ProductsPage() {
const sdk = storefront(cookies());
const { data: products } = await sdk.catalog.listProducts();
return (
<div>
{products?.products.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
);
}Server Actions
Server Actions can both read and write cookies, perfect for authentication:
// app/actions.ts
"use server";
import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers";
export async function loginWithEmail(email: string, password: string) {
const sdk = storefront(cookies());
const { data, error } = await sdk.auth.loginWithPassword({ email, password });
if (data) {
// Tokens are automatically saved to cookies
return { success: true, user: data.user };
}
return { success: false, error: error?.message };
}API Routes
API Routes work identically to Server Actions:
// app/api/products/route.ts
import { storefront } from "@/lib/storefront";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
export async function GET() {
try {
const sdk = storefront(cookies());
const { data, error } = await sdk.catalog.listProducts();
if (error) {
return NextResponse.json({ error: error.message }, { status: 400 });
}
return NextResponse.json(data);
} catch (error) {
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}Root Layout (Special Case)
Root Layout requires explicit flag since it's outside request context. Passing this flag will instruct the SDK to fallback to MemoryStore when rendering the root layout as the root layout runs on both server and client before cookie context is available.
// app/layout.tsx
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
import { storefront } from "@/lib/storefront";
// Root Layout requires explicit flag - no request context available
const sdk = storefront({ isRootLayout: true });
const { data: storeConfig } = await sdk.store.getStoreConfig();
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<StorefrontSDKInitializer />
<h1>Welcome to {storeConfig?.store_config?.brand.name}</h1>
{children}
</body>
</html>
);
}Static Site Generation (SSG) & Build-Time Optimization
The SDK provides powerful build-time optimizations through intelligent token caching.
Configure Next.js for Build-Time Optimization
Create or update your next.config.ts (or next.config.js) to enable automatic token caching during builds:
// next.config.ts
import type { NextConfig } from "next";
import { PHASE_PRODUCTION_BUILD } from 'next/constants';
const nextConfig = (phase: string): NextConfig => {
const isBuild = phase === PHASE_PRODUCTION_BUILD;
return {
env: {
// Enable build-time token caching during production builds and when explicitly set
NEXT_BUILD_CACHE_TOKENS: process.env.NEXT_BUILD_CACHE_TOKENS ?? (isBuild ? 'true' : 'false'),
// Critical: tells SDK to use MemoryStore during SSG/Build/ISR to avoid cookie context failures
NEXT_IS_BUILD: isBuild ? 'true' : 'false',
},
// Ensure static generation is enabled
output: undefined, // Let Next.js decide based on usage
};
};
export default nextConfig;SSG with generateStaticParams
// app/products/[slug]/page.tsx
import { storefront } from "@/lib/storefront";
import { notFound } from "next/navigation";
interface ProductPageProps {
params: Promise<{ slug: string }>;
}
export default async function ProductPage({ params }: ProductPageProps) {
const { slug } = await params;
const sdk = storefront(); // No cookies() - uses build-time storage
const { data, error } = await sdk.catalog.getProductDetail({
product_id_or_slug: slug
});
if (error || !data) {
notFound();
}
return (
<div>
<h1>{data.product.name}</h1>
<p>SKU: {data.product.sku}</p>
<p>Description: {data.product.short_description}</p>
</div>
);
}
// Generate static params from real API data
export async function generateStaticParams() {
const sdk = storefront(); // Token will be cached and reused
const { data: productsData, error } = await sdk.catalog.listProducts({
limit: 100
});
if (error || !productsData) {
return [];
}
return productsData.products.map(product => ({
slug: product.slug || product.id
}));
}Build Performance Benefits
With token caching enabled:
- ✅ Token created once and reused across all pages
- ✅ 100 pages = ~1 anonymous token API call total
- ✅ Faster builds, dramatically lower API usage
Authentication Patterns
⚠️ Important: Any authentication endpoint that returns tokens (like
loginWithPassword,verifyOtp,register, etc.) must be called in contexts where cookies can be set and managed:
- ✅ Server Actions (recommended for authentication flows)
- ✅ API Routes (
/apidirectory)- ✅ Client Components (browser environment)
- ❌ Server Components (cannot set cookies, tokens won't persist)
This ensures the SDK can automatically handle token storage and user session continuity.
Anonymous Users
The SDK automatically creates anonymous tokens via the StorefrontSDKInitializer component imported in your root layout. If this component is not imported (not recommended), a new token will be minted for each request as a fallback. It is highly recommended to use the StorefrontSDKInitializer and ensure all token-returning endpoints are called from request contexts that can set cookies. Authentication Patterns.
// This works everywhere - creates anonymous token automatically
const { data: products } = await storefront(cookies()).catalog.listProducts();Email/Password Login
// Server Action (✅ Recommended - can set cookies for token persistence)
"use server";
export async function loginUser(email: string, password: string) {
const sdk = storefront(cookies());
const { data, error } = await sdk.auth.loginWithPassword({ email, password });
if (data) {
// Tokens automatically saved to cookies
redirect('/dashboard');
}
return { error: error?.message };
}Phone/OTP Login
// Server Action - Step 1: Send OTP (✅ Recommended context)
export async function sendOTP(phone: string, country_code: string) {
const sdk = storefront(await cookies());
return await sdk.auth.loginWithPhone({
phone,
country_code,
register_if_not_exists: true
});
}
// Server Action - Step 2: Verify OTP (✅ Recommended - can set cookies for token persistence)
export async function verifyOTP(otp: string, otp_token: string, otp_action: string) {
const sdk = storefront(await cookies());
const { data, error } = await sdk.auth.verifyOtp({
otp,
otp_token,
otp_action
});
if (data) {
// Tokens automatically saved to cookies
return { success: true, user: data.user };
}
return { success: false, error: error?.message };
}API Reference
createStorefront(config?)
Creates a configured storefront function that works universally across all Next.js contexts.
import { createStorefront } from "@commercengine/storefront-sdk-nextjs";
export const storefront = createStorefront({
debug: true,
logger: (msg, ...args) => console.log('[DEBUG]', msg, ...args)
});Parameters:
config(optional):StorefrontRuntimeConfig- Advanced configuration options
Returns:
- Universal
storefront()function that works in all Next.js contexts
StorefrontRuntimeConfig
Optional configuration object for createStorefront():
interface StorefrontRuntimeConfig {
// Override environment variables
storeId?: string;
apiKey?: string;
environment?: Environment;
baseUrl?: string;
timeout?: number;
debug?: boolean;
// Advanced options (not available via environment variables)
accessToken?: string;
refreshToken?: string;
defaultHeaders?: SupportedDefaultHeaders;
logger?: (message: string, ...args: any[]) => void;
onTokensUpdated?: (accessToken: string, refreshToken: string) => void;
onTokensCleared?: () => void;
tokenStorageOptions?: NextJSTokenStorageOptions;
}Universal storefront() Function
The function returned by createStorefront() works in all contexts with strict enforcement:
// Client-side (browser)
const sdk = storefront();
// Server-side (requires cookies for user continuity)
const sdk = storefront(cookies());
// Root Layout (special exception with explicit flag)
const sdk = storefront({ isRootLayout: true });
// Server-side without cookies - throws helpful error to protect user sessions
const sdk = storefront(); // ❌ Throws error in server contextsFunction Signatures
function storefront(): StorefrontSDK;
function storefront(cookieStore: NextCookieStore): StorefrontSDK;
function storefront(options: { isRootLayout: true }): StorefrontSDK;
function storefront(cookieStore: NextCookieStore, options: { isRootLayout?: boolean }): StorefrontSDK;StorefrontSDKInitializer
Client-side initializer component (must be imported from /client):
import { StorefrontSDKInitializer } from "@commercengine/storefront-sdk-nextjs/client";
<StorefrontSDKInitializer />No props needed - configuration comes from environment variables and your lib/storefront.ts file.
Error Handling
All SDK methods return a consistent error structure:
const { data, error, response } = await storefront(cookies()).catalog.listProducts();
if (error) {
console.error("API Error:", error.message, error.code);
console.log("Status:", response.status);
} else {
console.log("Products:", data.products);
}Best Practices
✅ Do's
- Set required environment variables:
NEXT_PUBLIC_STORE_IDandNEXT_PUBLIC_API_KEYare mandatory - Create one lib/storefront.ts file: Use
createStorefront()to configure advanced options - Initialize once: Call
StorefrontSDKInitializeronly in your root layout - Use
storefront(cookies()): Always pass cookies in server contexts - Handle errors: Always check the error property in responses
- Use Server Actions: For authentication flows that need to persist tokens
❌ Don'ts
- Don't call
cookies()in Client Components: It will throw an error - Don't initialize multiple times: The SDK handles singleton behavior
- Don't forget error handling: API calls can fail for various reasons
- Don't skip environment variables: Missing required variables will cause errors
Troubleshooting
Common Issues
"Server context requires cookies for user continuity!"
// ❌ Wrong - server context without cookies (breaks user sessions) storefront() // ✅ Correct - server context with cookies storefront(cookies()) // ✅ Root Layout exception storefront({ isRootLayout: true })Missing Environment Variables
# Ensure your .env.local has the required variables: NEXT_PUBLIC_STORE_ID=your-store-id NEXT_PUBLIC_API_KEY=your-api-key"Cookie store passed in client environment"
// ❌ Wrong - client component with cookies storefront(cookies()) // ✅ Correct - client component without cookies storefront()Server Actions and async cookies()
// ✅ Correct - Server Actions need await cookies() const sdk = storefront(await cookies()); // ✅ Server Components and API Routes use cookies() directly const sdk = storefront(cookies());
Migration Guide
From Core SDK
// Before (core SDK)
import { StorefrontSDK } from "@commercengine/storefront-sdk";
const sdk = new StorefrontSDK({
storeId: "...",
tokenStorage: new BrowserTokenStorage()
});
// After (Next.js SDK)
import { storefront } from "@/lib/storefront";
const sdk = storefront(cookies());Why This Pattern?
The Perfect DX Pattern provides:
- One Config File: All advanced configuration in
lib/storefront.ts - Environment Variables: Basic config (storeId, apiKey) via env vars
- Universal Import: Same
storefront()import everywhere - Strict User Continuity: Enforces cookie passing to protect user sessions and analytics
- Explicit Exceptions: Clear patterns for special cases like Root Layout
- Zero Guesswork: Helpful errors guide developers to correct patterns
Result: Production-ready e-commerce with bulletproof user session management! 🛡️
API Reference
For complete API documentation of all available endpoints, visit SDK DOCS REFERENCE
The Next.js SDK provides access to all the same endpoints as the core SDK:
sdk.auth.*- Authentication and user managementsdk.customer.*- Customer profiles and preferencessdk.catalog.*- Products, categories, and searchsdk.cart.*- Shopping cart managementsdk.order.*- Order creation and trackingsdk.payments.*- Payment methods and processingsdk.store.*- Store configurationsdk.helpers.*- Countries, currencies, utilities
Related Packages
@commercengine/storefront-sdk- Core Storefront SDK
License
All Rights Reserved
