AI Template

May 1, 2026 · View on GitHub

Multi-tenant SaaS boilerplate built on FastAPI + authx + Next.js. Every feature toggleable via env vars; ship a production-ready SaaS in a weekend.

Backend Frontend Admin

What you get

  • Organizations / Workspaces with per-org data isolation (every tenant row carries org_id).
  • Roles: Owner / Admin / Member, mapped to authx scopes with wildcard support (org:members:*).
  • JWT scoped to org — switch orgs and the access token is re-issued with the new org_id and computed scopes.
  • API keys per organization with scoped permissions, validated via X-API-Key.
  • Email invitations — or token-link only when email delivery is disabled.
  • Optional, all gated by FEATURE_* env vars: OAuth (Google/GitHub), magic-link, real email delivery (Resend/SMTP), Stripe billing, Pydantic AI + LLM Gateway (chat + demo agents), Logfire observability, audit log, rate limiting.

Repository layout

ai-template/
├── backend/         FastAPI + authx + SQLAlchemy + Alembic (uv-managed)
├── frontend/        Next.js 16 tenant-facing app (Tailwind v4, Framer Motion, Zod)
├── admin/           Next.js 16 super-admin portal
├── infra/           docker-compose + postgres init
├── .env.example     every FEATURE_* and secret in one place
└── Makefile         `make dev`, `make test`, `make migrate`, ...

Stack

LayerTech
BackendFastAPI, Pydantic v2, authx 1.6.0, SQLAlchemy 2 async, Alembic, Postgres 17, Redis 7, uv
FrontendNext.js 16, React 19, Tailwind v4, Framer Motion, Zod, bun
Admin PortalNext.js 16, React 19, Tailwind v4, Framer Motion, Zod, bun
OptionalPydantic AI, LLM Gateway, Pydantic Logfire, Resend, Stripe, Authlib

Quick start

Prereqs: uv ≥ 0.5, bun ≥ 1.3, Docker.

git clone https://github.com/yezz123/ai-template && cd ai-template
cp .env.example .env

make stack-build
make stack-up
make stack-ps
  • Backend: http://localhost:8000 (OpenAPI UI: http://localhost:8000/docs)
  • Frontend: http://localhost:3000
  • Admin: http://localhost:3001
  • Mailpit: http://localhost:8025
make stack-logs

Option B: run infra in Docker + apps locally (fast inner loop)

git clone https://github.com/yezz123/ai-template && cd ai-template
cp .env.example .env

make bootstrap          # uv sync + bun install
make up                 # postgres + redis + mailpit
make migrate            # alembic upgrade head

# In three separate terminals:
make dev-backend        # http://localhost:8000  (FastAPI + /docs)
make dev-frontend       # http://localhost:3000  (tenant app)
make dev-admin          # http://localhost:3001  (super-admin)

Feature flags

Every optional module is gated by an env var. Disabled features incur zero runtime cost — their routers are never registered, their dependencies never imported.

FlagDefaultModule
FEATURE_API_KEYStrueAPI keys per org (X-API-Key)
FEATURE_AUDIT_LOGtrueAudit log of mutations
FEATURE_RATE_LIMITINGtrueauthx RateLimiter + Redis
FEATURE_OAUTHfalseGoogle + GitHub via Authlib
FEATURE_MAGIC_LINKfalsePasswordless
FEATURE_EMAIL_DELIVERYfalseResend / SMTP
FEATURE_AI_CHATfalsePydantic AI + LLM Gateway
FEATURE_LOGFIREfalsePydantic Logfire tracing
FEATURE_STRIPEfalsePer-org subscriptions

Optional Python deps live behind pyproject.toml extras:

uv sync --extra ai --extra stripe       # only what you need
uv sync --extra all                     # everything

Frontend and Admin feature visibility is gated by NEXT_PUBLIC_FEATURE_* vars which are baked into the Next.js bundle at build time (see .env.example).

Multi-tenancy model

  • Shared database, shared schema. Every tenant row has org_id UUID NOT NULL with composite indexes.
  • The active org lives in the JWT (data.org_id + data.role + scopes).
  • current_org FastAPI dep extracts the org from the token and every repository function takes it as a required argument.
  • POST /orgs/{slug}/switch re-issues the token pair with new claims.

Roles → scopes

ROLE_SCOPES = {
    Role.OWNER:  ["org:*"],
    Role.ADMIN:  ["org:members:*", "org:apikeys:*", "org:audit:read", "org:billing:read", "org:ai:*"],
    Role.MEMBER: ["org:read", "org:ai:use"],
}

Routes use auth.scopes_required(...) — wildcards ("org:*") match everything underneath.

Development

See per-app READMEs:

make test               # all suites
make lint               # ruff + next lint
make typecheck-backend  # mypy

Troubleshooting

  • CSRF 401s on POST/PUT/PATCH/DELETE: auth is cookie-based and expects X-CSRF-TOKEN. The Next.js clients automatically mirror the CSRF cookie into that header; if you’re rolling your own client, do the same.
  • Migrations failing inside Docker: migrate runs before backend. Rebuild images after changing deps: make stack-build && make stack-up.
  • OAuth not working locally: set FEATURE_OAUTH=true and add provider keys + callback URLs. Make sure APP_BASE_URL, FRONTEND_BASE_URL, and ADMIN_BASE_URL match where you’re running.
  • “Feature is missing in UI”: ensure both FEATURE_* (backend) and NEXT_PUBLIC_FEATURE_* (frontend/admin) are enabled, and rebuild Next.js if using Docker (public env is baked at build time).

License

MIT — see LICENSE.