npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@stackbe/sdk

v0.6.0

Published

Official JavaScript/TypeScript SDK for StackBE - the billing backend for your side project

Readme

@stackbe/sdk

Official JavaScript/TypeScript SDK for StackBE - the billing backend for your side project.

Installation

npm install @stackbe/sdk

Quick 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 entitlements
const { hasAccess } = await stackbe.entitlements.check('customer_123', 'premium_export');

// Create checkout session
const { url } = await stackbe.checkout.createSession({
  customer: 'cust_123',
  planId: 'plan_pro',
  successUrl: 'https://myapp.com/success',
});

// Get subscription
const subscription = await stackbe.subscriptions.get('cust_123');

// Send magic link
await stackbe.auth.sendMagicLink('[email protected]');

Modules

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 if within limits
const { allowed, remaining } = await stackbe.usage.check('customer_123', 'api_calls');
if (!allowed) {
  throw new Error('Usage limit exceeded');
}

// Get full usage summary
const usage = await stackbe.usage.get('customer_123');
console.log(usage.metrics);

// 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');

// Get all entitlements
const { entitlements, planName } = await stackbe.entitlements.getAll('customer_123');
// { 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',
]);

// Require a feature (throws if not available)
await stackbe.entitlements.require('customer_123', 'premium_export');

Checkout

Create Stripe checkout sessions:

// With existing customer ID
const { url } = await stackbe.checkout.createSession({
  customer: 'cust_123',
  planId: 'plan_pro_monthly',
  successUrl: 'https://myapp.com/success',
  cancelUrl: 'https://myapp.com/pricing',
});

// Redirect to checkout
res.redirect(url);

// With new customer (will be created)
const { url } = await stackbe.checkout.createSession({
  customer: { email: '[email protected]', name: 'John' },
  planId: 'plan_pro_monthly',
  successUrl: 'https://myapp.com/success',
  trialDays: 14,
});

// Get checkout URL directly
const checkoutUrl = await stackbe.checkout.getCheckoutUrl({
  customer: 'cust_123',
  planId: 'plan_pro',
  successUrl: 'https://myapp.com/success',
});

Subscriptions

Manage customer subscriptions:

// Get current subscription
const subscription = await stackbe.subscriptions.get('cust_123');
if (subscription) {
  console.log(`Plan: ${subscription.plan.name}`);
  console.log(`Status: ${subscription.status}`);
}

// Check if customer has active subscription
const isActive = await stackbe.subscriptions.isActive('cust_123');

// Cancel subscription (at end of billing period)
await stackbe.subscriptions.cancel('sub_123');

// Cancel immediately
await stackbe.subscriptions.cancel('sub_123', { immediate: true });

// Update subscription (change plan)
await stackbe.subscriptions.update('sub_123', {
  planId: 'plan_enterprise',
  prorate: true,
});

// Reactivate canceled subscription
await stackbe.subscriptions.reactivate('sub_123');

// List all subscriptions
const subscriptions = await stackbe.subscriptions.list('cust_123');

Authentication

Passwordless authentication with magic links:

// Send magic link
await stackbe.auth.sendMagicLink('[email protected]');

// With redirect URL
await stackbe.auth.sendMagicLink('[email protected]', {
  redirectUrl: 'https://myapp.com/dashboard',
});

// For localhost development
await stackbe.auth.sendMagicLink('[email protected]', {
  useDev: true,
});

// Verify magic link token (in your /verify route)
const { token } = req.query;
const result = await stackbe.auth.verifyToken(token);

// result includes tenant and org context:
// - customerId, email, sessionToken
// - tenantId (your StackBE tenant)
// - organizationId, orgRole (if in org context)
res.cookie('session', result.sessionToken, { httpOnly: true });
res.redirect('/dashboard');

// Get session from token (includes tenant context)
const session = await stackbe.auth.getSession(sessionToken);
if (session) {
  console.log(session.customerId);
  console.log(session.email);
  console.log(session.tenantId);       // Tenant context
  console.log(session.organizationId); // Org context (if applicable)
  console.log(session.subscription);
  console.log(session.entitlements);
}

// Check if authenticated
const isAuthenticated = await stackbe.auth.isAuthenticated(sessionToken);

Customer Management

// Get customer
const customer = await stackbe.customers.get('cust_123');

// Get by email
const customer = await stackbe.customers.getByEmail('[email protected]');

// Create customer
const newCustomer = await stackbe.customers.create({
  email: '[email protected]',
  name: 'John Doe',
  metadata: { source: 'api' },
});

// Get or create (idempotent)
const customer = await stackbe.customers.getOrCreate({
  email: '[email protected]',
  name: 'John Doe',
});

// Update customer
await stackbe.customers.update('cust_123', { name: 'Jane Doe' });

Express Middleware

Track Usage Automatically

app.use(stackbe.middleware({
  getCustomerId: (req) => req.user?.customerId,
  metric: 'api_calls',
  skip: (req) => req.path === '/health',
}));

Require Feature Entitlements

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' });
    },
  }),
  exportHandler
);

Enforce Usage Limits

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 });
    },
  })
);

Authenticate Requests

app.use('/dashboard',
  stackbe.auth.middleware({
    getToken: (req) => req.cookies.session,
    onUnauthenticated: (req, res) => res.redirect('/login'),
  })
);

app.get('/dashboard', (req, res) => {
  // req.customer, req.subscription, req.entitlements are available
  res.json({ email: req.customer.email });
});

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
  const { allowed, remaining } = await stackbe.usage.check(customerId, 'generations');
  if (!allowed) {
    return NextResponse.json({ error: 'Limit reached' }, { status: 429 });
  }

  // Track usage
  await stackbe.usage.track(customerId, 'generations');

  // Do work...
  return NextResponse.json({ success: true, remaining: remaining! - 1 });
}

