Complete guide to set up the Better Auth template.
- Bun (recommended) or Node.js 18+
- Wrangler (installed automatically via
bun install)
git clone <your-repo-url> my-auth-app
cd my-auth-app# Backend
cd backend && bun install
# Frontend
cd ../frontend && bun installcd backend
cp .dev.vars.example .dev.varsEdit .dev.vars:
BETTER_AUTH_SECRET=your_secret_here_min_32_charsOptional (for OAuth and email):
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
TWITTER_CLIENT_ID=your_twitter_client_id
TWITTER_CLIENT_SECRET=your_twitter_client_secret
RESEND_API_KEY=re_your_api_key
RESEND_FROM_EMAIL=noreply@yourdomain.comcd backend
cp .env.example .envEdit .env with the same variables above, plus:
PORT=4200
DB_PATH=./data/local.db
BETTER_AUTH_URL=http://localhost:4200
TRUSTED_ORIGINS=http://localhost:4000
APP_URL=http://localhost:4000cd frontend
cp .env.example .envEdit .env:
VITE_API_URL=http://localhost:4200
VITE_APP_URL=http://localhost:4000If you run Cloudflare Workers (bun run dev), set VITE_API_URL=http://localhost:8787 instead.
cd backend
# Apply migrations locally
bun run db:migrate:localDatabase is created automatically when you run bun run dev:node. Migrations are applied on startup.
Terminal 1 — Backend:
cd backend
bun run dev # Cloudflare Workers (port 8787)
# or
bun run dev:node # Node.js / Bun (port 4200)Terminal 2 — Frontend:
cd frontend
bun run devVisit http://localhost:4000
- Click "Sign in"
- Switch to "Sign up" mode
- Enter name, email, password (8+ chars)
- Create account
- Enter email address
- Check backend console for OTP code (in dev)
- Enter the 6-digit code
- Enter email address
- Check backend console for magic link URL (in dev)
- Click or paste the link
- Set up credentials (see GOOGLE_OAUTH_SETUP.md)
- Click "Sign in with Google" or "Sign in with Twitter"
Better Auth creates these tables (SQLite/D1):
-- User profiles
CREATE TABLE user (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
emailVerified INTEGER DEFAULT 0,
image TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL
);
-- Sessions
CREATE TABLE session (
id TEXT PRIMARY KEY,
userId TEXT REFERENCES user(id),
expiresAt TEXT NOT NULL,
token TEXT UNIQUE,
ipAddress TEXT,
userAgent TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL
);
-- Linked accounts (OAuth, credentials)
CREATE TABLE account (
id TEXT PRIMARY KEY,
userId TEXT REFERENCES user(id),
accountId TEXT NOT NULL,
providerId TEXT NOT NULL,
accessToken TEXT,
refreshToken TEXT,
password TEXT,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL
);
-- Verification tokens (OTP, magic link, email verify)
CREATE TABLE verification (
id TEXT PRIMARY KEY,
identifier TEXT NOT NULL,
value TEXT NOT NULL,
expiresAt TEXT NOT NULL,
createdAt TEXT,
updatedAt TEXT
);- Check Vite proxy points to correct backend port
- Ensure
credentials: "include"in auth client - Verify CORS origins match frontend URL
- Ensure callback URL in provider dashboard matches
BETTER_AUTH_URL - Check that
BETTER_AUTH_URLis set correctly (notAPP_URL) - Verify no extra spaces in env values
- If using Cloudflare Tunnel, add the current tunnel URL to
TRUSTED_ORIGINSand setAPP_URLto that same URL
# Check migration status
npx wrangler d1 migrations list DB --local
# Re-apply
bun run db:migrate:local