my-claude-code-setup/CLAUDE-cloudflare-mini.md

11 KiB

CLAUDE.md - Cloudflare Quick Template

Project: [NAME]

Auth: Clerk | Platform: Cloudflare Workers/Pages


CRITICAL: Verify Before Coding

Step 1: Detect Stack

Inspect project files:

  • package.json -> Node/TS (check deps: hono, next, remix, astro)
  • requirements.txt / pyproject.toml -> Python Workers
  • go.mod -> Go | Cargo.toml -> Rust
  • index.html (no package.json) -> Static HTML (use Pages Git Integration)
  • No files? -> Run npm create cloudflare@latest

Step 2: Fetch Documentation

Use MCP tools if available, otherwise web fetch these URLs:

Cloudflare:

Service URL
Workers https://developers.cloudflare.com/workers/
D1 Get Started https://developers.cloudflare.com/d1/get-started/
KV https://developers.cloudflare.com/kv/
R2 https://developers.cloudflare.com/r2/
Durable Objects https://developers.cloudflare.com/durable-objects/
Pages https://developers.cloudflare.com/pages/
Pages Git Integration https://developers.cloudflare.com/pages/get-started/git-integration/
Pages Functions https://developers.cloudflare.com/pages/functions/

Clerk:

Resource URL
JS Backend SDK https://clerk.com/docs/js-backend/getting-started/quickstart
authenticateRequest() https://clerk.com/docs/reference/backend/authenticate-request
Hono Middleware https://github.com/honojs/middleware/tree/main/packages/clerk-auth
Next.js https://clerk.com/docs/reference/nextjs/overview

Frameworks:

Framework URL
Hono https://hono.dev/docs/getting-started/cloudflare-workers
Next.js https://developers.cloudflare.com/pages/framework-guides/nextjs/
Drizzle+D1 https://orm.drizzle.team/docs/get-started/d1-new

Wrangler Config Reference

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "app",
  "main": "src/index.ts",
  "compatibility_date": "2024-12-01",
  "compatibility_flags": ["nodejs_compat"],
  "d1_databases": [{ "binding": "DB", "database_name": "x", "database_id": "x" }],
  "kv_namespaces": [{ "binding": "KV", "id": "x" }],
  "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "x" }]
}

Cloudflare Pages: Static HTML via GitHub

For static HTML sites (no build step). Docs: https://developers.cloudflare.com/pages/get-started/git-integration/

Setup (Dashboard)

  1. Dashboard > Workers & Pages > Create > Pages > Connect to Git
  2. Select GitHub repo
  3. Build command: (empty) | Output directory: / or /public
  4. Deploy

Static Site Structure

/
├── index.html
├── about.html
├── css/style.css
├── js/app.js
├── _headers          # Optional: caching/security headers
└── _redirects        # Optional: redirect rules

Adding API Routes (Pages Functions)

/
├── index.html
├── functions/
│   └── api/
│       └── hello.ts  # -> /api/hello

functions/api/hello.ts:

// Use PagesFunction<Env> when you have bindings (D1, KV, etc.)
export const onRequest: PagesFunction = async (context) => {
  return Response.json({ message: 'Hello!' })
}

CLI Commands

wrangler pages dev ./              # Local dev
wrangler pages deploy ./ --project-name=my-site  # Deploy

Clerk Patterns (VERIFY CURRENT API BEFORE USE)

Hono (@hono/clerk-auth):

import { Hono } from 'hono'
import { clerkMiddleware, getAuth } from '@hono/clerk-auth'

const app = new Hono<{ Bindings: Env }>()
app.use('*', clerkMiddleware())
app.get('/api/*', (c) => {
  const auth = getAuth(c)
  if (!auth?.userId) return c.json({ error: 'Unauthorized' }, 401)
  return c.json({ userId: auth.userId })
})
export default app

Raw Workers (@clerk/backend):

import { createClerkClient } from '@clerk/backend'

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // BOTH keys required!
    const clerkClient = createClerkClient({
      secretKey: env.CLERK_SECRET_KEY,
      publishableKey: env.CLERK_PUBLISHABLE_KEY,
    })
    
    const { isAuthenticated, toAuth } = await clerkClient.authenticateRequest(request, {
      authorizedParties: ['https://your-domain.com']
    })
    
    if (!isAuthenticated) {
      return new Response('Unauthorized', { status: 401 })
    }
    
    const auth = toAuth()
    return new Response(JSON.stringify({ userId: auth.userId }))
  }
}

Secrets

  • .dev.vars for local (gitignore!)
  • wrangler secret put KEY for prod
  • Required for Clerk: CLERK_SECRET_KEY AND CLERK_PUBLISHABLE_KEY

Commands Reference

Wrangler CLI Documentation

Full reference: https://developers.cloudflare.com/workers/wrangler/commands/

# ============================================================================
# PROJECT SETUP
# Docs: https://developers.cloudflare.com/workers/get-started/guide/
# ============================================================================
npm create cloudflare@latest              # Interactive project creation
npm create cloudflare@latest my-app       # Named project
npm create cloudflare@latest -- --template hono  # With template

