Feature Flag & Gradual Rollout / Experimentation platform (BaaS). Next.js + TypeScript frontend, 3 independent Node.js/Express microservices, PostgreSQL via Prisma.
apps/
web/ Next.js 15 frontend (App Router) — port 3000
auth-service/ signup/login/OTP/sessions/invites — port 4001
admin-service/ platform admin: users, orgs, plans, analytics — port 4002
core-service/ the actual BaaS: flags, segments, experiments, SDK eval — port 4003
packages/
db/ shared Prisma schema + client + seed script
shared/ Zod schemas, constants, and types shared by all 4 apps
One Postgres database, one Prisma schema, three independently deployable
services that each only touch the tables they own, and the Next.js frontend
never calls them directly — next.config.ts's rewrites() proxies
/api/auth/*, /api/admin/*, /api/core/* to the right service, the same
way the Vite server.proxy example worked.
- Node.js 20+
- Docker (for local Postgres) — or your own Postgres 14+ instance
# 1. Install everything (npm workspaces — one install for the whole monorepo)
npm install
# 2. Start Postgres
docker compose up -d
# 3. Copy env files — fill in JWT_ACCESS_SECRET / JWT_REFRESH_SECRET
# (must match exactly across all 3 services) and DATABASE_URL
cp .env.example .env
cp apps/auth-service/.env.example apps/auth-service/.env
cp apps/core-service/.env.example apps/core-service/.env
cp apps/admin-service/.env.example apps/admin-service/.env
cp apps/web/.env.example apps/web/.env
# 4. Generate the Prisma client and run migrations
npm run db:generate
npm run db:migrate
# 5. Seed plans, a bootstrap Platform Admin account, and starter
# knowledge-base articles
npm run db:seed
# Watch the console output — it prints the bootstrap admin email
# and password (or set SEED_ADMIN_EMAIL / SEED_ADMIN_PASSWORD first).
# 6. Run everything (separate terminals, or use a process manager of choice)
npm run dev:auth
npm run dev:core
npm run dev:admin
npm run dev:webVisit http://localhost:3000. Sign in to /admin/login with the bootstrap
admin credentials printed by the seed script.
- SendGrid not configured?
auth-serviceprints OTP codes and email content straight to its own console instead of failing — look for aEMAIL FALLBACKblock in that terminal. - OpenAI not configured? Rollout suggestions and anomaly detection fall
back to deterministic, rule-based logic automatically — the feature never
errors, it just won't say
"source": "ai". - Cloudinary — configured the same way as the snippet you provided;
wire
CLOUDINARY_CLOUD_NAME/CLOUDINARY_API_KEY/CLOUDINARY_API_SECRETinto whichever service ends up handling file uploads for your use case. - All placeholder URLs (
https://rollentz.example.com, webhook examples, etc.) should be swapped for your real domain after your first deployment.
Each of the 4 apps deploys independently (4 separate services/containers).
Point AUTH_SERVICE_URL / ADMIN_SERVICE_URL / CORE_SERVICE_URL in the
web app's environment at your real service URLs, keep JWT_ACCESS_SECRET
identical across all 3 backend services, and run npm run db:migrate:deploy
(via packages/db) as part of your release process rather than migrate dev.