Skip to content

Authentication

PieceRole
Better AuthEmail/password sessions for quiz creators
Drizzle ORMType-safe SQL + migrations
Cloudflare D1SQLite 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.

Better Auth is mounted at:

/api/auth/*

Examples (local API on port 8787):

  • POST /api/auth/sign-up/email
  • POST /api/auth/sign-in/email
  • POST /api/auth/sign-out
  • GET /api/auth/get-session

See Better Auth docs for the full route list. Quiz CRUD routes will require a valid session (feature work).

Email/password sign-up is enabled. Each account must use a unique email address.

LayerEnforcement
Databaseuser.email has a unique index (user_email_unique) in D1
Better AuthDuplicate sign-up returns 422 with USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL

emailVerified is stored but not required for sign-in during this project phase.

The following auth features are intentionally deferred:

  • Email verification — no verification emails, no requireEmailVerification, no verify-before-login flow
  • Password reset email — no sendResetPassword or 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).

Run the root setup script (copies env templates, migrates local D1, installs Playwright):

Terminal window
pnpm setup

Or manually:

Terminal window
cp apps/api/.dev.vars.example apps/api/.dev.vars
pnpm --filter @quiz-builder/api db:migrate:local

Start the API:

Terminal window
pnpm dev:api

wrangler dev loads .dev.vars and a local D1 database under .wrangler/state/.

VariablePurpose
BETTER_AUTH_URLPublic API base URL (e.g. http://127.0.0.1:8787)
BETTER_AUTH_SECRETSigning secret — ≥ 32 chars; use a unique value in production

Set production values as Cloudflare Worker secrets when deploying.

CommandPurpose
pnpm --filter @quiz-builder/api auth:generate-schemaRegenerate Better Auth Drizzle tables after config changes
pnpm --filter @quiz-builder/api db:generateGenerate SQL migrations from src/db/schema
pnpm --filter @quiz-builder/api db:migrate:localApply migrations to local D1
pnpm --filter @quiz-builder/api db:migrate:remoteApply migrations to remote D1

Schema layout:

apps/api/src/db/
client.ts # drizzle(env.DB)
schema/
auth.ts # Better Auth tables (CLI-generated)
index.ts
apps/api/drizzle/ # SQL migrations

Quiz 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).

Install the Better Auth React client in apps/web and point it at BETTER_AUTH_URL when building login/register pages.