Auth Β· Organizations & fine-grained RBAC Β· Stripe billing Β· usage-based metering Β· admin panel with impersonation Β· audit logs Β· durable jobs β every functionality your SaaS needs, so you just build the product. Runs with zero configuration.
Basework is a starter codebase for building a multi-tenant SaaS product on Next.js. It is for developers who want auth, organizations, billing, usage metering and an admin panel already in place, so they can focus on their own product. It runs locally with mock providers and no keys, and switches to real Postgres, Stripe, email and LLM providers when the matching .env values are set.
Basework isn't tied to one product idea β it's the complete foundation every SaaS needs. You bring the product (a CRM, an analytics tool, a dev platform, an AI app β anything); it brings everything around it, and you delete the parts you don't use.
- π Usage-based metering & billing. Meter any action β API calls, exports, AI tokens β as per-org credits, enforce plan limits, and report overage to Stripe. The included AI assistant is just one example consumer. Almost no template ships this.
- π’ Real multi-tenancy. Organizations, members, invitations, and a fine-grained permission policy (not bare role strings). Tenant isolation is covered by tests.
- π³ Billing done right. Checkout β signature-verified webhook β database sync, customer portal, plan changes that reset credits. The "everyone fakes it" part is real and tested.
- π‘οΈ Admin panel with secure impersonation, feature flags, and an immutable audit log.
- β‘ Zero-config dev.
pnpm devboots a fully seeded demo with no accounts and no keys β embedded Postgres (PGlite), plus mock email / billing / AI providers. Flip to production by filling.env. - β Genuinely tested. Typecheck, lint, unit + integration (in-memory Postgres), and Playwright E2E β all in CI with no external services.
The 30-second pitch: clone it, run
pnpm dev, and you have a multi-tenant dashboard with working billing, usage metering, an admin panel, and a streaming AI example β before you've created a single account anywhere. Then delete what you don't need and build your product.
git clone https://github.com/fizzexual/BaseworkSaaS.git
cd BaseworkSaaS
pnpm install
pnpm devOpen http://localhost:3000 and sign in with the seeded demo accounts:
| Account | Password | Role | |
|---|---|---|---|
| Owner / super-admin | admin@basework.dev |
password123 |
owner + admin |
| Member | member@basework.dev |
password123 |
member |
No .env, no database, no Stripe account, no API keys. It just runs.
| AI assistant (streamed + metered) | Billing & plans |
|---|---|
![]() |
![]() |
| Usage ledger | Admin panel |
|---|---|
![]() |
![]() |
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, RSC, Server Actions), React 19, TypeScript (strict) |
| UI | Tailwind CSS v4, Radix primitives, a token-driven design system with light & dark themes |
| Database | Drizzle ORM β embedded PGlite in dev β Postgres / Neon in prod |
| Auth | Better Auth β email+password, OAuth, organizations, admin + impersonation |
| Billing | Stripe (subscriptions + usage metering) behind a provider interface, with a mock provider for zero-config |
| AI | Vercel AI SDK β streaming chat, credit metering, bring-your-own keys; mock provider when no key |
| Infra | durable job queue, feature flags, per-plan rate limiting, structured logging, audit log, React Email + Resend |
| Tooling | Biome, Vitest, Playwright, GitHub Actions |
Basework has two modes, chosen automatically from the environment:
DATABASE_URL empty β embedded PGlite (Postgres in-process) | set it β Postgres / Neon
STRIPE_SECRET_KEY β mock billing (simulated checkout) | set it β real Stripe
OPENAI/ANTHROPIC key β mock AI (deterministic streaming) | set it β real LLM
RESEND_API_KEY empty β console email transport | set it β Resend
The credit-metering flow is the heart of it:
chat request β check org credit balance / plan overage policy
β stream the model response (Vercel AI SDK)
β on finish: count tokens β deduct credits (atomic, ledgered)
β report overage to Stripe as a metered usage event
Every component is built on semantic design tokens (bg-card, text-foreground, bg-brand, β¦) defined once in src/app/globals.css β so the whole app re-skins from one file. Two switches ship out of the box:
- Light / dark mode β a persisted toggle (next-themes) in the sidebar and marketing header. The
.darkclass overrides the palette vars via@theme inline; no per-component changes. - Nav layout β a left sidebar rail or a horizontal top bar. Seed the default with
NEXT_PUBLIC_NAV_LAYOUT=topnav, or flip it live from the admin panel (below).
A super-admin (global admin role or a SUPER_ADMIN_EMAILS address) configures the whole app at runtime from /admin/settings β no redeploy:
- Appearance β nav layout, default theme, brand name, and accent color, applied app-wide and live.
- Feature modules β switch AI, Billing, Members, or Usage off; a disabled module disappears from the nav, its pages redirect, and its API route / server actions reject (enforced server-side, not just in the UI).
- Access β close sign-ups (invite-only mode β unexpired invitations still work) or flip on maintenance mode, which locks the app for non-admins in both the UI and at the mutation boundary.
Settings persist in a platform_settings singleton row; module on/off rides the existing feature-flags table. The env flags seed the initial defaults, and the stored values override them once set β so it still boots zero-config.
- Copy
.env.exampleto.envand fill the values. - Provision Postgres (e.g. Neon) and set
DATABASE_URL. pnpm db:migrateto apply migrations.- Create Stripe products/prices (
pnpm stripe:sync) and setSTRIPE_*. - Set
BETTER_AUTH_SECRET,ENCRYPTION_KEY, and an LLM key. - Deploy. Point a cron at
POST /api/jobs/tickto drain the job queue.
Run pnpm doctor anytime to see which mode each subsystem is in.
src/
app/
(marketing)/ # landing + pricing
(auth)/ # sign-in / sign-up / accept-invitation
dashboard/ # app shell: overview, ai, members, usage, billing, settings
admin/ # super-admin: platform settings, users, flags, audit
api/ # better-auth, stripe webhook, ai chat, jobs
lib/
auth/ db/ billing/ ai/ rbac/ email/ jobs/ flags/ modules/ settings/ ratelimit/ observability/ env
server/ # request context + server actions
drizzle/ # schema migrations
tests/ # vitest (unit + integration) and playwright (e2e)
| Command | Description |
|---|---|
pnpm dev |
Run the app (zero-config) |
pnpm build / pnpm start |
Production build / serve |
pnpm typecheck |
tsc --noEmit |
pnpm lint / pnpm lint:fix |
Biome check / autofix |
pnpm test |
Vitest (in-memory Postgres) |
pnpm test:e2e |
Playwright end-to-end |
pnpm db:generate / pnpm db:migrate |
Drizzle migrations |
pnpm stripe:sync |
Create Stripe products/prices/meters |
pnpm doctor |
Print the active runtime modes |
| Basework | Typical boilerplate | |
|---|---|---|
| Usage-based metering & credits | β | β |
| Multi-tenant orgs + fine-grained RBAC | β | partial |
| Admin impersonation | β | β |
| Tested Stripe webhook sync | β | β |
| Zero-config runnable demo | β | β |
| Audit log + durable jobs | β | rare |
| 100% open source (MIT) | β | varies |
Contributions are welcome β see CONTRIBUTING.md. Please keep
pnpm typecheck, pnpm lint, pnpm test, and pnpm build green.
MIT β use it for anything, including commercial products.