# ============================================================================
# LOCAL DEVELOPMENT
# Workers: https://developers.cloudflare.com/workers/wrangler/commands/#dev
# Pages: https://developers.cloudflare.com/pages/functions/local-development/
# ============================================================================
wrangler dev                    # Workers dev server (localhost:8787)
wrangler dev --port 3000        # Custom port
wrangler dev --remote           # Use REAL cloud resources (CAUTION!)

wrangler pages dev ./           # Pages dev server (localhost:8788)
wrangler pages dev ./dist       # Pages with build output dir
wrangler pages dev ./ --d1=DB   # Pages with D1 binding

# ============================================================================
# DEPLOYMENT
# Docs: https://developers.cloudflare.com/workers/wrangler/commands/#deploy
# ============================================================================
wrangler deploy                 # Deploy Worker to production
wrangler deploy --env staging   # Deploy to environment

wrangler pages deploy ./dist --project-name=my-site  # Deploy Pages
wrangler pages project create my-site                 # Create Pages project first

# ============================================================================
# D1 DATABASE (SQLite)
# Docs: https://developers.cloudflare.com/d1/wrangler-commands/
# ============================================================================
wrangler d1 create <db-name>    # Create DB (returns database_id for config)
wrangler d1 list                # List all databases

# Execute SQL (--local for dev, --remote for production)
wrangler d1 execute <db> --local --file=./schema.sql   # Local
wrangler d1 execute <db> --remote --file=./schema.sql  # PRODUCTION (careful!)
wrangler d1 execute <db> --local --command="SELECT * FROM users"

# Migrations (recommended for schema changes)
# Docs: https://developers.cloudflare.com/d1/reference/migrations/
wrangler d1 migrations create <db> <name>   # Create migration file
wrangler d1 migrations apply <db> --local   # Apply locally
wrangler d1 migrations apply <db> --remote  # Apply to production

# ============================================================================
# KV STORAGE (Key-Value, eventually consistent)
# Docs: https://developers.cloudflare.com/kv/reference/kv-commands/
# ============================================================================
wrangler kv namespace create <name>         # Create namespace (returns id)
wrangler kv namespace create <name> --preview  # Create preview namespace
wrangler kv namespace list                  # List namespaces

wrangler kv key put --binding=KV "key" "value" --local  # Put (local)
wrangler kv key put --binding=KV "key" "value"          # Put (production)
wrangler kv key get --binding=KV "key"                  # Get value
wrangler kv key list --binding=KV                       # List keys

# ============================================================================
# R2 OBJECT STORAGE (S3-compatible)
# Docs: https://developers.cloudflare.com/r2/api/wrangler/
# ============================================================================
wrangler r2 bucket create <name>            # Create bucket
wrangler r2 bucket list                     # List buckets
wrangler r2 object put <bucket>/<key> --file=./file.txt  # Upload
wrangler r2 object get <bucket>/<key>       # Download

# ============================================================================
# SECRETS (Encrypted env vars for production)
# Docs: https://developers.cloudflare.com/workers/wrangler/commands/#secret
# ============================================================================
wrangler secret put <NAME>                  # Add secret (prompts for value)
echo "value" | wrangler secret put <NAME>   # Add from stdin (CI/CD)
wrangler secret list                        # List secret names
wrangler secret delete <NAME>               # Delete secret

# ============================================================================
# TYPESCRIPT & DEBUGGING
# ============================================================================
wrangler types                  # Generate Env types from config
                                # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#types

wrangler tail                   # Stream production logs
wrangler tail --status=error    # Filter by status
wrangler tail --format=json     # JSON output
                                # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#tail

# ============================================================================
# AUTH
# ============================================================================
wrangler login                  # Login to Cloudflare (opens browser)
wrangler whoami                 # Check current user
wrangler logout                 # Logout

Key Command Patterns for AI

# Pattern: Local vs Remote operations
# --local  = Uses local simulation (safe for development)
# --remote = Uses PRODUCTION resources (be careful!)

# Pattern: Bindings in Pages dev
# Pass bindings via CLI when no wrangler.toml:
wrangler pages dev ./ --d1=DB --kv=CACHE --r2=STORAGE

# Pattern: Environment-specific operations
wrangler deploy --env staging
wrangler secret put API_KEY --env staging
wrangler tail --env staging

# Pattern: Get resource IDs for wrangler config
wrangler d1 create my-db      # Output includes database_id
wrangler kv namespace create CACHE  # Output includes namespace id
# Add these IDs to wrangler.toml/wrangler.jsonc bindings

Key Constraints

Limit Value
D1 rows/query 1000 (use pagination)
KV consistency Eventually consistent
Workers CPU 30s paid / 10ms free
R2 object size 5TB max

Gotchas:

  • Clone request before reading body twice
  • Use nodejs_compat flag for Node APIs
  • Clerk @clerk/backend needs BOTH secretKey AND publishableKey
  • D1 transactions: await db.batch([stmt1, stmt2])
  • Set authorizedParties in Clerk to prevent CSRF

Notes