The architectural idea
Most SaaS starters put tenancy in the application layer: a where tenant_id = ?
that someone eventually forgets. GStack pushes it into Postgres. Every table is
tenant-scoped by row-level security through a current_tenant_id() helper, so a
missing filter in application code is not a data leak. Roles (member /
admin) are enforced the same way, with page metadata handling UX only.
That is the distinction I care about: the database is the gate, the UI is the convenience.
Feature layers
The codebase is split into ten Nuxt layers - account, admin, analytics,
billing, email, feedback, marketing, notes, tour, ui. Each owns
its pages, composables, and types, and most can be switched off with an
environment variable. notes is a deliberately boring CRUD example that shows
the pattern to copy.
Layers are not decoration here. They mean a feature can be deleted without grepping the whole app, and a new domain can be added without touching existing ones.
Operational maturity
- Billing via Polar checkout, customer portal, and webhook, with plan gating
through a
useSubscription()composable - Notifications - a database trigger fans an event out to tenant admins, a webhook mirrors it to transactional email
- Observability - Sentry on client and server, PostHog analytics, and feature flags that degrade safely when the service is unreachable
- CI - lint, typecheck, tests, plus dedicated accessibility and screenshot
workflows; screenshots in the README regenerate with
pnpm screenshots
