Skip to main content

Overview

This page provides complete examples and common integration patterns for Flowglad SDKs. For full working applications, check out our example projects.

Example Projects

Generation-based Subscription

A Next.js app with authentication and billing integration, demonstrating a pricing model with subscription, single-payment, and usage. Location: examples/generation-based-subscription Features:
  • Next.js App Router
  • BetterAuth authentication
  • FlowgladProvider setup
  • Pricing table
  • Feature-gated content
  • Usage-based billing for image generation
  • Subscription management
Key Files:
  • pricing.yaml - Pricing model specification
  • src/lib/flowglad.ts - Flowglad server setup
  • src/app/layout.tsx - FlowgladProvider setup
  • src/app/api/flowglad/[...path]/route.ts - Flowglad routeHandler
  • src/app/api/usage-events/route.ts - Route for usage event creation
  • src/components/pricing-cards-grid.tsx - Pricing page with usePricing

Tiered Usage-Gated Subscription

A Next.js app demonstrating tiered subscription plans with usage limits and feature access by tier. Similar to ChatGPT’s pricing model, with different quotas, context windows, and capabilities varying by plan level. Location: examples/tiered-usage-gated-subscription Features:
  • Next.js App Router
  • Tiered subscription plans with usage gates
  • Feature access by subscription tier
  • Usage credit grants that renew monthly
  • Multiple usage meters with tiered limits
  • Individual to multi-seat plan support
Key Files:
  • pricing.yaml - Pricing model with tiered usage limits
  • src/lib/flowglad.ts - Flowglad server setup
  • src/app/layout.tsx - FlowgladProvider setup
  • src/app/api/flowglad/[...path]/route.ts - Flowglad routeHandler
  • Usage balance checking and tier-based feature gating

Usage Limit Subscription

A Next.js app demonstrating a hybrid subscription + usage model. Subscriptions include monthly usage credits that renew each billing period, with optional overage billing. Perfect for API-intensive products like Cursor. Location: examples/usage-limit-subscription Features:
  • Next.js App Router
  • Hybrid subscription + metered usage model
  • Monthly usage credits that renew automatically
  • Optional on-demand credit purchases
  • Overage billing when credits are exhausted
  • Multiple tiered plans with different usage limits
Key Files:
  • pricing.yaml - Pricing model with usage limits and overage
  • src/lib/flowglad.ts - Flowglad server setup
  • src/app/layout.tsx - FlowgladProvider setup
  • src/app/api/flowglad/[...path]/route.ts - Flowglad routeHandler
  • Usage balance tracking and overage handling

Complete Integration Examples

Next.js Setup

Full setup for a Next.js application:
import { FlowgladServer } from '@flowglad/nextjs/server'

export const flowglad = (customerExternalId: string) => {
  // customerExternalId is the ID from YOUR app's database, NOT Flowglad's customer ID
  return new FlowgladServer({
    customerExternalId,
    getCustomerDetails: async (externalId) => {
      // Fetch customer details from YOUR database using YOUR app's ID
      const user = await db.users.findOne({ id: externalId })
      if (!user) {
        throw new Error('Customer not found')
      }
      return {
        email: user.email,
        name: user.name,
      }
    },
  })
}

React SPA with Node.js Backend

Setup for a React application with a separate Node.js backend:
import { FlowgladProvider } from '@flowglad/react'

export default function App() {
  const authToken = getAuthToken()
  return (
    <FlowgladProvider
      requestConfig={{
        headers: {
          ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
        },
      }}
    >
      <Router>
        {/* Your routes */}
      </Router>
    </FlowgladProvider>
  )
}

Common Patterns

Feature Gating

import { useBilling } from '@flowglad/react'

export function FeatureGate({ 
  featureSlug, 
  fallback, 
  children 
}: {
  featureSlug: string
  fallback?: React.ReactNode
  children: React.ReactNode
}) {
  const { checkFeatureAccess } = useBilling()

  if (!checkFeatureAccess) {
    return null
  }

  if (!checkFeatureAccess(featureSlug)) {
    return fallback || <div>Upgrade to access this feature</div>
  }

  return <>{children}</>
}

// Usage
<FeatureGate 
  featureSlug="advanced_analytics"
  fallback={<UpgradePrompt />}
>
  <AdvancedAnalytics />
</FeatureGate>

Usage Metering

import { flowglad } from './flowglad'

// Example: Track usage with price (for billing)
export async function trackAPICall(
  endpoint: string,
  customerExternalId: string,
  quantity: number = 1
) {
  // customerExternalId is the ID from YOUR app's database, NOT Flowglad's customer ID
  await flowglad(customerExternalId).createUsageEvent({
    subscriptionId: 'sub_123',
    priceSlug: 'price_usage_meter',
    amount: quantity,
    transactionId: `${endpoint}:${Date.now()}`,
    usageDate: Date.now(),
    properties: {
      endpoint,
      requestedAt: new Date().toISOString(),
    },
  })
}

