New to StateSet? Start with our 5-minute quickstart to understand the basics before diving into SDK installation.
🚀 Quick Start
Choose your language and get started in under 5 minutes:npm install stateset-node
pip install stateset-python
📦 Supported SDKs
StateSet provides official SDKs for the following languages:Node.js / TypeScript
Most Popular
Full TypeScript support with async/await
Python
AI/ML Ready
Perfect for data science and backend APIs
🎯 Which SDK Should I Use?
I'm building a modern web application
I'm building a modern web application
Recommended: Node.js/TypeScript SDK
- ✅ Best for React, Next.js, Vue, Angular
- ✅ Full TypeScript support
- ✅ Excellent async/await handling
- ✅ Largest community and ecosystem
I'm building a data-heavy or AI/ML application
I'm building a data-heavy or AI/ML application
Recommended: Python SDK
- ✅ Best for data processing and analytics
- ✅ Native pandas/numpy integration
- ✅ Perfect for Jupyter notebooks
- ✅ Great for FastAPI/Django backends
Node.js SDK
Prerequisites
1
Check Node.js Version
node --version
# Should output v16.0.0 or higher
Node.js 16+ is required. For older versions, use
stateset-node@legacy2
Verify Package Manager
npm --version # Should be 7.0.0+
# OR
yarn --version # Should be 1.22.0+
# OR
pnpm --version # Should be 6.0.0+
3
Optional: TypeScript Setup
tsc --version
# Should output Version 4.5.0 or higher
Installation
- npm
- yarn
- pnpm
# Install the main SDK
npm install stateset-node
# Install type definitions (if using TypeScript)
npm install --save-dev @types/node
# Install recommended utilities
npm install dotenv winston axios-retry
# Install the main SDK
yarn add stateset-node
# Install type definitions (if using TypeScript)
yarn add --dev @types/node
# Install recommended utilities
yarn add dotenv winston axios-retry
# Install the main SDK
pnpm add stateset-node
# Install type definitions (if using TypeScript)
pnpm add --save-dev @types/node
# Install recommended utilities
pnpm add dotenv winston axios-retry
Quick Setup
1
Create Project Structure
mkdir my-Stateset-app && cd my-Stateset-app
npm init -y
npm install stateset-node dotenv
# Create project structure
mkdir -p src/{services,utils,config}
touch .env .env.example .gitignore
2
Configure Environment Variables
# .env
STATESET_API_KEY=sk_live_your_actual_key_here
STATESET_ENVIRONMENT=production
STATESET_WEBHOOK_SECRET=whsec_3rK9pL7nQ2xS5mT8...
STATESET_LOG_LEVEL=info
STATESET_TIMEOUT=30000
STATESET_MAX_RETRIES=3
# .env.example (commit this to git)
STATESET_API_KEY=your_api_key_here
STATESET_ENVIRONMENT=sandbox
STATESET_WEBHOOK_SECRET=your_webhook_secret_here
STATESET_LOG_LEVEL=info
STATESET_TIMEOUT=30000
STATESET_MAX_RETRIES=3
# .gitignore
node_modules/
.env
.env.local
dist/
*.log
3
Create Configuration Module
// src/config/StateSet.js
import { StatesetClient } from 'stateset-node';
import dotenv from 'dotenv';
dotenv.config();
// Validate required environment variables
const requiredEnvVars = ['STATESET_API_KEY'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
// Create and export configured client
export const Stateset = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
environment: process.env.STATESET_ENVIRONMENT || 'sandbox',
timeout: parseInt(process.env.STATESET_TIMEOUT || '30000'),
maxRetries: parseInt(process.env.STATESET_MAX_RETRIES || '3'),
telemetry: true // Help us improve the SDK
});
// Export configuration for reference
export const config = {
environment: process.env.STATESET_ENVIRONMENT || 'sandbox',
logLevel: process.env.STATESET_LOG_LEVEL || 'info',
webhookSecret: process.env.STATESET_WEBHOOK_SECRET
};
Basic Configuration
- JavaScript (ES6)
- TypeScript
- CommonJS
// app.js
import { Stateset, config } from './src/config/StateSet.js';
// Test the connection with proper error handling
async function testConnection() {
try {
const health = await Stateset.health.check();
logger.info('✅ Connected to Stateset:', {
status: health.status,
environment: config.environment,
version: health.version
});
// Test basic API access
const { data: orders } = await Stateset.orders.list({ limit: 1 });
logger.info('✅ API Access verified:', orders.length, 'orders found');
} catch (error) {
logger.error('❌ Connection failed:', {
message: error.message,
code: error.code,
statusCode: error.statusCode
});
process.exit(1);
}
}
// Run tests
testConnection();
// app.ts
import { StatesetClient, StatesetConfig, StatesetError } from 'stateset-node';
import dotenv from 'dotenv';
dotenv.config();
// Type-safe configuration
interface AppConfig {
Stateset: StatesetConfig;
app: {
environment: 'development' | 'staging' | 'production';
logLevel: 'debug' | 'info' | 'warn' | 'error';
};
}
const config: AppConfig = {
Stateset: {
apiKey: process.env.STATESET_API_KEY!,
environment: (process.env.STATESET_ENVIRONMENT as 'sandbox' | 'production') || 'sandbox',
timeout: parseInt(process.env.STATESET_TIMEOUT || '30000'),
maxRetries: parseInt(process.env.STATESET_MAX_RETRIES || '3'),
telemetry: true
},
app: {
environment: (process.env.NODE_ENV as any) || 'development',
logLevel: (process.env.LOG_LEVEL as any) || 'info'
}
};
const client = new StatesetClient(config.StateSet);
// Type-safe error handling
async function testConnection(): Promise<void> {
try {
const health = await fetch('https://api.stateset.com/api/v1/health').then(res => res.json());
logger.info('✅ Connected to Stateset:', health);
// Test with proper types
const { data: orders } = await client.orders.list({
limit: 1,
status: 'pending'
});
logger.info(`✅ Found ${orders.length} orders`);
} catch (error) {
if (error instanceof StatesetError) {
logger.error('Stateset API Error:', {
message: error.message,
code: error.code,
statusCode: error.statusCode,
requestId: error.requestId
});
} else {
logger.error('Unexpected error:', error);
}
process.exit(1);
}
}
testConnection();
// app.js
const { StatesetClient } = require('stateset-node');
require('dotenv').config();
// Create client with error handling
let client;
try {
client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
environment: process.env.STATESET_ENVIRONMENT || 'sandbox',
timeout: 30000,
maxRetries: 3
});
} catch (error) {
logger.error('Failed to initialize Stateset client:', error.message);
process.exit(1);
}
// Test the connection
fetch('https://api.stateset.com/api/v1/health')
.then(res => res.json())
.then(health => {
logger.info('✅ Connected to Stateset:', health.status);
return client.orders.list({ limit: 1 });
})
.then(({ data: orders }) => {
logger.info('✅ API Access verified:', orders.length, 'orders found');
})
.catch(error => {
logger.error('❌ Connection failed:', error.message);
process.exit(1);
});
Advanced Configuration
- Custom HTTP Client
- Proxy Configuration
- Custom Logger
import { StatesetClient } from 'stateset-node';
import axios from 'axios';
import axiosRetry from 'axios-retry';
// Create custom axios instance
const httpClient = axios.create({
timeout: 60000,
headers: {
'User-Agent': 'MyApp/1.0.0'
}
});
// Configure retry logic
axiosRetry(httpClient, {
retries: 5,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: (error) => {
return axiosRetry.isNetworkOrIdempotentRequestError(error) ||
error.response?.status === 429; // Retry on rate limit
}
});
// Use custom HTTP client
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
httpClient: httpClient
});
import { StatesetClient } from 'stateset-node';
import { HttpsProxyAgent } from 'https-proxy-agent';
const proxyAgent = new HttpsProxyAgent('http://proxy.company.com:8080');
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
httpAgent: proxyAgent,
httpsAgent: proxyAgent
});
import { StatesetClient } from 'stateset-node';
import winston from 'winston';
// Create Winston logger
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.Console({
format: winston.format.simple()
}),
new winston.transports.File({
filename: 'Stateset-errors.log',
level: 'error'
})
]
});
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
logger: logger
});
Framework Integration
- Express.js
- Next.js
- NestJS
// server.js
import express from 'express';
import { StatesetClient } from 'stateset-node';
import morgan from 'morgan';
import helmet from 'helmet';
const app = express();
// Security and logging middleware
app.use(helmet());
app.use(morgan('combined'));
app.use(express.json());
// Initialize Stateset client
const Stateset = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
environment: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox'
});
// Stateset middleware
app.use((req, res, next) => {
req.stateset = Stateset;
next();
});
// Error handling middleware
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Routes
app.get('/api/orders', asyncHandler(async (req, res) => {
const { page = 1, limit = 20, status } = req.query;
const { data: orders, pagination } = await req.stateset.orders.list({
page: parseInt(page),
limit: parseInt(limit),
status: status
});
res.json({
success: true,
data: orders,
pagination
});
}));
app.post('/api/orders', asyncHandler(async (req, res) => {
const order = await req.stateset.orders.create({
customer_id: req.body.customer_id,
items: req.body.items,
shipping_address: req.body.shipping_address
});
res.status(201).json({
success: true,
data: order
});
}));
// Global error handler
app.use((err, req, res, next) => {
logger.error('Error:', err);
if (err.name === 'StatesetError') {
return res.status(err.statusCode || 400).json({
success: false,
error: {
message: err.message,
code: err.code,
requestId: err.requestId
}
});
}
res.status(500).json({
success: false,
error: {
message: 'Internal server error'
}
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info(`Server running on port ${PORT}`);
});
// lib/StateSet.ts
import { StatesetClient } from 'stateset-node';
// Singleton pattern for client-side usage
let client: StatesetClient;
export function getStatesetClient() {
if (!client) {
client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY!,
environment: process.env.NEXT_PUBLIC_STATESET_ENV as 'sandbox' | 'production' || 'sandbox'
});
}
return client;
}
// Server-side client (for API routes)
export const serverClient = new StatesetClient({
apiKey: process.env.STATESET_API_KEY!,
environment: process.env.STATESET_ENVIRONMENT as 'sandbox' | 'production' || 'sandbox'
});
// Type-safe API wrapper
export async function apiWrapper<T>(
apiCall: () => Promise<T>
): Promise<{ data?: T; error?: string }> {
try {
const data = await apiCall();
return { data };
} catch (error: any) {
logger.error('Stateset API Error:', error);
return {
error: error.message || 'An unexpected error occurred'
};
}
}
// pages/api/orders/index.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { serverClient, apiWrapper } from '../../../lib/StateSet';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Enable CORS if needed
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
switch (req.method) {
case 'GET': {
const { page = '1', limit = '20', status } = req.query;
const result = await apiWrapper(() =>
serverClient.orders.list({
page: parseInt(page as string),
limit: parseInt(limit as string),
status: status as string
})
);
if (result.error) {
return res.status(400).json({ error: result.error });
}
return res.status(200).json(result.data);
}
case 'POST': {
const result = await apiWrapper(() =>
serverClient.orders.create(req.body)
);
if (result.error) {
return res.status(400).json({ error: result.error });
}
return res.status(201).json(result.data);
}
default:
res.setHeader('Allow', ['GET', 'POST']);
return res.status(405).json({
error: `Method ${req.method} Not Allowed`
});
}
}
// hooks/useStateset.ts (React Hook)
import { useState, useEffect } from 'react';
import { getStatesetClient } from '../lib/StateSet';
export function useOrders(options = {}) {
const [orders, setOrders] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchOrders = async () => {
try {
const client = getStatesetClient();
const { data } = await client.orders.list(options);
setOrders(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchOrders();
}, []);
return { orders, loading, error };
}
// Stateset/StateSet.module.ts
import { Module, DynamicModule, Global } from '@nestjs/common';
import { StatesetClient } from 'stateset-node';
import { STATESET_CLIENT } from './StateSet.constants';
import { StatesetService } from './StateSet.service';
export interface StatesetModuleOptions {
apiKey: string;
environment?: 'sandbox' | 'production';
isGlobal?: boolean;
}
@Module({})
export class StatesetModule {
static forRoot(options: StatesetModuleOptions): DynamicModule {
const providers = [
{
provide: STATESET_CLIENT,
useFactory: () => new StatesetClient({
apiKey: options.apiKey,
environment: options.environment || 'sandbox'
})
},
StatesetService
];
return {
module: StatesetModule,
global: options.isGlobal ?? true,
providers,
exports: [STATESET_CLIENT, StatesetService]
};
}
static forRootAsync(options: {
useFactory: (...args: any[]) => Promise<StatesetModuleOptions> | StatesetModuleOptions;
inject?: any[];
isGlobal?: boolean;
}): DynamicModule {
const providers = [
{
provide: STATESET_CLIENT,
useFactory: async (...args: any[]) => {
const config = await options.useFactory(...args);
return new StatesetClient({
apiKey: config.apiKey,
environment: config.environment || 'sandbox'
});
},
inject: options.inject || []
},
StatesetService
];
return {
module: StatesetModule,
global: options.isGlobal ?? true,
providers,
exports: [STATESET_CLIENT, StatesetService]
};
}
}
// Stateset/StateSet.service.ts
import { Injectable, Inject, Logger } from '@nestjs/common';
import { StatesetClient } from 'stateset-node';
import { STATESET_CLIENT } from './StateSet.constants';
@Injectable()
export class StatesetService {
private readonly logger = new Logger(StatesetService.name);
constructor(
@Inject(STATESET_CLIENT) private readonly client: StatesetClient
) {}
async createOrder(data: any) {
try {
this.logger.log('Creating order', { customerId: data.customer_id });
const order = await this.client.orders.create(data);
this.logger.log('Order created successfully', { orderId: order.id });
return order;
} catch (error) {
this.logger.error('Failed to create order', error);
throw error;
}
}
async getOrders(filters = {}) {
return this.client.orders.list(filters);
}
async getOrder(id: string) {
return this.client.orders.get(id);
}
async updateOrder(id: string, data: any) {
return this.client.orders.update(id, data);
}
}
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { StatesetModule } from './StateSet/StateSet.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true
}),
StatesetModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
apiKey: config.get('STATESET_API_KEY'),
environment: config.get('STATESET_ENVIRONMENT', 'sandbox')
})
})
]
})
export class AppModule {}
// orders/orders.controller.ts
import {
Controller,
Get,
Post,
Body,
Param,
Query,
UseInterceptors,
ClassSerializerInterceptor
} from '@nestjs/common';
import { StatesetService } from '../StateSet/StateSet.service';
@Controller('orders')
@UseInterceptors(ClassSerializerInterceptor)
export class OrdersController {
constructor(private readonly StatesetService: StatesetService) {}
@Get()
async getOrders(
@Query('page') page = 1,
@Query('limit') limit = 20,
@Query('status') status?: string
) {
return this.StateSetService.getOrders({
page: Number(page),
limit: Number(limit),
status
});
}
@Get(':id')
async getOrder(@Param('id') id: string) {
return this.StateSetService.getOrder(id);
}
@Post()
async createOrder(@Body() createOrderDto: any) {
return this.StateSetService.createOrder(createOrderDto);
}
}
Python SDK
Prerequisites
1
Check Python Version
python --version
# Should output Python 3.8.0 or higher
# Check pip version
pip --version
# Should be version 20.0 or higher
Python 3.8+ is required. For older versions, use
stateset-python==1.x2
Set Up Virtual Environment
# Create virtual environment
python -m venv Stateset-env
# Activate on macOS/Linux
source Stateset-env/bin/activate
# Activate on Windows
Stateset-env\Scripts\activate
# Create conda environment
conda create -n Stateset-env python=3.9
conda activate Stateset-env
# Initialize poetry project
poetry new my-Stateset-app
cd my-Stateset-app
Installation
- pip
- poetry
- conda
- requirements.txt
# Install Stateset SDK with all optional dependencies
pip install "stateset-python[all]"
# Or install core SDK only
pip install stateset-python
# Install specific extras
pip install "stateset-python[async]" # For async support
pip install "stateset-python[pandas]" # For data analysis
# Add Stateset SDK
poetry add stateset-python
# Add with extras
poetry add "stateset-python[async,pandas]"
# Add development dependencies
poetry add --group dev pytest pytest-asyncio black mypy
# Install via pip in conda environment
pip install stateset-python
# Install conda dependencies first
conda install pandas numpy requests
pip install "stateset-python[async]"
# requirements.txt
stateset-python>=2.0.0
python-dotenv>=0.19.0
requests>=2.28.0
# Optional: async support
httpx>=0.23.0
# Optional: data analysis
pandas>=1.3.0
numpy>=1.21.0
pip install -r requirements.txt
Quick Setup
1
Create Project Structure
# Create project structure
mkdir my-Stateset-app && cd my-Stateset-app
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install stateset-python python-dotenv
# Create project files
mkdir -p src/{services,utils,config}
touch .env .env.example .gitignore README.md
touch src/__init__.py src/config/__init__.py
2
Configure Environment
# .env
STATESET_API_KEY=sk_live_your_actual_key_here
STATESET_ENVIRONMENT=production
STATESET_WEBHOOK_SECRET=whsec_3rK9pL7nQ2xS5mT8...
STATESET_LOG_LEVEL=INFO
STATESET_TIMEOUT=30
STATESET_MAX_RETRIES=3
# .gitignore
__pycache__/
*.py[cod]
*$py.class
.env
.venv/
venv/
.pytest_cache/
.mypy_cache/
*.log
3
Create Configuration Module
# src/config/StateSet.py
import os
import logging
from typing import Optional
from stateset import Stateset, AsyncStateset
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Configure logging
logging.basicConfig(
level=getattr(logging, os.getenv('STATESET_LOG_LEVEL', 'INFO')),
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Validate required environment variables
REQUIRED_ENV_VARS = ['STATESET_API_KEY']
for var in REQUIRED_ENV_VARS:
if not os.getenv(var):
raise ValueError(f"Missing required environment variable: {var}")
# Configuration
class Config:
API_KEY = os.getenv('STATESET_API_KEY')
ENVIRONMENT = os.getenv('STATESET_ENVIRONMENT', 'sandbox')
WEBHOOK_SECRET = os.getenv('STATESET_WEBHOOK_SECRET')
TIMEOUT = int(os.getenv('STATESET_TIMEOUT', '30'))
MAX_RETRIES = int(os.getenv('STATESET_MAX_RETRIES', '3'))
# Create client instances
def get_client() -> Stateset:
"""Get configured Stateset client"""
return Stateset(
api_key=Config.API_KEY,
environment=Config.ENVIRONMENT,
timeout=Config.TIMEOUT,
max_retries=Config.MAX_RETRIES
)
def get_async_client() -> AsyncStateset:
"""Get configured async Stateset client"""
return AsyncStateset(
api_key=Config.API_KEY,
environment=Config.ENVIRONMENT,
timeout=Config.TIMEOUT,
max_retries=Config.MAX_RETRIES
)
# Export configured client
client = get_client()
async_client = get_async_client()
Basic Configuration
- Synchronous
- Asynchronous
- Type Hints
# app.py
import requests
from src.config.StateSet import client, Config, logger
def test_connection():
"""Test Stateset connection and basic API access"""
try:
# Test health check (REST -- the SDK has no health resource)
health = requests.get("https://api.stateset.com/api/v1/health").json()
logger.info(f"✅ Connected to Stateset: {health}")
# Test API access
orders = client.orders.list(limit=1)
logger.info(f"✅ API Access verified: {len(orders.data)} orders found")
# Display configuration
logger.info(f"Environment: {Config.ENVIRONMENT}")
logger.info(f"Timeout: {Config.TIMEOUT}s")
return True
except Exception as e:
logger.error(f"❌ Connection failed: {str(e)}")
if hasattr(e, 'status_code'):
logger.error(f"Status Code: {e.status_code}")
if hasattr(e, 'request_id'):
logger.error(f"Request ID: {e.request_id}")
return False
if __name__ == "__main__":
if test_connection():
logger.info("Stateset SDK is properly configured!")
else:
exit(1)
# app_async.py
import asyncio
from src.config.StateSet import async_client, Config, logger
async def test_connection():
"""Test Stateset async connection"""
try:
# Test health check
health = await async_client.health.check()
logger.info(f"✅ Connected to Stateset: {health}")
# Test parallel API calls
tasks = [
async_client.orders.list(limit=5),
async_client.customers.list(limit=5),
async_client.products.list(limit=5)
]
results = await asyncio.gather(*tasks, return_exceptions=True)
for i, result in enumerate(results):
if isinstance(result, Exception):
logger.error(f"Task {i} failed: {result}")
else:
logger.info(f"Task {i} succeeded: {len(result.data)} items")
return True
except Exception as e:
logger.error(f"❌ Connection failed: {str(e)}")
return False
finally:
await async_client.close()
if __name__ == "__main__":
asyncio.run(test_connection())
# app_typed.py
from typing import List, Optional, Dict, Any
from dataclasses import dataclass
from src.config.StateSet import client, logger
from stateset.models import Order, Customer, Product
@dataclass
class OrderService:
"""Type-safe order service"""
client: Any
def get_orders(
self,
status: Optional[str] = None,
limit: int = 20
) -> List[Order]:
"""Get orders with optional filtering"""
try:
response = self.client.orders.list(
status=status,
limit=limit
)
return response.data
except Exception as e:
logger.error(f"Failed to fetch orders: {e}")
raise
def create_order(self, order_data: Dict[str, Any]) -> Order:
"""Create a new order"""
try:
return self.client.orders.create(**order_data)
except Exception as e:
logger.error(f"Failed to create order: {e}")
raise
def get_order_summary(self, order_id: str) -> Dict[str, Any]:
"""Get comprehensive order summary"""
order = self.client.orders.get(order_id)
return {
"order": order,
"customer_id": order.customer_id,
"total_amount": order.total,
"status": order.status
}
# Usage
service = OrderService(client=client)
orders = service.get_orders(status="pending", limit=10)
print(f"Found {len(orders)} pending orders")
Framework Integration
- Flask
- FastAPI
- Django
# app.py
from flask import Flask, jsonify, request, g
from functools import wraps
from src.config.StateSet import client, Config, logger
import requests
import traceback
app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False
# Error handling decorator
def handle_errors(f):
@wraps(f)
def decorated_function(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
logger.error(f"Error in {f.__name__}: {str(e)}")
logger.error(traceback.format_exc())
if hasattr(e, 'status_code'):
status_code = e.status_code
else:
status_code = 500
return jsonify({
'success': False,
'error': {
'message': str(e),
'type': type(e).__name__
}
}), status_code
return decorated_function
# Routes
@app.route('/api/orders', methods=['GET'])
@handle_errors
def get_orders():
page = int(request.args.get('page', 1))
limit = int(request.args.get('limit', 20))
status = request.args.get('status')
orders = client.orders.list(
page=page,
limit=limit,
status=status
)
return jsonify({
'success': True,
'data': orders.data,
'pagination': {
'page': orders.page,
'limit': orders.limit,
'total': orders.total
}
})
@app.route('/api/orders', methods=['POST'])
@handle_errors
def create_order():
data = request.get_json()
# Validate required fields
required_fields = ['customer_id', 'items']
for field in required_fields:
if field not in data:
return jsonify({
'success': False,
'error': f'Missing required field: {field}'
}), 400
order = client.orders.create(**data)
return jsonify({
'success': True,
'data': order
}), 201
@app.route('/api/orders/<order_id>', methods=['GET'])
@handle_errors
def get_order(order_id):
order = client.orders.get(order_id)
return jsonify({
'success': True,
'data': order
})
@app.route('/api/health', methods=['GET'])
def health_check():
try:
health = requests.get("https://api.stateset.com/api/v1/health").json()
return jsonify({
'status': 'healthy',
'Stateset': health,
'environment': Config.ENVIRONMENT
})
except Exception as e:
return jsonify({
'status': 'unhealthy',
'error': str(e)
}), 503
if __name__ == '__main__':
app.run(debug=True, port=5000)
# main.py
from fastapi import FastAPI, HTTPException, Query, Depends
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any
from src.config.StateSet import async_client, Config, logger
import httpx
import uvicorn
app = FastAPI(title="Stateset API", version="1.0.0")
# Pydantic models
class OrderCreate(BaseModel):
customer_id: str
items: List[Dict[str, Any]]
shipping_address: Dict[str, str]
metadata: Optional[Dict[str, Any]] = None
class OrderResponse(BaseModel):
id: str
customer_id: str
status: str
total: float
created_at: str
class PaginatedResponse(BaseModel):
data: List[Any]
pagination: Dict[str, int]
# Dependency for Stateset client
async def get_Stateset_client():
return async_client
# Exception handler
@app.exception_handler(Exception)
async def Stateset_exception_handler(request, exc):
logger.error(f"Stateset error: {exc}")
if hasattr(exc, 'status_code'):
status_code = exc.status_code
else:
status_code = 500
return JSONResponse(
status_code=status_code,
content={
"detail": str(exc),
"type": type(exc).__name__
}
)
# Routes
@app.get("/api/orders", response_model=PaginatedResponse)
async def get_orders(
page: int = Query(1, ge=1),
limit: int = Query(20, ge=1, le=100),
status: Optional[str] = None,
client = Depends(get_Stateset_client)
):
"""Get paginated list of orders"""
response = await client.orders.list(
page=page,
limit=limit,
status=status
)
return {
"data": response.data,
"pagination": {
"page": response.page,
"limit": response.limit,
"total": response.total
}
}
@app.post("/api/orders", response_model=OrderResponse, status_code=201)
async def create_order(
order: OrderCreate,
client = Depends(get_Stateset_client)
):
"""Create a new order"""
result = await client.orders.create(**order.dict())
return result
@app.get("/api/orders/{order_id}", response_model=OrderResponse)
async def get_order(
order_id: str,
client = Depends(get_Stateset_client)
):
"""Get order by ID"""
order = await client.orders.get(order_id)
if not order:
raise HTTPException(status_code=404, detail="Order not found")
return order
@app.get("/api/health")
async def health_check(client = Depends(get_Stateset_client)):
"""Health check endpoint"""
try:
async with httpx.AsyncClient() as http:
health = (await http.get("https://api.stateset.com/api/v1/health")).json()
return {
"status": "healthy",
"Stateset": health,
"environment": Config.ENVIRONMENT
}
except Exception as e:
raise HTTPException(status_code=503, detail=str(e))
# Startup/shutdown events
@app.on_event("startup")
async def startup_event():
logger.info("Starting up Stateset FastAPI app")
@app.on_event("shutdown")
async def shutdown_event():
logger.info("Shutting down Stateset FastAPI app")
await async_client.close()
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
# settings.py
import os
from dotenv import load_dotenv
load_dotenv()
# Stateset Configuration
STATESET_API_KEY = os.getenv('STATESET_API_KEY')
STATESET_ENVIRONMENT = os.getenv('STATESET_ENVIRONMENT', 'sandbox')
STATESET_WEBHOOK_SECRET = os.getenv('STATESET_WEBHOOK_SECRET')
# Stateset_client.py
from django.conf import settings
from stateset import Stateset
import logging
logger = logging.getLogger(__name__)
class StatesetService:
"""Singleton Stateset service for Django"""
_instance = None
_client = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
@property
def client(self):
if self._client is None:
self._client = Stateset(
api_key=settings.STATESET_API_KEY,
environment=settings.STATESET_ENVIRONMENT
)
return self._client
# views.py
from django.http import JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from .StateSet_client import StatesetService
import json
Stateset_service = StatesetService()
@method_decorator(csrf_exempt, name='dispatch')
class OrdersView(View):
def get(self, request):
try:
page = int(request.GET.get('page', 1))
limit = int(request.GET.get('limit', 20))
status = request.GET.get('status')
response = Stateset_service.client.orders.list(
page=page,
limit=limit,
status=status
)
return JsonResponse({
'success': True,
'data': response.data,
'pagination': {
'page': response.page,
'limit': response.limit,
'total': response.total
}
})
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
}, status=500)
def post(self, request):
try:
data = json.loads(request.body)
order = Stateset_service.client.orders.create(**data)
return JsonResponse({
'success': True,
'data': order
}, status=201)
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
}, status=400)
class OrderDetailView(View):
def get(self, request, order_id):
try:
order = Stateset_service.client.orders.get(order_id)
return JsonResponse({
'success': True,
'data': order
})
except Exception as e:
status_code = 404 if 'not found' in str(e).lower() else 500
return JsonResponse({
'success': False,
'error': str(e)
}, status=status_code)
# urls.py
from django.urls import path
from .views import OrdersView, OrderDetailView
urlpatterns = [
path('api/orders/', OrdersView.as_view(), name='orders'),
path('api/orders/<str:order_id>/', OrderDetailView.as_view(), name='order-detail'),
]
Environment Configuration
Development Environment
# .env.development
STATESET_API_KEY=sk_test_your_actual_key_here
STATESET_ENVIRONMENT=sandbox
STATESET_WEBHOOK_SECRET=whsec_test_3rK9pL7nQ2xS5mT8...
LOG_LEVEL=debug
Production Environment
# .env.production
STATESET_API_KEY=sk_live_your_actual_key_here
STATESET_ENVIRONMENT=production
STATESET_WEBHOOK_SECRET=whsec_prod_3rK9pL7nQ2xS5mT8...
LOG_LEVEL=info
Docker Configuration
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- STATESET_API_KEY=${STATESET_API_KEY}
- STATESET_ENVIRONMENT=production
volumes:
- .:/app
- /app/node_modules
Troubleshooting
Common Issues
Authentication Errors
Authentication Errors
Problem: Code fix:
401 Unauthorized errorsSolutions:# 1. Verify API key format
echo $STATESET_API_KEY
# Should start with sk_test_ or sk_live_
# 2. Check environment variables are loaded
node -e "logger.info(process.env.STATESET_API_KEY);"
# 3. Verify API key permissions in dashboard
# https://app.stateset.com/settings/api-keys
# 4. Ensure correct environment
# sandbox keys: sk_test_*
# production keys: sk_live_*
// Ensure environment variables are loaded
import dotenv from 'dotenv';
dotenv.config({ path: '.env.local' }); // Try different path
// Debug API key
logger.info('API key exists:', !!process.env.STATESET_API_KEY);
logger.info('API key prefix:', process.env.STATESET_API_KEY?.substring(0, 7));
Network Timeouts
Network Timeouts
Problem: Requests timing out or
ETIMEDOUT errorsSolutions:// 1. Increase timeout
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
timeout: 60000, // 60 seconds
maxRetries: 5
});
// 2. Check network connectivity
// Run: curl https://api.stateset.com/api/v1/health
// 3. Configure proxy if behind firewall
import { HttpsProxyAgent } from 'https-proxy-agent';
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
httpAgent: new HttpsProxyAgent(process.env.HTTP_PROXY)
});
// 4. Implement custom retry logic
async function retryableRequest(fn, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
}
}
}
Module Import Errors
Module Import Errors
Problem: Cannot import StateSet modules or TypeScript errorsSolutions:
# 1. Clear cache and reinstall
rm -rf node_modules package-lock.json
npm cache clean --force
npm install
# 2. Check Node.js version
node --version # Must be 16.0.0+
# 3. Verify installation
npm list stateset-node
- Fix TypeScript module resolution in
tsconfig.json:
{
"compilerOptions": {
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}
- For ESM issues, add to
package.json:
{
"type": "module"
}
Rate Limiting
Rate Limiting
Problem:
429 Too Many Requests errorsSolutions:// 1. Implement exponential backoff
import pRetry from 'p-retry';
const createOrderWithRetry = async (data) => {
return pRetry(
() => client.orders.create(data),
{
retries: 5,
onFailedAttempt: error => {
if (error.statusCode === 429) {
logger.info(`Rate limited, retrying in ${error.retriesLeft * 1000}ms`);
}
}
}
);
};
// 2. Implement request queuing
import PQueue from 'p-queue';
const queue = new PQueue({
concurrency: 2, // Max 2 concurrent requests
interval: 1000, // Per second
intervalCap: 10 // Max 10 requests per second
});
// 3. Cache frequently accessed data
const cache = new Map();
async function getCachedOrder(id) {
if (cache.has(id)) {
return cache.get(id);
}
const order = await client.orders.get(id);
cache.set(id, order);
return order;
}
Webhook Signature Verification
Webhook Signature Verification
Problem: Webhook signature verification failingSolutions:
// 1. Ensure raw body is used
app.use('/webhooks/StateSet', express.raw({ type: 'application/json' }));
// 2. Correct signature verification
app.post('/webhooks/StateSet', (req, res) => {
const sig = req.headers['Stateset-signature'];
const body = req.body; // Must be raw Buffer
try {
const event = Stateset.webhooks.constructEvent(
body,
sig,
process.env.STATESET_WEBHOOK_SECRET
);
// Process event
res.json({ received: true });
} catch (err) {
logger.error('Webhook Error:', err.message);
res.status(400).send(`Webhook Error: ${err.message}`);
}
});
// 3. Debug webhook secret
logger.info('Webhook secret exists:', !!process.env.STATESET_WEBHOOK_SECRET);
logger.info('Secret prefix:', process.env.STATESET_WEBHOOK_SECRET?.substring(0, 7));
Platform-Specific Issues
- Vercel
- AWS Lambda
- Docker
// vercel.json
{
"functions": {
"api/webhooks/StateSet.js": {
"maxDuration": 30
}
},
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" }
]
}
]
}
// api/webhooks/StateSet.js
export const config = {
api: {
bodyParser: false, // Important for raw body
},
};
// serverless.yml
functions:
webhook:
handler: handler.webhook
events:
- http:
path: webhooks/StateSet
method: post
cors: true
environment:
STATESET_API_KEY: ${env:STATESET_API_KEY}
STATESET_WEBHOOK_SECRET: ${env:STATESET_WEBHOOK_SECRET}
// handler.js
const getRawBody = require('raw-body');
exports.webhook = async (event) => {
const body = Buffer.from(event.body, 'base64');
const sig = event.headers['Stateset-signature'];
try {
const webhookEvent = Stateset.webhooks.constructEvent(
body,
sig,
process.env.STATESET_WEBHOOK_SECRET
);
return {
statusCode: 200,
body: JSON.stringify({ received: true })
};
} catch (err) {
return {
statusCode: 400,
body: JSON.stringify({ error: err.message })
};
}
};
# Dockerfile debugging
FROM node:18-alpine
# Install debugging tools
RUN apk add --no-cache curl openssl
WORKDIR /app
# Copy and install dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy application
COPY . .
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('./health-check.js')" || exit 1
EXPOSE 3000
CMD ["node", "index.js"]
# docker-compose.yml with debugging
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- STATESET_API_KEY=${STATESET_API_KEY}
- DEBUG=Stateset:*
volumes:
- ./logs:/app/logs
command: >
sh -c "
echo 'Testing Stateset connection...' &&
node test-connection.js &&
node index.js
"
Testing Your Integration
Unit Testing
- Jest (Node.js)
- Pytest (Python)
// __tests__/StateSet.test.js
import { StatesetClient } from 'stateset-node';
import nock from 'nock';
describe('Stateset Integration', () => {
let client;
beforeEach(() => {
client = new StatesetClient({
apiKey: 'sk_test_123',
environment: 'sandbox'
});
});
afterEach(() => {
nock.cleanAll();
});
test('should create an order', async () => {
const orderData = {
customer_id: 'cus_123',
items: [{ product_id: 'prod_123', quantity: 1 }]
};
nock('https://api.stateset.com')
.post('/v1/orders', orderData)
.reply(201, {
id: 'ord_123',
...orderData,
status: 'pending'
});
const order = await client.orders.create(orderData);
expect(order.id).toBe('ord_123');
expect(order.status).toBe('pending');
});
test('should handle errors gracefully', async () => {
nock('https://api.stateset.com')
.post('/v1/orders')
.reply(400, {
error: {
message: 'Invalid customer_id',
code: 'invalid_request'
}
});
await expect(client.orders.create({}))
.rejects
.toThrow('Invalid customer_id');
});
});
# test_Stateset.py
import pytest
from unittest.mock import Mock, patch
from stateset import Stateset
from stateset.exceptions import StatesetError
@pytest.fixture
def client():
return Stateset(api_key='sk_test_123')
@pytest.fixture
def mock_response():
mock = Mock()
mock.json.return_value = {
'id': 'ord_123',
'status': 'pending'
}
mock.status_code = 201
return mock
def test_create_order(client, mock_response):
with patch('requests.post', return_value=mock_response):
order = client.orders.create(
customer_id='cus_123',
items=[{'product_id': 'prod_123', 'quantity': 1}]
)
assert order.id == 'ord_123'
assert order.status == 'pending'
def test_handle_error(client):
mock_response = Mock()
mock_response.status_code = 400
mock_response.json.return_value = {
'error': {
'message': 'Invalid request',
'code': 'invalid_request'
}
}
with patch('requests.post', return_value=mock_response):
with pytest.raises(StatesetError) as exc_info:
client.orders.create()
assert 'Invalid request' in str(exc_info.value)
@pytest.mark.asyncio
async def test_async_client():
from stateset import AsyncStateset
async_client = AsyncStateset(api_key='sk_test_123')
with patch('httpx.AsyncClient.post') as mock_post:
mock_post.return_value.json.return_value = {'id': 'ord_123'}
mock_post.return_value.status_code = 201
order = await async_client.orders.create(
customer_id='cus_123'
)
assert order.id == 'ord_123'
Integration Testing
// integration-test.js
import { StatesetClient } from 'stateset-node';
import assert from 'assert';
const runIntegrationTests = async () => {
logger.info('🧪 Running Stateset Integration Tests...\n');
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
environment: 'sandbox'
});
const tests = [
{
name: 'Health Check',
fn: async () => {
const health = await fetch('https://api.stateset.com/api/v1/health').then(res => res.json());
assert(health.status === 'healthy', 'API should be healthy');
}
},
{
name: 'Create Customer',
fn: async () => {
const customer = await client.customers.create({
email: `test-${Date.now()}@example.com`,
name: 'Test Customer'
});
assert(customer.id, 'Customer should have an ID');
return customer;
}
},
{
name: 'Create Order',
fn: async (customer) => {
const order = await client.orders.create({
customer_id: customer.id,
items: [
{
product_id: 'prod_test',
quantity: 1,
price: 1000
}
]
});
assert(order.id, 'Order should have an ID');
assert(order.status === 'pending', 'Order should be pending');
return order;
}
},
{
name: 'Retrieve Order',
fn: async (order) => {
const retrieved = await client.orders.get(order.id);
assert(retrieved.id === order.id, 'Should retrieve the same order');
}
},
{
name: 'List Orders',
fn: async () => {
const orders = await client.orders.list({ limit: 5 });
assert(Array.isArray(orders.data), 'Should return an array of orders');
assert(orders.data.length <= 5, 'Should respect limit parameter');
}
}
];
let context = {};
for (const test of tests) {
try {
const result = await test.fn(context);
if (result) context = result;
logger.info(`✅ ${test.name}`);
} catch (error) {
logger.error(`❌ ${test.name}: ${error.message}`);
process.exit(1);
}
}
logger.info('\n✨ All integration tests passed!');
};
runIntegrationTests().catch(console.error);
Load Testing
// load-test.js
import { StatesetClient } from 'stateset-node';
import pLimit from 'p-limit';
const runLoadTest = async () => {
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
environment: 'sandbox'
});
const limit = pLimit(10); // Max 10 concurrent requests
const totalRequests = 100;
const startTime = Date.now();
logger.info(`🚀 Starting load test with ${totalRequests} requests...\n`);
const requests = Array.from({ length: totalRequests }, (_, i) =>
limit(async () => {
const start = Date.now();
try {
await client.orders.list({ limit: 1 });
return { success: true, duration: Date.now() - start };
} catch (error) {
return { success: false, error: error.message, duration: Date.now() - start };
}
})
);
const results = await Promise.all(requests);
const endTime = Date.now();
// Calculate statistics
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
const avgDuration = results.reduce((sum, r) => sum + r.duration, 0) / results.length;
const totalDuration = endTime - startTime;
const requestsPerSecond = (totalRequests / totalDuration) * 1000;
logger.info('📊 Load Test Results:');
logger.info(`Total Requests: ${totalRequests}`);
logger.info(`Successful: ${successful} (${(successful/totalRequests*100).toFixed(1)}%)`);
logger.info(`Failed: ${failed}`);
logger.info(`Average Response Time: ${avgDuration.toFixed(0)}ms`);
logger.info(`Total Duration: ${totalDuration}ms`);
logger.info(`Requests/Second: ${requestsPerSecond.toFixed(1)}`);
if (failed > 0) {
logger.info('\n❌ Failed requests:');
results.filter(r => !r.success).forEach((r, i) => {
logger.info(` ${i + 1}. ${r.error}`);
});
}
};
runLoadTest().catch(console.error);
Best Practices
Security
Never commit API keys or secrets to version control. Always use environment variables.
- Environment Management
- API key Rotation
// config/StateSet.js
import { StatesetClient } from 'stateset-node';
// Validate environment
const validateEnvironment = () => {
const required = ['STATESET_API_KEY'];
const missing = required.filter(key => !process.env[key]);
if (missing.length > 0) {
throw new Error(`Missing environment variables: ${missing.join(', ')}`);
}
// Validate API key format
const apiKey = process.env.STATESET_API_KEY;
if (!apiKey.startsWith('sk_test_') && !apiKey.startsWith('sk_live_')) {
throw new Error('Invalid API key format');
}
// Ensure test keys aren't used in production
if (process.env.NODE_ENV === 'production' && apiKey.startsWith('sk_test_')) {
throw new Error('Test API keys cannot be used in production');
}
};
validateEnvironment();
export const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
environment: process.env.NODE_ENV === 'production' ? 'production' : 'sandbox'
});
// Implement key rotation without downtime
class StatesetClientManager {
constructor() {
this.clients = new Map();
this.primaryKey = process.env.STATESET_API_KEY_PRIMARY;
this.secondaryKey = process.env.STATESET_API_KEY_SECONDARY;
}
getClient(useSecondary = false) {
const key = useSecondary ? this.secondaryKey : this.primaryKey;
if (!this.clients.has(key)) {
this.clients.set(key, new StatesetClient({ apiKey: key }));
}
return this.clients.get(key);
}
async rotateKeys() {
// Test secondary key
try {
const secondaryClient = this.getClient(true);
await secondaryClient.health.check();
// Swap keys
this.primaryKey = this.secondaryKey;
this.secondaryKey = process.env.STATESET_API_KEY_NEW;
// Clear old client
this.clients.clear();
logger.info('Key rotation completed successfully');
} catch (error) {
logger.error('Key rotation failed:', error);
throw error;
}
}
}
Error Handling
Retry Strategy
import { StatesetClient } from 'stateset-node';
import retry from 'async-retry';
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
maxRetries: 0 // Disable built-in retry
});
// Custom retry with exponential backoff
async function resilientRequest(operation) {
return retry(
async (bail) => {
try {
return await operation();
} catch (error) {
// Don't retry client errors
if (error.statusCode >= 400 && error.statusCode < 500) {
bail(error);
}
throw error;
}
},
{
retries: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 30000,
onRetry: (error, attempt) => {
logger.info(`Attempt ${attempt} failed:`, error.message);
}
}
);
}
// Usage
const order = await resilientRequest(() =>
client.orders.create(orderData)
);
Circuit Breaker
import CircuitBreaker from 'opossum';
const options = {
timeout: 3000,
errorThresholdPercentage: 50,
resetTimeout: 30000
};
const breaker = new CircuitBreaker(
async (data) => client.orders.create(data),
options
);
breaker.on('open', () =>
logger.info('Circuit breaker opened');
);
breaker.on('halfOpen', () =>
logger.info('Circuit breaker half-open');
);
// Usage with fallback
try {
const order = await breaker.fire(orderData);
} catch (error) {
if (breaker.opened) {
// Use fallback behavior
logger.info('Service unavailable, using cache');
return getCachedOrder(orderId);
}
throw error;
}
Performance Optimization
- Connection Pooling
- Request Batching
- Caching Strategy
// Reuse HTTP connections
import https from 'https';
const agent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 60000
});
const client = new StatesetClient({
apiKey: process.env.STATESET_API_KEY,
httpsAgent: agent
});
class BatchProcessor {
constructor(client, options = {}) {
this.client = client;
this.batchSize = options.batchSize || 100;
this.flushInterval = options.flushInterval || 1000;
this.queue = [];
this.timer = null;
}
add(operation) {
this.queue.push(operation);
if (this.queue.length >= this.batchSize) {
this.flush();
} else if (!this.timer) {
this.timer = setTimeout(() => this.flush(), this.flushInterval);
}
}
async flush() {
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
if (this.queue.length === 0) return;
const batch = this.queue.splice(0, this.batchSize);
try {
const results = await Promise.all(
batch.map(op => op.execute())
);
batch.forEach((op, i) => {
op.resolve(results[i]);
});
} catch (error) {
batch.forEach(op => op.reject(error));
}
}
}
import { LRUCache } from 'lru-cache';
const cache = new LRUCache({
max: 500,
ttl: 1000 * 60 * 5, // 5 minutes
updateAgeOnGet: true
});
class CachedStatesetClient {
constructor(client) {
this.client = client;
}
async getOrder(orderId, skipCache = false) {
const cacheKey = `order:${orderId}`;
if (!skipCache && cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const order = await this.client.orders.get(orderId);
cache.set(cacheKey, order);
return order;
}
async createOrder(data) {
const order = await this.client.orders.create(data);
// Cache the created order
cache.set(`order:${order.id}`, order);
// Invalidate list cache
cache.delete('orders:list');
return order;
}
async listOrders(params = {}) {
const cacheKey = `orders:list:${JSON.stringify(params)}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const orders = await this.client.orders.list(params);
cache.set(cacheKey, orders);
return orders;
}
}
Monitoring & Logging
// monitoring.js
import { StatesetClient } from 'stateset-node';
import winston from 'winston';
import { StatsD } from 'node-statsd';
// Configure logger
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'Stateset-error.log', level: 'error' }),
new winston.transports.File({ filename: 'Stateset-combined.log' })
]
});
// Configure metrics
const metrics = new StatsD({
host: 'localhost',
port: 8125,
prefix: 'Stateset.'
});
// Wrap client with monitoring
class MonitoredStatesetClient extends StatesetClient {
async request(method, path, data) {
const startTime = Date.now();
const metricName = `api.${method.toLowerCase()}.${path.replace(/\//g, '.')}`;
try {
const result = await super.request(method, path, data);
// Log success
const duration = Date.now() - startTime;
metrics.timing(metricName, duration);
metrics.increment(`${metricName}.success`);
logger.info('API Request Success', {
method,
path,
duration,
status: result.status
});
return result;
} catch (error) {
// Log error
const duration = Date.now() - startTime;
metrics.timing(metricName, duration);
metrics.increment(`${metricName}.error`);
logger.error('API Request Failed', {
method,
path,
duration,
error: {
message: error.message,
code: error.code,
statusCode: error.statusCode,
requestId: error.requestId
}
});
throw error;
}
}
}
Next Steps
Now that you have StateSet SDK installed and configured:1
Explore the API
API Reference
Detailed documentation for all endpoints
API Explorer
Interactive API testing environment
2
Follow Integration Guides
E-commerce Integration
Connect your online store
Webhook Setup
Configure real-time events
Error Handling
Build resilient applications
Rate Limits
Understand API limits
3
Build Your Application
Check out our example applications:
Support & Resources
Documentation
Direct Support
- Email: support@stateset.com
- Enterprise: enterprise@stateset.com
- Security: security@stateset.com
Status & Updates
Need help? Our support team is available Monday-Friday, 9 AM - 5 PM PST. Enterprise customers have 24/7 support.