Skip to content

docs: add Nuxt + Cloudflare Pages + D1 example#8

Open
amondnet wants to merge 2 commits into
mainfrom
amondnet/freckle-ear
Open

docs: add Nuxt + Cloudflare Pages + D1 example#8
amondnet wants to merge 2 commits into
mainfrom
amondnet/freckle-ear

Conversation

@amondnet

@amondnet amondnet commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add a minimal example showing better-auth with Nuxt on Cloudflare Pages using D1 as the database
  • Demonstrates email/password authentication with SSR session management
  • Includes auth middleware, login/signup/dashboard pages, and CLI-based schema generation via npx auth generate

Changes

  • examples/nuxt-d1-cloudflare/ - complete Nuxt example app
    • wrangler.toml - Cloudflare Pages + D1 binding configuration
    • nuxt.config.ts - Nuxt config with Cloudflare preset and nitro D1 bindings
    • server/lib/auth.ts - better-auth server setup with D1 adapter
    • lib/auth-client.ts - better-auth client setup
    • middleware/auth.global.ts - global auth middleware for route protection
    • pages/ - login, signup, dashboard, and index pages
    • server/api/ - auth catch-all handler and /api/me endpoint
    • README.md - setup and deployment instructions

Test Plan

  • Review example code for correctness and completeness
  • Verify wrangler.toml and nuxt.config.ts are properly configured
  • Confirm README instructions are accurate

Summary by cubic

Adds a minimal Nuxt + Cloudflare Pages example using D1 with better-auth for email/password auth and SSR sessions. Also adds a per-request auth setup for Pages and tightens middleware/secret handling.

  • New Features

    • Example at examples/nuxt-d1-cloudflare/ with better-auth + D1 on Nuxt and Pages.
    • Email/password auth, SSR session, login/signup/dashboard pages, and a global route guard.
    • Catch‑all auth API and /api/me using auth.api.getSession(); ready-to-run wrangler.toml, nuxt.config.ts, .env.example, and schema/migration scripts.
  • Bug Fixes

    • Enforce BETTER_AUTH_SECRET at runtime; removed any fallback secret.
    • Make auth middleware client-only with import.meta.client and protect all /dashboard* routes.
    • Update README and code to use per-request getAuth(event) for Cloudflare Pages.

Written for commit 0306761. Summary will update on new commits.

Add a minimal example demonstrating better-auth with Nuxt on Cloudflare
Pages using D1 as the database. Includes email/password authentication,
SSR session management, auth middleware, login/signup/dashboard pages,
and CLI-based schema generation via `npx auth generate`.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 18 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="examples/nuxt-d1-cloudflare/nuxt.config.ts">

<violation number="1" location="examples/nuxt-d1-cloudflare/nuxt.config.ts:9">
P1: Do not fall back to a hardcoded auth secret; require `BETTER_AUTH_SECRET` so production cannot start with a predictable signing key.</violation>
</file>

<file name="examples/nuxt-d1-cloudflare/pages/signup.vue">

<violation number="1" location="examples/nuxt-d1-cloudflare/pages/signup.vue:14">
P2: Handle thrown signup errors with `try/catch/finally` so loading state is always cleared and failures are shown to the user.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Browser as Client (Nuxt/Vue)
    participant Middleware as Auth Middleware
    participant Server as Nitro Server (Cloudflare Worker)
    participant Auth as better-auth (Server)
    participant D1 as Cloudflare D1 (Database)

    Note over Browser,D1: SSR Session Validation / Route Protection

    Browser->>Middleware: NEW: Request /dashboard
    Middleware->>Browser: NEW: authClient.useSession(useFetch)
    Browser->>Server: GET /api/auth/get-session
    Server->>Auth: NEW: getAuth(event)
    Note right of Auth: Reads env.DB & BETTER_AUTH_SECRET
    Auth->>D1: Query session/user data
    D1-->>Auth: Record found
    Auth-->>Server: Session object
    Server-->>Browser: 200 OK (Session JSON)
    
    alt Session exists
        Middleware-->>Browser: Allow navigation to Dashboard
    else Session missing
        Middleware-->>Browser: NEW: navigateTo('/login')
    end

    Note over Browser,D1: User Signup Flow

    Browser->>Server: NEW: POST /api/auth/signup
    Server->>Auth: auth.handler()
    Auth->>D1: NEW: Create User & Session records
    D1-->>Auth: Success
    Auth-->>Server: Set-Cookie (session_token)
    Server-->>Browser: 200 OK

    Note over Browser,D1: Protected API Route Flow

    Browser->>Server: GET /api/me
    Server->>Auth: NEW: auth.api.getSession(headers)
    Auth->>D1: Validate session token from headers
    D1-->>Auth: Valid user record
    Auth-->>Server: Session/User data
    alt Authorized
        Server-->>Browser: { user: ... }
    else 401 Unauthorized
        Server-->>Browser: Throw 401 Error
    end
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread examples/nuxt-d1-cloudflare/nuxt.config.ts Outdated
error.value = ''
loading.value = true

const { error: signUpError } = await authClient.signUp.email({

@cubic-dev-ai cubic-dev-ai Bot Mar 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Handle thrown signup errors with try/catch/finally so loading state is always cleared and failures are shown to the user.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/nuxt-d1-cloudflare/pages/signup.vue, line 14:

<comment>Handle thrown signup errors with `try/catch/finally` so loading state is always cleared and failures are shown to the user.</comment>

<file context>
@@ -0,0 +1,60 @@
+  error.value = ''
+  loading.value = true
+
+  const { error: signUpError } = await authClient.signUp.email({
+    name: name.value,
+    email: email.value,
</file context>
Fix with Cubic

- Remove hardcoded fallback secret, add runtime guard for missing
  BETTER_AUTH_SECRET
- Fix middleware to only run on client side (import.meta.client guard)
- Protect all /dashboard sub-routes with startsWith
- Update README code snippet to show per-request getAuth(event) pattern

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="examples/nuxt-d1-cloudflare/middleware/auth.global.ts">

<violation number="1" location="examples/nuxt-d1-cloudflare/middleware/auth.global.ts:4">
P1: The new client-only early return disables auth checks during SSR, so protected `/dashboard` routes are no longer enforced server-side.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

import { authClient } from '~/lib/auth-client'

export default defineNuxtRouteMiddleware(async (to) => {
if (!import.meta.client) {

@cubic-dev-ai cubic-dev-ai Bot Mar 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The new client-only early return disables auth checks during SSR, so protected /dashboard routes are no longer enforced server-side.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/nuxt-d1-cloudflare/middleware/auth.global.ts, line 4:

<comment>The new client-only early return disables auth checks during SSR, so protected `/dashboard` routes are no longer enforced server-side.</comment>

<file context>
@@ -1,9 +1,13 @@
 import { authClient } from '~/lib/auth-client'
 
 export default defineNuxtRouteMiddleware(async (to) => {
+  if (!import.meta.client) {
+    return
+  }
</file context>
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant