Next.js
Next.js apps usually use Guapocado in two places:
- Server code with
@guapocado/sdk. - Client UI with
@guapocado/reactand a client key.
Keep server keys on the server. Anything prefixed with NEXT_PUBLIC_ is exposed
to the browser.
Environment Variables
Section titled “Environment Variables”GUAPOCADO_API_KEY=sk_guap_test_...NEXT_PUBLIC_GUAPOCADO_CLIENT_KEY=ck_guap_test_...Server Helper
Section titled “Server Helper”Create a small helper for request-scoped billing:
import { createGuapocadoClient } from "@guapocado/sdk";
export function createBilling(customerId: string) { return createGuapocadoClient({ apiKey: process.env.GUAPOCADO_API_KEY!, customerId, });}In a real app, customerId should come from your auth/session layer.
Route Handler: Feature Check
Section titled “Route Handler: Feature Check”import { createBilling } from "@/lib/billing";import { NextResponse } from "next/server";
export async function GET( request: Request, { params }: { params: Promise<{ feature: string }> },) { const { feature } = await params; const { searchParams } = new URL(request.url); const customerId = searchParams.get("customerId");
if (!customerId) { return NextResponse.json({ error: "customerId required" }, { status: 400 }); }
const guap = createBilling(customerId); const hasAccess = await guap.has(feature);
return NextResponse.json({ feature, hasAccess });}Route Handler: Checkout
Section titled “Route Handler: Checkout”import { createBilling } from "@/lib/billing";import { redirect } from "next/navigation";
export async function POST( request: Request, { params }: { params: Promise<{ productKey: string }> },) { const { productKey } = await params; const form = await request.formData(); const customerId = String(form.get("customerId") ?? "");
if (!customerId) { return Response.json({ error: "customerId required" }, { status: 400 }); }
const origin = new URL(request.url).origin; const guap = createBilling(customerId);
await guap.customers.create({ id: customerId });
const checkout = await guap.checkout.create({ productKey, successUrl: `${origin}/billing/success`, cancelUrl: `${origin}/billing`, });
redirect(checkout.url);}Server Action: Usage
Section titled “Server Action: Usage”"use server";
import { createBilling } from "@/lib/billing";
export async function summarizeText(formData: FormData) { const customerId = String(formData.get("customerId") ?? ""); const text = String(formData.get("text") ?? ""); const credits = Math.ceil(text.length / 4);
const guap = createBilling(customerId);
await guap.usage.consume("ai-credits", credits);
try { return await summarize(text); } catch (error) { await guap.usage.refund("ai-credits", credits); throw error; }}Client Provider
Section titled “Client Provider”Use @guapocado/react for browser-safe reads.
"use client";
import { GuapocadoProvider } from "@guapocado/react";import type { ReactNode } from "react";
export function BillingProvider({ children, customerId,}: { children: ReactNode; customerId: string;}) { return ( <GuapocadoProvider apiKey={process.env.NEXT_PUBLIC_GUAPOCADO_CLIENT_KEY!} customerId={customerId} > {children} </GuapocadoProvider> );}Then read feature state in client components:
"use client";
import { useEntitlement } from "@guapocado/react";
export function AnalyticsLink() { const analytics = useEntitlement("advanced-analytics");
if (analytics.loading) return <span>Checking...</span>; if (!analytics.has) return <a href="/billing">Upgrade</a>;
return <a href="/analytics">Open analytics</a>;}App Router Recommendation
Section titled “App Router Recommendation”For newer teams, keep all mutations server-side:
- checkout
- usage consume
- usage refund
- subscription change
- customer create
- limit settings
Use client components only for browser-safe reads and display.