Usage Metering and Subscription Enforcement
Track per-tenant usage and gate access based on plan limits and billing status.
Why Usage Metering Matters in SaaS
In a multi-tenant SaaS application, different customers pay for different tiers. A Starter plan might allow 1,000 API calls per month while an Enterprise plan allows unlimited access. Without usage metering, every tenant gets the same experience regardless of what they pay.
Usage metering serves two purposes:
- Enforcement: Block or degrade service when limits are reached
- Billing signals: Feed accurate consumption data to your billing provider (e.g. Stripe)
In Next.js 15 with the App Router, metering fits naturally into Server Actions and Route Handlers — the two places where real work happens on the server.
Data Model: Tenants, Plans, and Usage
Start with a schema that captures the relationship between a tenant, their active subscription plan, and their current usage counters. A minimal Postgres schema looks like this:
tenants— one row per organisationsubscription_plans— limits per feature per tiertenant_usage— rolling counters reset on billing cycle
Keeping usage in a dedicated table (rather than summing event logs on every request) makes limit checks a single indexed read, which is critical for low-latency Server Actions.
// types/billing.ts
export type PlanTier = 'starter' | 'pro' | 'enterprise';
export interface SubscriptionPlan {
id: string;
tier: PlanTier;
limits: {
apiCallsPerMonth: number; // -1 = unlimited
teamMembers: number;
storageMb: number;
};
}
export interface TenantUsage {
tenantId: string;
billingPeriodStart: Date;
apiCallsUsed: number;
teamMembersActive: number;
storageMbUsed: number;
}
export interface Tenant {
id: string;
name: string;
planId: string;
subscriptionStatus: 'active' | 'past_due' | 'canceled' | 'trialing';
}All lessons in this course
- Subdomain and Path-Based Tenant Resolution
- Row-Level Tenant Data Isolation Patterns
- Per-Tenant Theming and Feature Flags
- Usage Metering and Subscription Enforcement