Server Actions

'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) {
  const { hasAccess } = await stackbe.entitlements.check(customerId, 'data_export');
  if (!hasAccess) {
    throw new Error('Upgrade to Pro to export data');
  }
  // Perform export...
}

Error Handling

The SDK provides typed error codes for specific error handling:

import { StackBE, StackBEError } from '@stackbe/sdk';

try {
  await stackbe.auth.verifyToken(token);
} catch (error) {
  if (error instanceof StackBEError) {
    // Handle specific error types
    switch (error.code) {
      case 'TOKEN_EXPIRED':
        return res.redirect('/login?error=expired');
      case 'TOKEN_ALREADY_USED':
        return res.redirect('/login?error=used');
      case 'SESSION_EXPIRED':
        return res.redirect('/login');
      case 'CUSTOMER_NOT_FOUND':
        return res.status(404).json({ error: 'Customer not found' });
      case 'USAGE_LIMIT_EXCEEDED':
        return res.status(429).json({ error: 'Limit exceeded' });
      default:
        return res.status(500).json({ error: 'Something went wrong' });
    }

    // Or use helper methods
    if (error.isAuthError()) {
      return res.redirect('/login');
    }
    if (error.isNotFoundError()) {
      return res.status(404).json({ error: error.message });
    }
  }
}

Error Codes

| Category | Codes | |----------|-------| | Auth | TOKEN_EXPIRED, TOKEN_ALREADY_USED, TOKEN_INVALID, SESSION_EXPIRED, SESSION_INVALID, UNAUTHORIZED | | Resources | NOT_FOUND, CUSTOMER_NOT_FOUND, SUBSCRIPTION_NOT_FOUND, PLAN_NOT_FOUND, APP_NOT_FOUND | | Usage | USAGE_LIMIT_EXCEEDED, METRIC_NOT_FOUND | | Entitlements | FEATURE_NOT_AVAILABLE, NO_ACTIVE_SUBSCRIPTION | | Validation | VALIDATION_ERROR, MISSING_REQUIRED_FIELD, INVALID_EMAIL | | Network | TIMEOUT, NETWORK_ERROR, UNKNOWN_ERROR |

Configuration

const stackbe = new StackBE({
  apiKey: 'sk_live_...',           // Required: Your API key
  appId: 'app_...',                // Required: Your App ID
  baseUrl: 'https://api.stackbe.io', // Optional: API base URL
  timeout: 30000,                   // Optional: Request timeout in ms

  // Session caching (reduces API calls)
  sessionCacheTTL: 120,            // Optional: Cache sessions for 2 minutes

  // Environment-aware redirects
  devCallbackUrl: 'http://localhost:3000/auth/callback', // Optional: Auto-use in dev
});

Session Caching

Enable session caching to reduce API calls on every request:

const stackbe = new StackBE({
  apiKey,
  appId,
  sessionCacheTTL: 120, // Cache for 2 minutes
});

// Cached calls won't hit the API
const session1 = await stackbe.auth.getSession(token); // API call
const session2 = await stackbe.auth.getSession(token); // From cache

// Manually invalidate cache
stackbe.auth.invalidateSession(token);
stackbe.auth.clearCache(); // Clear all

Environment-Aware Redirects

Auto-detect development environment for magic link redirects:

const stackbe = new StackBE({
  apiKey,
  appId,
  devCallbackUrl: 'http://localhost:3000/auth/callback',
});

// In development (NODE_ENV !== 'production'), uses devCallbackUrl automatically
await stackbe.auth.sendMagicLink('[email protected]');
// Redirects to: http://localhost:3000/auth/callback

// In production, uses your configured auth callback URL
// Or pass explicit redirectUrl to override
await stackbe.auth.sendMagicLink('[email protected]', {
  redirectUrl: 'https://myapp.com/custom-callback',
});

Webhooks

Typed webhook payloads for handling StackBE events:

import type {
  AnyWebhookEvent,
  SubscriptionCreatedEvent,
  SubscriptionCancelledEvent,
  PaymentFailedEvent,
} from '@stackbe/sdk';

// In your webhook handler
app.post('/webhooks/stackbe', (req, res) => {
  const event = req.body as AnyWebhookEvent;

  switch (event.type) {
    case 'subscription_created':
      const sub = event as SubscriptionCreatedEvent;
      console.log(`New subscription: ${sub.data.planName}`);
      break;

    case 'subscription_cancelled':
      const cancelled = event as SubscriptionCancelledEvent;
      console.log(`Cancelled: ${cancelled.data.id}`);
      break;

    case 'payment_failed':
      const payment = event as PaymentFailedEvent;
      console.log(`Payment failed: ${payment.data.failureReason}`);
      // Send dunning email
      break;
  }

  res.json({ received: true });
});

Webhook Event Types

| Event | Payload | |-------|---------| | subscription_created | SubscriptionWebhookPayload | | subscription_updated | SubscriptionWebhookPayload | | subscription_cancelled | SubscriptionWebhookPayload | | subscription_renewed | SubscriptionWebhookPayload | | trial_started | SubscriptionWebhookPayload | | trial_ended | SubscriptionWebhookPayload | | payment_succeeded | PaymentWebhookPayload | | payment_failed | PaymentWebhookPayload | | customer_created | CustomerWebhookPayload | | customer_updated | CustomerWebhookPayload |

TypeScript

Full type definitions included:

import type {
  Customer,
  Subscription,
  SubscriptionWithPlan,
  CheckoutSessionResponse,
  SessionResponse,
  TrackUsageResponse,
  CheckEntitlementResponse,
  StackBEErrorCode,
  WebhookEventType,
  AnyWebhookEvent,
} from '@stackbe/sdk';

License

MIT