Commerce Engine is now in beta. We're working hard to make it better for you.
Complete guide to using the SDK in Node.js backend applications
npm install @commercengine/storefront-sdk # or pnpm install @commercengine/storefront-sdk
// src/lib/storefront.ts import { StorefrontSDK, Environment, MemoryTokenStorage } from '@commercengine/storefront-sdk'; const storefront = new StorefrontSDK({ storeId: process.env.STORE_ID!, environment: process.env.NODE_ENV === 'production' ? Environment.Production : Environment.Staging, apiKey: process.env.CE_API_KEY!, tokenStorage: new MemoryTokenStorage(), // For server-side token management timeout: 30000 // 30 second timeout }); export default storefront;
# .env STORE_ID=your_store_id CE_API_KEY=your_api_key NODE_ENV=development
import { MemoryTokenStorage } from '@commercengine/storefront-sdk'; const client = new StorefrontSDK({ storeId: process.env.STORE_ID!, apiKey: process.env.CE_API_KEY!, tokenStorage: new MemoryTokenStorage() });
import { TokenStorage } from '@commercengine/storefront-sdk'; class DatabaseTokenStorage implements TokenStorage { private userId: string; constructor(userId: string) { this.userId = userId; } async getAccessToken(): Promise<string | null> { const user = await User.findById(this.userId); return user?.accessToken || null; } async setAccessToken(token: string): Promise<void> { await User.findByIdAndUpdate(this.userId, { accessToken: token }); } async getRefreshToken(): Promise<string | null> { const user = await User.findById(this.userId); return user?.refreshToken || null; } async setRefreshToken(token: string): Promise<void> { await User.findByIdAndUpdate(this.userId, { refreshToken: token }); } async clearTokens(): Promise<void> { await User.findByIdAndUpdate(this.userId, { accessToken: null, refreshToken: null }); } } // Usage with user-specific tokens const userClient = new StorefrontSDK({ storeId: process.env.STORE_ID!, apiKey: process.env.CE_API_KEY!, tokenStorage: new DatabaseTokenStorage('user-123') });
import Redis from 'ioredis'; import { TokenStorage } from '@commercengine/storefront-sdk'; class RedisTokenStorage implements TokenStorage { private redis: Redis; private keyPrefix: string; constructor(redis: Redis, userId: string) { this.redis = redis; this.keyPrefix = `storefront:${userId}`; } async getAccessToken(): Promise<string | null> { return await this.redis.get(`${this.keyPrefix}:access_token`); } async setAccessToken(token: string): Promise<void> { await this.redis.setex(`${this.keyPrefix}:access_token`, 3600, token); // 1 hour expiry } async getRefreshToken(): Promise<string | null> { return await this.redis.get(`${this.keyPrefix}:refresh_token`); } async setRefreshToken(token: string): Promise<void> { await this.redis.setex(`${this.keyPrefix}:refresh_token`, 86400, token); // 24 hour expiry } async clearTokens(): Promise<void> { await this.redis.del( `${this.keyPrefix}:access_token`, `${this.keyPrefix}:refresh_token` ); } }
import express from 'express'; import storefront from './lib/storefront'; const app = express(); app.use(express.json()); app.get('/api/products', async (req, res) => { try { const { data, error } = await storefront.catalog.listProducts({ limit: parseInt(req.query.limit as string) || 20 }); if (error) { return res.status(400).json({ error: error.message }); } res.json(data); } catch (error) { res.status(500).json({ error: 'Internal server error' }); } }); app.listen(3000, () => { console.log('Server running on port 3000'); });
import { Request, Response, NextFunction } from 'express'; interface AuthenticatedRequest extends Request { userClient?: StorefrontSDK; userId?: string; } const authenticateUser = async (req: AuthenticatedRequest, res: Response, next: NextFunction) => { const authHeader = req.headers.authorization; if (!authHeader) { return res.status(401).json({ error: 'Authorization header required' }); } const token = authHeader.replace('Bearer ', ''); // Create user-specific SDK instance req.userClient = new StorefrontSDK({ storeId: process.env.STORE_ID!, apiKey: process.env.CE_API_KEY!, accessToken: token // Manual token management }); // Verify token by getting user info const { data: userData, error } = await req.userClient.auth.retrieveUser(); if (error) { return res.status(401).json({ error: 'Invalid token' }); } req.userId = userData?.content.id; next(); }; // Protected routes app.get('/api/cart', authenticateUser, async (req: AuthenticatedRequest, res) => { const { data, error } = await req.userClient!.cart.retrieveCartUsingUserId(req.userId!); if (error) { return res.status(400).json({ error: error.message }); } res.json(data?.content); });
import { Queue, Worker } from 'bullmq'; import storefront from './lib/storefront'; interface OrderProcessingJob { orderId: string; userId: string; } const orderQueue = new Queue<OrderProcessingJob>('order-processing'); const orderWorker = new Worker<OrderProcessingJob>('order-processing', async (job) => { const { orderId, userId } = job.data; try { // Get order details const orderResult = await storefront.order.retrieveOrderDetail(orderId); if (!orderResult.success) { throw new Error(`Failed to retrieve order: ${orderResult.error.message}`); } // Process order (send emails, update inventory, etc.) await processOrderFulfillment(orderResult.data); console.log(`Order ${orderId} processed successfully`); } catch (error) { console.error(`Failed to process order ${orderId}:`, error); throw error; // Re-throw to mark job as failed } }); // Add job to queue export async function queueOrderProcessing(orderId: string, userId: string) { await orderQueue.add('process-order', { orderId, userId }); }
app.post('/webhooks/order-created', async (req, res) => { const { order_id, user_id } = req.body; try { // Verify webhook signature here // Queue background processing await queueOrderProcessing(order_id, user_id); res.status(200).json({ message: 'Webhook processed' }); } catch (error) { console.error('Webhook processing failed:', error); res.status(500).json({ error: 'Webhook processing failed' }); } });
// order-service.ts export class OrderService { private storefront: StorefrontSDK; constructor() { this.storefront = new StorefrontSDK({ storeId: process.env.STORE_ID!, apiKey: process.env.CE_API_KEY!, tokenStorage: new MemoryTokenStorage() }); } async createOrder(userId: string, cartData: any) { const result = await this.storefront.order.createOrder({ user_id: userId, ...cartData }); if (result.success) { // Emit event for other services await this.eventBus.emit('order.created', { orderId: result.data.id, userId, amount: result.data.grand_total }); } return result; } }
app.get('/health', async (req, res) => { try { // Test SDK connectivity const result = await storefront.helpers.retrieveAllCountries(); if (result.success) { res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString(), sdk: 'connected' }); } else { res.status(503).json({ status: 'unhealthy', error: 'SDK connection failed' }); } } catch (error) { res.status(503).json({ status: 'unhealthy', error: error.message }); } });
import { ResponseUtils } from '@commercengine/storefront-sdk'; app.use((error: any, req: Request, res: Response, next: NextFunction) => { console.error('Unhandled error:', error); // Handle SDK-specific errors if (error.response && ResponseUtils.isSuccess) { const metadata = ResponseUtils.getMetadata(error.response); return res.status(metadata.status).json({ error: 'API request failed', details: metadata }); } // Generic error response res.status(500).json({ error: 'Internal server error', requestId: req.headers['x-request-id'] }); });
process.on('SIGTERM', async () => { console.log('SIGTERM received, shutting down gracefully'); // Close database connections, stop workers, etc. await orderWorker.close(); await redis.disconnect(); process.exit(0); });
const client = new StorefrontSDK({ storeId: process.env.STORE_ID!, apiKey: process.env.CE_API_KEY!, timeout: 10000, // 10 second timeout for backend operations debug: process.env.NODE_ENV === 'development' });
// Monitor SDK performance const trackAPIPerformance = async (operation: string, fn: () => Promise<any>) => { const start = process.hrtime.bigint(); try { const result = await fn(); const duration = Number(process.hrtime.bigint() - start) / 1000000; // Convert to ms console.log(`SDK operation ${operation} completed in ${duration.toFixed(2)}ms`); return result; } catch (error) { const duration = Number(process.hrtime.bigint() - start) / 1000000; console.error(`SDK operation ${operation} failed after ${duration.toFixed(2)}ms:`, error); throw error; } }; // Usage const products = await trackAPIPerformance('listProducts', () => storefront.catalog.listProducts({ limit: 20 }) );