Package Exports
- @stackbe/sdk
Readme
@stackbe/sdk
Official JavaScript/TypeScript SDK for StackBE - the billing backend for your side project.
Installation
npm install @stackbe/sdk
# or
yarn add @stackbe/sdk
# or
pnpm add @stackbe/sdkQuick Start
import { StackBE } from '@stackbe/sdk';
const stackbe = new StackBE({
apiKey: process.env.STACKBE_API_KEY!,
appId: process.env.STACKBE_APP_ID!,
});
// Track usage
await stackbe.usage.track('customer_123', 'api_calls');
// Check if within limits
const { allowed, remaining } = await stackbe.usage.check('customer_123', 'api_calls');
// Check feature access
const { hasAccess } = await stackbe.entitlements.check('customer_123', 'premium_export');Usage Tracking
Track billable usage events for your customers:
// Track a single event
await stackbe.usage.track('customer_123', 'api_calls');
// Track multiple units
await stackbe.usage.track('customer_123', 'tokens', { quantity: 1500 });
// Check current usage
const usage = await stackbe.usage.get('customer_123');
console.log(usage.metrics);
// [{ metric: 'api_calls', currentUsage: 150, limit: 1000, remaining: 850 }]
// Check specific metric
const { allowed, remaining } = await stackbe.usage.check('customer_123', 'api_calls');
if (!allowed) {
throw new Error('Usage limit exceeded');
}
// Track and check in one call
const result = await stackbe.usage.trackAndCheck('customer_123', 'api_calls');
if (!result.allowed) {
// Handle limit exceeded
}Entitlements
Check feature access based on customer's plan:
// Check single feature
const { hasAccess } = await stackbe.entitlements.check('customer_123', 'premium_export');
if (!hasAccess) {
return res.status(403).json({ error: 'Upgrade to access this feature' });
}
// Get all entitlements
const { entitlements, planName } = await stackbe.entitlements.getAll('customer_123');
console.log(`Customer is on ${planName}`);
console.log(entitlements);
// { premium_export: true, api_access: true, max_projects: 10 }
// Check multiple features at once
const features = await stackbe.entitlements.checkMany('customer_123', [
'premium_export',
'advanced_analytics',
'api_access'
]);
// { premium_export: true, advanced_analytics: false, api_access: true }
// Require a feature (throws if not available)
await stackbe.entitlements.require('customer_123', 'premium_export');Customer Management
// Get customer
const customer = await stackbe.customers.get('customer_123');
// Get by email
const customer = await stackbe.customers.getByEmail('user@example.com');
// Create customer
const newCustomer = await stackbe.customers.create({
email: 'user@example.com',
name: 'John Doe',
metadata: { source: 'api' }
});
// Get or create (idempotent)
const customer = await stackbe.customers.getOrCreate({
email: 'user@example.com',
name: 'John Doe'
});
// Update customer
await stackbe.customers.update('customer_123', {
name: 'Jane Doe'
});Express Middleware
The SDK includes ready-to-use middleware for Express:
Track Usage Automatically
import express from 'express';
import { StackBE } from '@stackbe/sdk';
const app = express();
const stackbe = new StackBE({ apiKey: '...', appId: '...' });
// Track all API calls
app.use(stackbe.middleware({
getCustomerId: (req) => req.user?.customerId,
metric: 'api_calls',
skip: (req) => req.path === '/health', // Skip health checks
}));Require Feature Entitlements
// Protect premium endpoints
app.get('/api/export',
stackbe.requireFeature({
getCustomerId: (req) => req.user?.customerId,
feature: 'premium_export',
onDenied: (req, res) => {
res.status(403).json({ error: 'Upgrade to Pro to access exports' });
}
}),
exportHandler
);Enforce Usage Limits
// Block requests when limit exceeded
app.use('/api',
stackbe.enforceLimit({
getCustomerId: (req) => req.user?.customerId,
metric: 'api_calls',
onLimitExceeded: (req, res, { current, limit }) => {
res.status(429).json({
error: 'Rate limit exceeded',
current,
limit,
upgradeUrl: 'https://myapp.com/upgrade'
});
}
})
);Next.js Integration
API Routes (App Router)
// app/api/generate/route.ts
import { StackBE } from '@stackbe/sdk';
import { NextResponse } from 'next/server';
const stackbe = new StackBE({
apiKey: process.env.STACKBE_API_KEY!,
appId: process.env.STACKBE_APP_ID!,
});
export async function POST(request: Request) {
const { customerId } = await request.json();
// Check limits before processing
const { allowed, remaining } = await stackbe.usage.check(customerId, 'generations');
if (!allowed) {
return NextResponse.json(
{ error: 'Generation limit reached', remaining: 0 },
{ status: 429 }
);
}
// Track usage
await stackbe.usage.track(customerId, 'generations');
// Do the work...
const result = await generateSomething();
return NextResponse.json({ result, remaining: remaining! - 1 });
}Server Actions
// app/actions.ts
'use server';
import { StackBE } from '@stackbe/sdk';
const stackbe = new StackBE({
apiKey: process.env.STACKBE_API_KEY!,
appId: process.env.STACKBE_APP_ID!,
});
export async function exportData(customerId: string) {
// Check feature access
const { hasAccess } = await stackbe.entitlements.check(customerId, 'data_export');
if (!hasAccess) {
throw new Error('Upgrade to Pro to export data');
}
// Perform export...
}Error Handling
import { StackBE, StackBEError } from '@stackbe/sdk';
try {
await stackbe.usage.track('customer_123', 'api_calls');
} catch (error) {
if (error instanceof StackBEError) {
console.error(`StackBE Error: ${error.message}`);
console.error(`Status: ${error.statusCode}`);
console.error(`Code: ${error.code}`);
}
}Configuration
const stackbe = new StackBE({
// Required
apiKey: 'sk_live_...', // Your API key
appId: 'app_...', // Your App ID
// Optional
baseUrl: 'https://api.stackbe.io', // API base URL
timeout: 30000, // Request timeout in ms
});TypeScript
The SDK is written in TypeScript and includes full type definitions:
import type {
Customer,
Subscription,
TrackUsageResponse,
CheckEntitlementResponse,
} from '@stackbe/sdk';License
MIT