SaaS Starter

Welcome

A production-ready Bun + Express + Prisma + Next.js boilerplate with DDD layering, BetterAuth, billing, observability and a UseDeploy design system.

What this is

An opinionated SaaS starter built around four constraints:

  • Domain-Driven Design, enforced by ESLint — domain/ cannot import express, @prisma/client, or anything from application/ / infrastructure/.
  • Cookie-based auth via BetterAuth (Postgres-backed sessions, magic link, Google OAuth, organizations).
  • Provider-agnostic billing — Stripe, Mercado Pago and Polar share one PaymentProvider interface.
  • Typed contracts end-to-end — Zod schemas in @app/contracts are the single source of truth; the OpenAPI spec is regenerated and committed on every endpoint change.

What's wired

CapabilityWhere it livesNotes
HTTP serverapps/server/src/index.tsOTel boots first, then Express, then routes
DDD modulesapps/server/src/modules/<context>/iam, billing, tenancy, notifications, storage, audit, feature-flags
RBACpackages/shared/src/permissions/resource:action strings, requirePermission(p) middleware
Background jobsapps/server/src/infrastructure/jobs/BullMQ, Bull Board UI at /admin/queues
Domain eventsapps/server/src/infrastructure/events/event-bus.tsIn-memory bus, listeners wire in bootstrap
Storageapps/server/src/modules/storage/null / local / s3 / uploadthing providers
Rate limitingapps/server/src/infrastructure/http/rate-limit.tsMemoized tier limiters with separate Redis prefixes
Observabilityapps/server/src/infrastructure/observability/OTel + Prometheus /metrics, Sentry server (@sentry/bun)
Frontendapps/client/Next 16, Tailwind v4, Fumadocs, UseDeploy design system in components/brand/*

30-second tour

git clone <repo> my-saas
cd my-saas
bun install
cp apps/server/.env.example apps/server/.env
bunx prisma migrate dev --schema apps/server/prisma/schema.prisma
bun run dev          # client :3004, server :3005, docs at /docs

The minimum env to boot: BETTER_AUTH_SECRET, BETTER_AUTH_URL, DATABASE_URL, CORS_ORIGINS. Every optional adapter (Sentry, OTel, S3, UploadThing, Redis) boots to a no-op when its config is unset.

Where to next

On this page