@stackbe/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +286 -0
- package/dist/index.d.mts +448 -0
- package/dist/index.d.ts +448 -0
- package/dist/index.js +592 -0
- package/dist/index.mjs +561 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# @stackbe/sdk
|
|
2
|
+
|
|
3
|
+
Official JavaScript/TypeScript SDK for [StackBE](https://stackbe.io) - the billing backend for your side project.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @stackbe/sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @stackbe/sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @stackbe/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { StackBE } from '@stackbe/sdk';
|
|
19
|
+
|
|
20
|
+
const stackbe = new StackBE({
|
|
21
|
+
apiKey: process.env.STACKBE_API_KEY!,
|
|
22
|
+
appId: process.env.STACKBE_APP_ID!,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Track usage
|
|
26
|
+
await stackbe.usage.track('customer_123', 'api_calls');
|
|
27
|
+
|
|
28
|
+
// Check if within limits
|
|
29
|
+
const { allowed, remaining } = await stackbe.usage.check('customer_123', 'api_calls');
|
|
30
|
+
|
|
31
|
+
// Check feature access
|
|
32
|
+
const { hasAccess } = await stackbe.entitlements.check('customer_123', 'premium_export');
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage Tracking
|
|
36
|
+
|
|
37
|
+
Track billable usage events for your customers:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// Track a single event
|
|
41
|
+
await stackbe.usage.track('customer_123', 'api_calls');
|
|
42
|
+
|
|
43
|
+
// Track multiple units
|
|
44
|
+
await stackbe.usage.track('customer_123', 'tokens', { quantity: 1500 });
|
|
45
|
+
|
|
46
|
+
// Check current usage
|
|
47
|
+
const usage = await stackbe.usage.get('customer_123');
|
|
48
|
+
console.log(usage.metrics);
|
|
49
|
+
// [{ metric: 'api_calls', currentUsage: 150, limit: 1000, remaining: 850 }]
|
|
50
|
+
|
|
51
|
+
// Check specific metric
|
|
52
|
+
const { allowed, remaining } = await stackbe.usage.check('customer_123', 'api_calls');
|
|
53
|
+
if (!allowed) {
|
|
54
|
+
throw new Error('Usage limit exceeded');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Track and check in one call
|
|
58
|
+
const result = await stackbe.usage.trackAndCheck('customer_123', 'api_calls');
|
|
59
|
+
if (!result.allowed) {
|
|
60
|
+
// Handle limit exceeded
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Entitlements
|
|
65
|
+
|
|
66
|
+
Check feature access based on customer's plan:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Check single feature
|
|
70
|
+
const { hasAccess } = await stackbe.entitlements.check('customer_123', 'premium_export');
|
|
71
|
+
|
|
72
|
+
if (!hasAccess) {
|
|
73
|
+
return res.status(403).json({ error: 'Upgrade to access this feature' });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Get all entitlements
|
|
77
|
+
const { entitlements, planName } = await stackbe.entitlements.getAll('customer_123');
|
|
78
|
+
console.log(`Customer is on ${planName}`);
|
|
79
|
+
console.log(entitlements);
|
|
80
|
+
// { premium_export: true, api_access: true, max_projects: 10 }
|
|
81
|
+
|
|
82
|
+
// Check multiple features at once
|
|
83
|
+
const features = await stackbe.entitlements.checkMany('customer_123', [
|
|
84
|
+
'premium_export',
|
|
85
|
+
'advanced_analytics',
|
|
86
|
+
'api_access'
|
|
87
|
+
]);
|
|
88
|
+
// { premium_export: true, advanced_analytics: false, api_access: true }
|
|
89
|
+
|
|
90
|
+
// Require a feature (throws if not available)
|
|
91
|
+
await stackbe.entitlements.require('customer_123', 'premium_export');
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Customer Management
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Get customer
|
|
98
|
+
const customer = await stackbe.customers.get('customer_123');
|
|
99
|
+
|
|
100
|
+
// Get by email
|
|
101
|
+
const customer = await stackbe.customers.getByEmail('user@example.com');
|
|
102
|
+
|
|
103
|
+
// Create customer
|
|
104
|
+
const newCustomer = await stackbe.customers.create({
|
|
105
|
+
email: 'user@example.com',
|
|
106
|
+
name: 'John Doe',
|
|
107
|
+
metadata: { source: 'api' }
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Get or create (idempotent)
|
|
111
|
+
const customer = await stackbe.customers.getOrCreate({
|
|
112
|
+
email: 'user@example.com',
|
|
113
|
+
name: 'John Doe'
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Update customer
|
|
117
|
+
await stackbe.customers.update('customer_123', {
|
|
118
|
+
name: 'Jane Doe'
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Express Middleware
|
|
123
|
+
|
|
124
|
+
The SDK includes ready-to-use middleware for Express:
|
|
125
|
+
|
|
126
|
+
### Track Usage Automatically
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import express from 'express';
|
|
130
|
+
import { StackBE } from '@stackbe/sdk';
|
|
131
|
+
|
|
132
|
+
const app = express();
|
|
133
|
+
const stackbe = new StackBE({ apiKey: '...', appId: '...' });
|
|
134
|
+
|
|
135
|
+
// Track all API calls
|
|
136
|
+
app.use(stackbe.middleware({
|
|
137
|
+
getCustomerId: (req) => req.user?.customerId,
|
|
138
|
+
metric: 'api_calls',
|
|
139
|
+
skip: (req) => req.path === '/health', // Skip health checks
|
|
140
|
+
}));
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Require Feature Entitlements
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// Protect premium endpoints
|
|
147
|
+
app.get('/api/export',
|
|
148
|
+
stackbe.requireFeature({
|
|
149
|
+
getCustomerId: (req) => req.user?.customerId,
|
|
150
|
+
feature: 'premium_export',
|
|
151
|
+
onDenied: (req, res) => {
|
|
152
|
+
res.status(403).json({ error: 'Upgrade to Pro to access exports' });
|
|
153
|
+
}
|
|
154
|
+
}),
|
|
155
|
+
exportHandler
|
|
156
|
+
);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Enforce Usage Limits
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Block requests when limit exceeded
|
|
163
|
+
app.use('/api',
|
|
164
|
+
stackbe.enforceLimit({
|
|
165
|
+
getCustomerId: (req) => req.user?.customerId,
|
|
166
|
+
metric: 'api_calls',
|
|
167
|
+
onLimitExceeded: (req, res, { current, limit }) => {
|
|
168
|
+
res.status(429).json({
|
|
169
|
+
error: 'Rate limit exceeded',
|
|
170
|
+
current,
|
|
171
|
+
limit,
|
|
172
|
+
upgradeUrl: 'https://myapp.com/upgrade'
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Next.js Integration
|
|
180
|
+
|
|
181
|
+
### API Routes (App Router)
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// app/api/generate/route.ts
|
|
185
|
+
import { StackBE } from '@stackbe/sdk';
|
|
186
|
+
import { NextResponse } from 'next/server';
|
|
187
|
+
|
|
188
|
+
const stackbe = new StackBE({
|
|
189
|
+
apiKey: process.env.STACKBE_API_KEY!,
|
|
190
|
+
appId: process.env.STACKBE_APP_ID!,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
export async function POST(request: Request) {
|
|
194
|
+
const { customerId } = await request.json();
|
|
195
|
+
|
|
196
|
+
// Check limits before processing
|
|
197
|
+
const { allowed, remaining } = await stackbe.usage.check(customerId, 'generations');
|
|
198
|
+
|
|
199
|
+
if (!allowed) {
|
|
200
|
+
return NextResponse.json(
|
|
201
|
+
{ error: 'Generation limit reached', remaining: 0 },
|
|
202
|
+
{ status: 429 }
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Track usage
|
|
207
|
+
await stackbe.usage.track(customerId, 'generations');
|
|
208
|
+
|
|
209
|
+
// Do the work...
|
|
210
|
+
const result = await generateSomething();
|
|
211
|
+
|
|
212
|
+
return NextResponse.json({ result, remaining: remaining! - 1 });
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Server Actions
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// app/actions.ts
|
|
220
|
+
'use server';
|
|
221
|
+
|
|
222
|
+
import { StackBE } from '@stackbe/sdk';
|
|
223
|
+
|
|
224
|
+
const stackbe = new StackBE({
|
|
225
|
+
apiKey: process.env.STACKBE_API_KEY!,
|
|
226
|
+
appId: process.env.STACKBE_APP_ID!,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
export async function exportData(customerId: string) {
|
|
230
|
+
// Check feature access
|
|
231
|
+
const { hasAccess } = await stackbe.entitlements.check(customerId, 'data_export');
|
|
232
|
+
|
|
233
|
+
if (!hasAccess) {
|
|
234
|
+
throw new Error('Upgrade to Pro to export data');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Perform export...
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Error Handling
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { StackBE, StackBEError } from '@stackbe/sdk';
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
await stackbe.usage.track('customer_123', 'api_calls');
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error instanceof StackBEError) {
|
|
250
|
+
console.error(`StackBE Error: ${error.message}`);
|
|
251
|
+
console.error(`Status: ${error.statusCode}`);
|
|
252
|
+
console.error(`Code: ${error.code}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Configuration
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
const stackbe = new StackBE({
|
|
261
|
+
// Required
|
|
262
|
+
apiKey: 'sk_live_...', // Your API key
|
|
263
|
+
appId: 'app_...', // Your App ID
|
|
264
|
+
|
|
265
|
+
// Optional
|
|
266
|
+
baseUrl: 'https://api.stackbe.io', // API base URL
|
|
267
|
+
timeout: 30000, // Request timeout in ms
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## TypeScript
|
|
272
|
+
|
|
273
|
+
The SDK is written in TypeScript and includes full type definitions:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import type {
|
|
277
|
+
Customer,
|
|
278
|
+
Subscription,
|
|
279
|
+
TrackUsageResponse,
|
|
280
|
+
CheckEntitlementResponse,
|
|
281
|
+
} from '@stackbe/sdk';
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
MIT
|