Quickback Docs

Worker Setup

Deploy Account UI to Cloudflare Workers

Worker Setup

Deploy the Account UI as a Cloudflare Worker with static asset serving.

Quick Start

Using the Template

npx degit Kardoe-com/quickback-better-auth-account-ui my-account-app
cd my-account-app
npm install

Using the Library

If you're consuming Account UI as an npm package, re-export the worker from the library:

src/worker.ts
export { default } from 'quickback-better-auth-account-ui/worker';

Then continue with the Wrangler configuration below.

2. Configure Wrangler

The template includes a wrangler.toml pre-configured for Cloudflare Workers:

wrangler.toml
name = "my-account-app"
compatibility_date = "2025-01-01"
compatibility_flags = ["nodejs_compat"]

# Custom domain (uncomment and set your domain)
# routes = [
#   { pattern = "account.example.com", custom_domain = true }
# ]

# Worker entry point
main = "src/worker.ts"

# Static assets
[assets]
binding = "ASSETS"
directory = "dist/client"

3. Build and Deploy

npm run build
npx wrangler deploy

Worker Entry Point

The worker is at src/worker.ts. It serves static assets and handles SPA routing:

  • Static asset serving via the ASSETS binding
  • SPA routing (all routes serve index.html)
  • Health check endpoint (/health)

Custom Worker Logic

Edit src/worker.ts directly to add custom logic:

src/worker.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // Custom health check with more info
    if (url.pathname === '/health') {
      return new Response(JSON.stringify({
        status: 'ok',
        app: 'my-account-ui',
        version: '1.0.0',
        timestamp: new Date().toISOString(),
      }), {
        headers: { 'Content-Type': 'application/json' }
      });
    }

    // Custom analytics or logging
    console.log(`${request.method} ${url.pathname}`);

    // Try to serve static asset
    const assetResponse = await env.ASSETS.fetch(request);

    // If asset found, return it
    if (assetResponse.status !== 404) {
      return assetResponse;
    }

    // SPA routing: serve index.html for all other routes
    const indexRequest = new Request(`${url.origin}/index.html`, request);
    return env.ASSETS.fetch(indexRequest);
  },
};

Wrangler Configuration

Environment Variables

For production deployment, set environment variables in Cloudflare dashboard or via wrangler:

wrangler.toml
[env.production]
name = "my-account-app"

[env.production.vars]
VITE_API_URL = "https://api.example.com"
VITE_ACCOUNT_APP_URL = "https://account.example.com"
VITE_APP_NAME = "My App"
VITE_APP_URL = "https://app.example.com"

Build-Time Variables

Vite environment variables (VITE_*) are baked into the build at build time. Changing them in wrangler.toml requires rebuilding. Use .env.production for production builds.

Multiple Environments

wrangler.toml
# Development
[env.dev]
name = "my-account-app-dev"
route = "account-dev.example.com/*"

[env.dev.vars]
VITE_API_URL = "https://api-dev.example.com"
VITE_ACCOUNT_APP_URL = "https://account-dev.example.com"

# Staging
[env.staging]
name = "my-account-app-staging"
route = "account-staging.example.com/*"

[env.staging.vars]
VITE_API_URL = "https://api-staging.example.com"
VITE_ACCOUNT_APP_URL = "https://account-staging.example.com"

# Production
[env.production]
name = "my-account-app"
route = "account.example.com/*"

[env.production.vars]
VITE_API_URL = "https://api.example.com"
VITE_ACCOUNT_APP_URL = "https://account.example.com"

Deploy to specific environment:

wrangler deploy --env staging
wrangler deploy --env production

Cloudflare Pages Alternative

You can also deploy to Cloudflare Pages instead of Workers:

1. Configure Build

wrangler.toml
# Remove [assets] section - Pages handles this

name = "my-account-app"
pages_build_output_dir = "dist/client"

2. Deploy to Pages

# First time setup
wrangler pages project create my-account-app

# Deploy
wrangler pages deploy dist/client

3. Configure Environment Variables

Set environment variables in Cloudflare Pages dashboard:

  1. Go to your Pages project
  2. SettingsEnvironment Variables
  3. Add VITE_* variables
  4. Redeploy

Pages vs Workers

Workers: Better for custom server logic, middleware, edge compute Pages: Simpler for static SPAs, automatic preview deployments, built-in analytics

Local Development

Option 1: Vite Dev Server

The fastest way to develop locally:

npx vite dev

This starts the Vite dev server with hot module replacement on localhost:5173.

Option 2: Wrangler Dev

Test the worker locally with wrangler dev:

npm run build && wrangler dev

This runs the worker on localhost:8787.

Health Check

The worker includes a built-in health check endpoint:

curl https://account.example.com/health

Response:

{
  "status": "ok",
  "app": "auth-ui"
}

Use this for:

  • Uptime monitoring
  • Load balancer health checks
  • Deployment verification

Security Headers

Add security headers by editing src/worker.ts:

src/worker.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // ... routing logic ...

    // Add security headers to response
    const response = await env.ASSETS.fetch(request);

    const headers = new Headers(response.headers);
    headers.set('X-Frame-Options', 'DENY');
    headers.set('X-Content-Type-Options', 'nosniff');
    headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
    headers.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');

    return new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers,
    });
  },
};

Custom 404 Page

Serve a custom 404 page for unmatched routes:

src/worker.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // Try to serve static asset
    const assetResponse = await env.ASSETS.fetch(request);

    if (assetResponse.status !== 404) {
      return assetResponse;
    }

    // Serve index.html for SPA routes
    if (!url.pathname.startsWith('/api/')) {
      const indexRequest = new Request(`${url.origin}/index.html`, request);
      return env.ASSETS.fetch(indexRequest);
    }

    // Custom 404 for API routes
    return new Response('Not Found', { status: 404 });
  },
};

Deployment Checklist

Before deploying to production:

  • Set all required environment variables (VITE_API_URL, etc.)
  • Configure custom domain in Cloudflare
  • Add SSL certificate (automatic with Cloudflare)
  • Test all authentication flows
  • Configure DNS records
  • Set up monitoring/health checks
  • Test email sending
  • Verify passkey functionality (requires HTTPS)
  • Check CORS configuration on API
  • Review security headers
  • Test organization invitations
  • Verify redirection to main app works

Troubleshooting

Assets Not Loading

Problem: 404 errors for static assets (JS, CSS)

Solution: Check directory path in wrangler.toml:

[assets]
binding = "ASSETS"
directory = "dist/client"  # Must point to Vite's client build output

Environment Variables Not Working

Problem: Config values are undefined

Solution: Remember that VITE_* variables are build-time. Either:

  1. Set them in .env.production before building
  2. Use setAppConfig() in src/main.tsx for runtime overrides
  3. Edit src/config/app.ts defaults directly

SPA Routing Issues

Problem: Refreshing on /profile returns 404

Solution: Ensure your worker serves index.html for all non-asset routes:

if (assetResponse.status === 404 && !url.pathname.startsWith('/api/')) {
  return env.ASSETS.fetch(new Request(`${url.origin}/index.html`, request));
}

Next Steps

On this page