Authentication
| Piece | Role |
|---|---|
| Better Auth | Email/password sessions for quiz creators |
| Drizzle ORM | Type-safe SQL + migrations |
| Cloudflare D1 | SQLite database binding on the Worker |
This follows the Hono Better Auth on Cloudflare pattern, adapted for D1 instead of Neon Postgres so the API stays fully on Cloudflare Workers.
Auth routes
Section titled “Auth routes”Better Auth is mounted at:
/api/auth/*Examples (local API on port 8787):
POST /api/auth/sign-up/emailPOST /api/auth/sign-in/emailPOST /api/auth/sign-outGET /api/auth/get-session
See Better Auth docs for the full route list. Quiz CRUD routes will require a valid session (feature work).
Registration
Section titled “Registration”Email/password sign-up is enabled. Each account must use a unique email address.
| Layer | Enforcement |
|---|---|
| Database | user.email has a unique index (user_email_unique) in D1 |
| Better Auth | Duplicate sign-up returns 422 with USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL |
emailVerified is stored but not required for sign-in during this project phase.
Out of scope (for now)
Section titled “Out of scope (for now)”The following auth features are intentionally deferred:
- Email verification — no verification emails, no
requireEmailVerification, no verify-before-login flow - Password reset email — no
sendResetPasswordor forgot-password flow - Outbound email delivery — no Resend, Cloudflare Email Sending, or similar provider wired up
Sign-up and sign-in work with email and password only. Email verification can be added later via Better Auth’s emailVerification.sendVerificationEmail hook and a mail provider (for example Cloudflare Email Sending or Resend).
Local setup
Section titled “Local setup”Run the root setup script (copies env templates, migrates local D1, installs Playwright):
pnpm setupOr manually:
cp apps/api/.dev.vars.example apps/api/.dev.varspnpm --filter @quiz-builder/api db:migrate:localStart the API:
pnpm dev:apiwrangler dev loads .dev.vars and a local D1 database under .wrangler/state/.
Environment variables
Section titled “Environment variables”| Variable | Purpose |
|---|---|
BETTER_AUTH_URL | Public API base URL (e.g. http://127.0.0.1:8787) |
BETTER_AUTH_SECRET | Signing secret — ≥ 32 chars; use a unique value in production |
Set production values as Cloudflare Worker secrets when deploying.
Database workflow
Section titled “Database workflow”| Command | Purpose |
|---|---|
pnpm --filter @quiz-builder/api auth:generate-schema | Regenerate Better Auth Drizzle tables after config changes |
pnpm --filter @quiz-builder/api db:generate | Generate SQL migrations from src/db/schema |
pnpm --filter @quiz-builder/api db:migrate:local | Apply migrations to local D1 |
pnpm --filter @quiz-builder/api db:migrate:remote | Apply migrations to remote D1 |
Schema layout:
apps/api/src/db/ client.ts # drizzle(env.DB) schema/ auth.ts # Better Auth tables (CLI-generated) index.tsapps/api/drizzle/ # SQL migrationsQuiz tables will be added to src/db/schema/ alongside auth in a later phase.
The API enables credentials: true for browser cookie sessions from the Vite dev server (localhost:5173 / 127.0.0.1:5173).
Web client (next step)
Section titled “Web client (next step)”Install the Better Auth React client in apps/web and point it at BETTER_AUTH_URL when building login/register pages.