The SDK works excellently in Node.js applications for backend services, APIs, and server-side e-commerce operations.
Installation
Install the SDK in your Node.js project:
npm install @commercengine/storefront-sdk
# or
pnpm install @commercengine/storefront-sdk
Basic Setup
Configure the SDK for server-side usage:
// 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 ;
Environment Variables
Set up your environment configuration:
# .env
STORE_ID = your_store_id
CE_API_KEY = your_api_key
NODE_ENV = development
Token Management Strategies
Memory Storage (Default)
For simple server applications or stateless operations:
import { MemoryTokenStorage } from '@commercengine/storefront-sdk' ;
const client = new StorefrontSDK ({
storeId: process . env . STORE_ID ! ,
apiKey: process . env . CE_API_KEY ! ,
tokenStorage: new MemoryTokenStorage ()
});
Database Integration
For persistent token storage across server restarts:
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' )
});
Redis Integration
For distributed systems with shared token storage:
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`
);
}
}
Express.js Integration
Basic Express Setup
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' );
});
Middleware for User Context
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 );
});
Background Jobs
Order Processing
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 });
}
Webhook Handlers
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' });
}
});
Microservices Architecture
Service-to-Service Communication
// 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 ;
}
}
Health Checks
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
});
}
});
Error Handling
Global Error Handler
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' ]
});
});
Graceful Shutdown
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 );
});
Request Timeouts
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'
});
Connection Monitoring
// 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 })
);
Best Practices
Environment Configuration Use environment variables for all configuration and never hardcode sensitive values
Error Handling Implement comprehensive error handling for all SDK operations with appropriate logging
Token Storage Choose appropriate token storage based on your architecture (memory, database, Redis)
Health Monitoring Implement health checks and monitoring to ensure SDK connectivity and performance
Cross-References
The SDK is optimized for server-side usage with proper timeout handling and token management. Choose your token storage strategy based on your application architecture and scaling requirements.