Advanced Examples
Explore advanced patterns and real-world implementations.
Webhook Handler
Setting Up Webhooks
webhook-handler.ts
import { SDK } from "our-sdk";
import express from "express";
import crypto from "crypto";
const app = express();
const sdk = new SDK({ apiKey: process.env.SDK_API_KEY });
// Webhook secret from your dashboard
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;
// Verify webhook signature
function verifySignature(payload: string, signature: string): boolean {
const expectedSignature = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Webhook endpoint
app.post(
"/webhook",
express.raw({ type: "application/json" }),
async (req, res) => {
const signature = req.headers["x-webhook-signature"] as string;
const payload = req.body.toString();
// Verify the signature
if (!verifySignature(payload, signature)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(payload);
// Handle different event types
switch (event.type) {
case "resource.created":
await handleResourceCreated(event.data);
break;
case "resource.updated":
await handleResourceUpdated(event.data);
break;
case "resource.deleted":
await handleResourceDeleted(event.data);
break;
default:
console.log("Unknown event type:", event.type);
}
res.status(200).send("OK");
}
);
async function handleResourceCreated(data: any) {
console.log("New resource created:", data.id);
// Your custom logic here
}
async function handleResourceUpdated(data: any) {
console.log("Resource updated:", data.id);
// Your custom logic here
}
async function handleResourceDeleted(data: any) {
console.log("Resource deleted:", data.id);
// Your custom logic here
}
Retry Logic with Exponential Backoff
Robust Request Handler
retry-logic.ts
interface RetryOptions {
maxRetries?: number;
baseDelay?: number;
maxDelay?: number;
}
async function withRetry<T>(
fn: () => Promise<T>,
options: RetryOptions = {}
): Promise<T> {
const { maxRetries = 3, baseDelay = 1000, maxDelay = 30000 } = options;
let lastError: Error;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
// Don't retry on certain errors
if (
error instanceof SDK.ValidationError ||
error instanceof SDK.AuthenticationError
) {
throw error;
}
// Don't retry if this was the last attempt
if (attempt === maxRetries) {
break;
}
// Calculate delay with exponential backoff
const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
// Add jitter to prevent thundering herd
const jitter = Math.random() * 0.3 * delay;
console.log(
`Retry attempt ${attempt + 1}/${maxRetries} after ${delay + jitter}ms`
);
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
}
}
throw lastError!;
}
// Usage
async function fetchWithRetry() {
return withRetry(() => sdk.get("res_123"), {
maxRetries: 5,
baseDelay: 2000,
});
}
Caching Layer
Redis Cache Integration
cached-sdk.ts
import Redis from "ioredis";
class CachedSDK {
private sdk: SDK;
private redis: Redis;
private defaultTTL = 300; // 5 minutes
constructor(sdkConfig: SDKConfig, redisConfig?: RedisOptions) {
this.sdk = new SDK(sdkConfig);
this.redis = new Redis(redisConfig);
}
async get(id: string, options?: GetOptions): Promise<Resource> {
const cacheKey = `resource:${id}:${JSON.stringify(options || {})}`;
// Try to get from cache
const cached = await this.redis.get(cacheKey);
if (cached) {
console.log("Cache hit:", id);
return JSON.parse(cached);
}
// Cache miss - fetch from API
console.log("Cache miss:", id);
const resource = await this.sdk.get(id, options);
// Store in cache
await this.redis.setex(cacheKey, this.defaultTTL, JSON.stringify(resource));
return resource;
}
async invalidate(id: string): Promise<void> {
const pattern = `resource:${id}:*`;
const keys = await this.redis.keys(pattern);
if (keys.length > 0) {
await this.redis.del(...keys);
console.log(`Invalidated ${keys.length} cache entries for ${id}`);
}
}
async update(id: string, data: UpdateData): Promise<Resource> {
const resource = await this.sdk.update(id, data);
// Invalidate cache
await this.invalidate(id);
return resource;
}
}
// Usage
const cachedSdk = new CachedSDK(
{ apiKey: process.env.SDK_API_KEY },
{ host: "localhost", port: 6379 }
);
const resource = await cachedSdk.get("res_123");
Queue-based Processing
Bull Queue Integration
queue-processing.ts
import Queue from "bull";
import { SDK } from "our-sdk";
const sdk = new SDK({ apiKey: process.env.SDK_API_KEY });
// Create queue
const processQueue = new Queue("sdk-operations", {
redis: {
host: "localhost",
port: 6379,
},
});
// Define job types
interface CreateJob {
type: "create";
data: CreateData;
}
interface UpdateJob {
type: "update";
id: string;
data: UpdateData;
}
type Job = CreateJob | UpdateJob;
// Process jobs
processQueue.process(async (job) => {
const { type } = job.data;
switch (type) {
case "create":
return await sdk.create(job.data.data);
case "update":
return await sdk.update(job.data.id, job.data.data);
default:
throw new Error(`Unknown job type: ${type}`);
}
});
// Add jobs to queue
async function queueCreate(data: CreateData) {
await processQueue.add(
{ type: "create", data },
{
attempts: 3,
backoff: {
type: "exponential",
delay: 2000,
},
}
);
}
async function queueUpdate(id: string, data: UpdateData) {
await processQueue.add(
{ type: "update", id, data },
{
attempts: 3,
backoff: {
type: "exponential",
delay: 2000,
},
}
);
}
// Monitor queue
processQueue.on("completed", (job, result) => {
console.log(`Job ${job.id} completed:`, result);
});
processQueue.on("failed", (job, err) => {
console.error(`Job ${job!.id} failed:`, err);
});
These patterns help you build robust, production-ready applications with the SDK.
Next Steps
- Review Best Practices
- Check the API Reference
- Join our Community Forum