// Example: Track usage without price (for tracking only)
export async function trackAPICallNoBilling(
  endpoint: string,
  customerExternalId: string,
  quantity: number = 1
) {
  // customerExternalId is the ID from YOUR app's database, NOT Flowglad's customer ID
  await flowglad(customerExternalId).createUsageEvent({
    subscriptionId: 'sub_123',
    usageMeterSlug: 'api_calls',
    amount: quantity,
    transactionId: `${endpoint}:${Date.now()}`,
    usageDate: Date.now(),
    properties: {
      endpoint,
      requestedAt: new Date().toISOString(),
    },
  })
}

// Usage in API routes
app.get('/api/data', async (req, res) => {
  // Extract customerExternalId from your auth/session
  const userId = await getUserIdFromRequest(req)
  await trackAPICall('/api/data', userId)
  
  const data = await fetchData()
  res.json(data)
})

Subscription Management

import { useBilling } from '@flowglad/react'
import { useState } from 'react'

export function UpgradeButton({ priceId }: { priceId: string }) {
  const { createCheckoutSession } = useBilling()
  const [isLoading, setIsLoading] = useState(false)

  const handleUpgrade = async () => {
    setIsLoading(true)
    try {
      await createCheckoutSession({
        priceId,
        successUrl: `${window.location.origin}/billing/success`,
        cancelUrl: window.location.href,
        autoRedirect: true,
      })
    } catch (error) {
      console.error('Upgrade failed:', error)
      setIsLoading(false)
    }
  }

  return (
    <button onClick={handleUpgrade} disabled={isLoading}>
      {isLoading ? 'Loading...' : 'Upgrade'}
    </button>
  )
}

Pricing Table

import { useBilling, usePricing } from '@flowglad/react'

export function PricingTable() {
  const pricingModel = usePricing()
  const { subscriptions, createCheckoutSession } = useBilling()

  const currentPlan = subscriptions?.find((subscription) => subscription.status === 'active')

  if (!pricingModel) {
    return <div>Loading pricing...</div>
  }

  const handleSelectPlan = async (priceId: string) => {
    await createCheckoutSession({
      priceId,
      successUrl: `${window.location.origin}/billing/success`,
      cancelUrl: window.location.href,
      autoRedirect: true,
    })
  }

  return (
    <div className="grid grid-cols-2 gap-4">
      {pricingModel.products.map((product) => {
        const defaultPrice = product.defaultPrice ?? product.prices?.[0]
        if (!defaultPrice) return null

        const priceLabel = defaultPrice.intervalUnit
          ? `${(defaultPrice.unitPrice / 100).toFixed(2)} / ${defaultPrice.intervalUnit}`
          : (defaultPrice.unitPrice / 100).toFixed(2)

        return (
          <div key={product.id} className="border rounded-lg p-6">
            <h3>{product.name}</h3>
            <p className="text-2xl font-bold">${priceLabel}</p>

            <ul>
              {product.features.map((feature) => (
                <li key={feature.slug}>{feature.name}</li>
              ))}
            </ul>

            <button
              onClick={() => handleSelectPlan(defaultPrice.id)}
              disabled={currentPlan?.priceId === defaultPrice.id}
            >
              {currentPlan?.priceId === defaultPrice.id
                ? 'Current Plan'
                : 'Select Plan'}
            </button>
          </div>
        )
      })}
    </div>
  )
}

Customer Portal

import { useBilling } from '@flowglad/react'

export function CustomerPortal() {
  const { customer, subscriptions, paymentMethods, invoices } = useBilling()

  if (!customer) {
    return <div>Loading...</div>
  }

  return (
    <div>
      <section>
        <h2>Account Information</h2>
        <p>Name: {customer.name}</p>
        <p>Email: {customer.email}</p>
      </section>

      <section>
        <h2>Subscriptions</h2>
        {subscriptions?.map((sub) => (
          <div key={sub.id}>
            <p>Status: {sub.status}</p>
            <p>Renews: {new Date(sub.currentPeriodEnd).toLocaleDateString()}</p>
          </div>
        ))}
      </section>

      <section>
        <h2>Payment Methods</h2>
        {paymentMethods?.map((pm) => (
          <div key={pm.id}>
            <p>{pm.card?.brand} •••• {pm.card?.last4}</p>
            {pm.isDefault && <span>Default</span>}
          </div>
        ))}
      </section>

      <section>
        <h2>Recent Invoices</h2>
        {invoices?.slice(0, 5).map((invoice) => (
          <div key={invoice.id}>
            <p>{new Date(invoice.createdAt).toLocaleDateString()}</p>
            <p>${(invoice.amount / 100).toFixed(2)}</p>
            <p>{invoice.status}</p>
          </div>
        ))}
      </section>
    </div>
  )
}

Testing Examples

Mock Billing Context

import { FlowgladProvider } from '@flowglad/react'

const mockBillingData = {
  // Provide a full CustomerBillingDetails object in real tests.
  customer: {
    id: 'cus_test',
    externalId: 'user_123',
    name: 'Test User',
    email: '[email protected]',
  },
  subscriptions: [
    {
      id: 'sub_test',
      status: 'active',
      priceId: 'price_premium',
    },
  ],
}

export function TestWrapper({ children }) {
  return (
    <FlowgladProvider __devMode billingMocks={mockBillingData}>
      {children}
    </FlowgladProvider>
  )
}

Next Steps