BuildBaseBuildBase

Going to Production

Everything between the 5-minute quickstart and a production-ready SaaS app.

The quickstart gets you a working demo. Here is what to configure before real users sign in.

1. Environment variables

Create a .env.local file in your project root. Never commit this file.

# BuildBase
NEXT_PUBLIC_BUILDBASE_SERVER_URL=https://api.console.buildbase.app
NEXT_PUBLIC_BUILDBASE_ORG_ID=your-org-id
NEXT_PUBLIC_BUILDBASE_CLIENT_ID=your-client-id
NEXT_PUBLIC_BUILDBASE_REDIRECT_URL=http://localhost:3000/callback

# Server-only — never expose with a NEXT_PUBLIC_ prefix
BUILDBASE_CLIENT_SECRET=your-client-secret

# Session secret (used to sign httpOnly cookies)
SESSION_SECRET=run-openssl-rand-hex-32

Update your provider to read from env:

<SaaSOSProvider
  serverUrl={process.env.NEXT_PUBLIC_BUILDBASE_SERVER_URL!}
  version={ApiVersion.V1}
  orgId={process.env.NEXT_PUBLIC_BUILDBASE_ORG_ID!}
  auth={{
    clientId: process.env.NEXT_PUBLIC_BUILDBASE_CLIENT_ID!,
    redirectUrl: process.env.NEXT_PUBLIC_BUILDBASE_REDIRECT_URL!,
    callbacks: { /* ... */ },
  }}
>

2. Auth callback API route

The quickstart shows fetch('/api/auth/verify') — here's the actual implementation. This route calls BuildBase's token endpoint (POST /api/v1/auth/token) to exchange the auth code for a session, then sets an httpOnly cookie. The exchange requires your clientSecret, which is why it runs on your backend — the secret must never reach the browser.

// app/api/auth/verify/route.ts
import { cookies } from 'next/headers';

export async function POST(req: Request) {
  const { code } = await req.json();

  // Exchange the auth code for a session
  const res = await fetch(
    `${process.env.NEXT_PUBLIC_BUILDBASE_SERVER_URL}/api/v1/auth/token`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        code,
        orgId: process.env.NEXT_PUBLIC_BUILDBASE_ORG_ID,
        clientId: process.env.NEXT_PUBLIC_BUILDBASE_CLIENT_ID,
        clientSecret: process.env.BUILDBASE_CLIENT_SECRET,
      }),
    }
  );

  if (!res.ok) {
    return Response.json({ error: 'Invalid or expired code' }, { status: 401 });
  }

  const { data } = await res.json(); // { sessionId, user }

  // Set httpOnly cookie (secure in production)
  const cookieStore = await cookies();
  cookieStore.set('bb-session', data.sessionId, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    path: '/',
    maxAge: 60 * 60 * 24 * 30, // 30 days
  });

  return Response.json({ sessionId: data.sessionId });
}
// app/api/auth/session/route.ts
import { cookies } from 'next/headers';

export async function GET() {
  const cookieStore = await cookies();
  const sessionId = cookieStore.get('bb-session')?.value ?? null;
  return Response.json({ sessionId });
}
// app/api/auth/signout/route.ts
import { cookies } from 'next/headers';

export async function POST() {
  const cookieStore = await cookies();
  cookieStore.delete('bb-session');
  return Response.json({ ok: true });
}

3. Project structure

A typical Next.js + BuildBase project:

app/
├── api/
│   └── auth/
│       ├── verify/route.ts     ← exchanges code for session
│       ├── session/route.ts    ← returns session from cookie
│       └── signout/route.ts    ← clears session cookie
├── (auth)/
│   ├── login/page.tsx          ← public pages
│   └── register/page.tsx
├── (app)/
│   ├── layout.tsx              ← wraps in BuildBaseProvider
│   ├── dashboard/page.tsx      ← protected pages
│   ├── settings/page.tsx
│   └── billing/page.tsx
├── layout.tsx                  ← root layout (fonts, global styles)
└── providers.tsx               ← BuildBaseProvider component

4. Protect your routes

Wrap your authenticated layout so unauthenticated users can't access app pages:

// app/(app)/layout.tsx
'use client';

import {
  useSaaSAuth,
  WhenAuthenticated,
  WhenUnauthenticated,
} from '@buildbase/sdk/react';

export default function AppLayout({ children }: { children: React.ReactNode }) {
  const { isLoading, signIn } = useSaaSAuth();

  if (isLoading) return <LoadingSpinner />;

  return (
    <>
      <WhenAuthenticated>{children}</WhenAuthenticated>
      <WhenUnauthenticated>
        <button onClick={() => signIn()}>Sign In</button>
      </WhenUnauthenticated>
    </>
  );
}

5. Dashboard setup checklist

Before going live, make sure these are configured in the BuildBase dashboard:

StepWhereWhat to do
Create orgDashboard homeClick "Create Organization"
Get credentialsSettings → GeneralCopy orgId
Set up OAuthUser Management → Access → AuthenticationCreate a client, get clientId and clientSecret, add redirect URLs
Add auth methodsUser Management → Access → Authentication → MethodsEnable Email/Password, Google, or Magic Link
Create plansBilling → PlansSet up at least one plan (if using billing)
Connect StripeBilling → CredentialsEnter Stripe API keys (test keys for dev)
Set up email senderMessaging → Sender AccountsConfigure Google, Mailgun, or SMTP
Add redirect URLsUser Management → Access → AuthenticationAdd your production URL (e.g. https://yourapp.com/callback)

6. Production environment

For production, update your environment variables:

# .env.production
NEXT_PUBLIC_BUILDBASE_SERVER_URL=https://api.console.buildbase.app
NEXT_PUBLIC_BUILDBASE_ORG_ID=your-org-id
NEXT_PUBLIC_BUILDBASE_CLIENT_ID=your-client-id
NEXT_PUBLIC_BUILDBASE_REDIRECT_URL=https://yourapp.com/callback

BUILDBASE_CLIENT_SECRET=your-client-secret
SESSION_SECRET=your-production-secret

Key differences from development:

  • REDIRECT_URL must be your production domain (not localhost)
  • Add the production redirect URL in the dashboard under User Management → Access → Authentication
  • Use real Stripe keys instead of test keys
  • Set secure: true on session cookies (already handled by the NODE_ENV check above)

7. Deployment checklist

Before your first deploy:

  • Environment variables set in your hosting platform (Vercel, Railway, etc.)
  • Production redirect URL added in BuildBase dashboard
  • Auth methods enabled (email, social, magic link)
  • At least one plan created and published (if using billing)
  • Stripe connected with live keys (if using billing)
  • Email sender configured (for transactional emails)
  • Test the full sign-in → dashboard → sign-out flow in production

Next Steps

  • Authentication — Deep dive into auth methods and sessions.
  • Billing — Set up plans, checkout, and subscriptions.
  • Server SDK — Backend operations: usage recording, credits, notifications.
  • Self-Hosted — Deploy on your own infrastructure.