Feature Gating
Define plans and check feature access with one line.
Feature gating lets you map billing products to application features, then check access with a simple API.
Define your plans
import { createEnhancedClient } from "@fuime/billing-sdk";
import { stripe } from "@fuime/billing-sdk/stripe";
const billing = createEnhancedClient({
provider: stripe({ apiKey: process.env.STRIPE_KEY }),
plans: {
// Stripe product IDs
prod_free: {
features: ["basic"],
},
prod_pro: {
features: ["basic", "api", "export", "analytics"],
},
prod_enterprise: {
features: "*", // All features
},
},
});Check feature access
// Simple boolean check
if (await billing.hasFeature(userId, "api")) {
// User has API access
}
// Check multiple features
const features = await billing.getFeatures(userId);
// ["basic", "api", "export", "analytics"]
// Check if user has any active subscription
const entitlement = await billing.getEntitlement(userId);
if (entitlement?.active) {
// User is subscribed
}Wildcard features
Use "*" for plans that should have access to everything:
plans: {
prod_enterprise: {
features: "*", // Access to all features, current and future
},
}When checking features:
await billing.hasFeature(enterpriseUser, "anything"); // true
await billing.hasFeature(enterpriseUser, "new-feature"); // trueFeature flags vs. feature gating
| Feature Flags | Feature Gating |
|---|---|
| Control rollout | Control access by subscription |
| Toggle on/off for everyone | Different access per plan |
| A/B testing | Monetization |
You can combine both:
// Check feature flag AND billing
if (featureFlags.isEnabled("new-dashboard") &&
await billing.hasFeature(userId, "dashboard")) {
// Show new dashboard to paying users in the rollout
}Middleware pattern
Protect routes with middleware:
middleware.ts
import { billing } from "@/lib/billing";
import { NextResponse } from "next/server";
export async function middleware(request: Request) {
const userId = getUserFromSession(request);
if (request.nextUrl.pathname.startsWith("/api/pro")) {
if (!(await billing.hasFeature(userId, "api"))) {
return NextResponse.json(
{ error: "Pro subscription required" },
{ status: 403 }
);
}
}
return NextResponse.next();
}React component pattern
async function FeatureGate({
feature,
userId,
children,
fallback
}: {
feature: string;
userId: string;
children: React.ReactNode;
fallback?: React.ReactNode;
}) {
const hasAccess = await billing.hasFeature(userId, feature);
if (!hasAccess) {
return fallback ?? null;
}
return <>{children}</>;
}
// Usage
<FeatureGate feature="export" userId={user.id} fallback={<UpgradePrompt />}>
<ExportButton />
</FeatureGate>