# CLAUDE.md - Cloudflare Platform Project Template ## Project Overview - **Project Name**: [PROJECT_NAME] - **Description**: [BRIEF_DESCRIPTION] - **Auth Provider**: Clerk --- ## CRITICAL: Documentation Verification Rules ### Before Writing ANY Code 1. **Determine project language/framework** by inspecting project files 2. **Lookup current documentation** for the detected stack before implementing 3. **Never assume** API signatures, binding syntax, or SDK methods ### MCP Tools (if available) - **context7 MCP**: Query for latest documentation - **Cloudflare MCP**: List/create resources, search docs, verify resource IDs ### Documentation URLs (for web fetch if no MCP) Fetch and read these URLs to verify current APIs: **Cloudflare Platform:** | Service | URL | |---------|-----| | Workers | https://developers.cloudflare.com/workers/ | | D1 (SQLite) | https://developers.cloudflare.com/d1/ | | D1 Get Started | https://developers.cloudflare.com/d1/get-started/ | | KV | https://developers.cloudflare.com/kv/ | | R2 (Objects) | https://developers.cloudflare.com/r2/ | | Durable Objects | https://developers.cloudflare.com/durable-objects/ | | Queues | https://developers.cloudflare.com/queues/ | | Workers AI | https://developers.cloudflare.com/workers-ai/ | | Hyperdrive | https://developers.cloudflare.com/hyperdrive/ | | Workflows | https://developers.cloudflare.com/workflows/ | | 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/ | | Pages Functions Config | https://developers.cloudflare.com/pages/functions/wrangler-configuration/ | | Wrangler Config | https://developers.cloudflare.com/workers/wrangler/configuration/ | **Clerk Authentication:** | Resource | URL | |----------|-----| | Backend SDK Overview | https://clerk.com/docs/reference/backend/overview | | JS Backend SDK Quickstart | https://clerk.com/docs/js-backend/getting-started/quickstart | | authenticateRequest() Reference | https://clerk.com/docs/reference/backend/authenticate-request | | Express SDK | https://clerk.com/docs/reference/express/overview | | Next.js SDK | https://clerk.com/docs/reference/nextjs/overview | | Hono Middleware (Community) | https://github.com/honojs/middleware/tree/main/packages/clerk-auth | **Framework Deployment Guides:** | Framework | URL | |-----------|-----| | Hono on Workers | https://hono.dev/docs/getting-started/cloudflare-workers | | Next.js on Pages | https://developers.cloudflare.com/pages/framework-guides/nextjs/ | | Remix on Pages | https://developers.cloudflare.com/pages/framework-guides/deploy-a-remix-site/ | | Astro on Pages | https://developers.cloudflare.com/pages/framework-guides/deploy-an-astro-site/ | | SvelteKit on Pages | https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-kit-site/ | **ORM/Database:** | Tool | URL | |------|-----| | Drizzle + D1 | https://orm.drizzle.team/docs/get-started/d1-new | --- ## Project Detection & Setup ### Step 1: Detect Existing Project Type Inspect project files to determine language/framework: ``` File Found -> Stack -> Action ----------------------------------------------------------------- package.json -> Node.js/TypeScript -> Check dependencies for framework - "hono" -> Hono -> Fetch Hono + CF Workers docs - "next" -> Next.js -> Fetch Next.js + CF Pages docs - "@remix-run/*" -> Remix -> Fetch Remix + CF Pages docs - "astro" -> Astro -> Fetch Astro + CF Pages docs - No framework -> Raw Worker -> Fetch Workers docs only requirements.txt -> Python Workers -> Add compatibility_flags: ["python_workers"] pyproject.toml -> Python Workers -> Add compatibility_flags: ["python_workers"] go.mod -> Go (WASM) -> Fetch Go Workers docs Cargo.toml -> Rust (WASM) -> Fetch Rust Workers docs wrangler.toml/.jsonc -> Existing config -> Follow existing patterns index.html (no package.json) -> Static HTML -> Use Pages Git Integration (no build) *.html files only -> Static HTML -> Use Pages Git Integration (no build) ``` ### Step 2: New Project Setup (if no manifest files) ```bash # Create new Workers project npm create cloudflare@latest -- my-app # Or with specific template npm create cloudflare@latest -- my-app --template hono ``` ### Step 3: Install Dependencies (after detection) ```bash # Core (always needed for local dev) npm install wrangler --save-dev # Hono (if using) npm install hono # Clerk (choose based on framework) npm install @clerk/backend # Raw Workers (also needs publishableKey!) npm install @hono/clerk-auth @clerk/backend # Hono (both packages required) npm install @clerk/nextjs # Next.js npm install @clerk/express # Express # Drizzle ORM (if using D1) npm install drizzle-orm npm install drizzle-kit --save-dev ``` --- ## Wrangler Configuration ### Config File (wrangler.jsonc preferred) ```jsonc { "$schema": "./node_modules/wrangler/config-schema.json", "name": "project-name", "main": "src/index.ts", "compatibility_date": "2024-12-01", "compatibility_flags": ["nodejs_compat"] } ``` ### Binding Configurations **D1 Database:** ```jsonc "d1_databases": [ { "binding": "DB", "database_name": "my-db", "database_id": "UUID_HERE" } ] ``` **KV Namespace:** ```jsonc "kv_namespaces": [ { "binding": "KV", "id": "NAMESPACE_ID", "preview_id": "PREVIEW_ID" } ] ``` **R2 Bucket:** ```jsonc "r2_buckets": [ { "binding": "BUCKET", "bucket_name": "my-bucket" } ] ``` **Durable Objects:** ```jsonc "durable_objects": { "bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }] }, "migrations": [{ "tag": "v1", "new_classes": ["MyDurableObject"] }] ``` **Queues:** ```jsonc "queues": { "producers": [{ "binding": "QUEUE", "queue": "my-queue" }], "consumers": [{ "queue": "my-queue", "max_batch_size": 10 }] } ``` **Workers AI:** ```jsonc "ai": { "binding": "AI" } ``` **Hyperdrive (external Postgres):** ```jsonc "hyperdrive": [ { "binding": "HYPERDRIVE", "id": "HYPERDRIVE_ID" } ] ``` **Static Assets:** ```jsonc "assets": { "directory": "./public" } ``` --- ## Cloudflare Pages: Static HTML via GitHub For pure static HTML sites deployed via GitHub integration (no build step required). ### Documentation | Resource | URL | |----------|-----| | Pages Get Started | https://developers.cloudflare.com/pages/get-started/git-integration/ | | Pages Functions | https://developers.cloudflare.com/pages/functions/ | | Pages Direct Upload | https://developers.cloudflare.com/pages/get-started/direct-upload/ | ### Setup via Cloudflare Dashboard (Recommended) 1. Go to **Cloudflare Dashboard > Workers & Pages > Create > Pages > Connect to Git** 2. Select your GitHub repository 3. Configure build settings: - **Build command**: *(leave empty for static HTML)* - **Build output directory**: `/` or your HTML folder (e.g., `/public`) 4. Deploy ### Repository Structure (Static HTML) ``` / ├── index.html # Homepage ├── about.html # Other pages ├── css/ │ └── style.css ├── js/ │ └── app.js ├── images/ │ └── logo.png └── _headers # Optional: custom headers ``` ### Optional: `_headers` File (for caching, security) ``` /* X-Frame-Options: DENY X-Content-Type-Options: nosniff /css/* Cache-Control: public, max-age=31536000 /images/* Cache-Control: public, max-age=31536000 ``` ### Optional: `_redirects` File ``` /old-page /new-page 301 /blog/* /articles/:splat 302 ``` ### Adding API Routes (Pages Functions) To add server-side API routes to a static site, create a `functions/` directory: ``` / ├── index.html ├── functions/ │ ├── api/ │ │ └── hello.ts # Accessible at /api/hello │ └── _middleware.ts # Optional: runs on all routes └── ... ``` **Example `functions/api/hello.ts`:** ```typescript // VERIFY at: https://developers.cloudflare.com/pages/functions/ // PagesFunction type is globally available in Pages Functions export const onRequest: PagesFunction = async (context) => { return Response.json({ message: 'Hello from Pages Function!' }) } ``` **Example `functions/api/hello.ts` with D1:** ```typescript // Define Env interface for typed bindings interface Env { DB: D1Database } export const onRequest: PagesFunction = async (context) => { const result = await context.env.DB.prepare('SELECT * FROM users LIMIT 10').all() return Response.json(result) } ``` ### Bindings for Pages Functions Create `wrangler.toml` in project root for Pages Function bindings: ```toml name = "my-static-site" pages_build_output_dir = "./" compatibility_date = "2024-12-01" [[d1_databases]] binding = "DB" database_name = "my-db" database_id = "UUID_HERE" [[kv_namespaces]] binding = "KV" id = "NAMESPACE_ID" ``` ### CLI Deployment (Alternative to GitHub) ```bash # Install wrangler npm install wrangler --save-dev # Deploy directly (no GitHub needed) wrangler pages deploy ./public --project-name=my-site # Or create project first wrangler pages project create my-site wrangler pages deploy ./ --project-name=my-site ``` ### Local Development ```bash # Serve static files + Pages Functions locally wrangler pages dev ./ # With specific port wrangler pages dev ./ --port 3000 # With bindings (D1, KV, etc.) wrangler pages dev ./ --d1=DB ``` ### Custom Domain 1. Dashboard: **Pages project > Custom domains > Set up a custom domain** 2. Add CNAME record pointing to `.pages.dev` 3. SSL provisioned automatically --- ## TypeScript Environment Types ### Generate Types ```bash wrangler types ``` Creates `worker-configuration.d.ts`. Define your Env interface: ```typescript // src/types.ts interface Env { DB: D1Database KV: KVNamespace BUCKET: R2Bucket CLERK_SECRET_KEY: string CLERK_PUBLISHABLE_KEY: string // Add other bindings as needed } ``` --- ## Clerk Authentication ### Required Secrets Local (`.dev.vars` - NEVER commit): ``` CLERK_SECRET_KEY=sk_test_xxx CLERK_PUBLISHABLE_KEY=pk_test_xxx ``` Production: ```bash wrangler secret put CLERK_SECRET_KEY wrangler secret put CLERK_PUBLISHABLE_KEY ``` ### Pattern: Hono + @hono/clerk-auth (Community SDK) ```typescript // VERIFY at: https://github.com/honojs/middleware/tree/main/packages/clerk-auth import { Hono } from 'hono' import { clerkMiddleware, getAuth } from '@hono/clerk-auth' const app = new Hono<{ Bindings: Env }>() app.use('*', clerkMiddleware()) app.get('/protected', (c) => { const auth = getAuth(c) if (!auth?.userId) { return c.json({ error: 'Unauthorized' }, 401) } return c.json({ userId: auth.userId }) }) export default app ``` ### Pattern: Raw Workers + @clerk/backend ```typescript // VERIFY at: https://clerk.com/docs/reference/backend/authenticate-request // IMPORTANT: Both secretKey AND publishableKey are REQUIRED import { createClerkClient } from '@clerk/backend' export default { async fetch(request: Request, env: Env): Promise { const clerkClient = createClerkClient({ secretKey: env.CLERK_SECRET_KEY, publishableKey: env.CLERK_PUBLISHABLE_KEY, // REQUIRED! }) // authenticateRequest returns { isAuthenticated, toAuth(), ... } const { isAuthenticated, toAuth } = await clerkClient.authenticateRequest(request, { authorizedParties: ['https://your-domain.com'] // Recommended for security }) if (!isAuthenticated) { return new Response('Unauthorized', { status: 401 }) } // Get full Auth object if needed const auth = toAuth() // auth.userId, auth.sessionId, auth.orgId available return new Response(JSON.stringify({ userId: auth.userId }), { headers: { 'Content-Type': 'application/json' } }) } } ``` --- ## Development Commands ### Wrangler CLI Reference Documentation: https://developers.cloudflare.com/workers/wrangler/commands/ ```bash # ============================================================================ # LOCAL DEVELOPMENT # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#dev # ============================================================================ # Start local development server for Workers # - Runs on http://localhost:8787 by default # - Hot reloads on file changes # - Uses .dev.vars for local secrets # - Simulates Cloudflare runtime locally (miniflare) wrangler dev # Start with specific port wrangler dev --port 3000 # Start with remote resources (connects to real D1/KV/R2 in Cloudflare) # WARNING: This uses PRODUCTION data! Use with caution. wrangler dev --remote # Start for Pages project (different from Workers) # Docs: https://developers.cloudflare.com/pages/functions/local-development/ # - Runs on http://localhost:8788 by default # - Serves static files + Pages Functions wrangler pages dev ./ # Pages dev with specific output directory wrangler pages dev ./dist # Pages dev with bindings passed via CLI (alternative to wrangler.toml) wrangler pages dev ./ --d1=DB --kv=KV --r2=BUCKET # ============================================================================ # DEPLOYMENT # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#deploy # ============================================================================ # Deploy Worker to Cloudflare (production) # - Reads wrangler.toml/wrangler.jsonc for configuration # - Uploads code and applies bindings # - Returns deployment URL wrangler deploy # Deploy specific script (overrides config) wrangler deploy src/index.ts # Deploy to specific environment (if using wrangler environments) # Docs: https://developers.cloudflare.com/workers/wrangler/environments/ wrangler deploy --env staging # Deploy Pages project directly (without Git integration) # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#deploy-1 wrangler pages deploy ./dist --project-name=my-site # Create Pages project first (required for first deploy) wrangler pages project create my-site # ============================================================================ # D1 DATABASE OPERATIONS # Docs: https://developers.cloudflare.com/d1/wrangler-commands/ # D1 is Cloudflare's serverless SQLite database # ============================================================================ # Create a new D1 database # Returns database_id to add to wrangler config wrangler d1 create # List all D1 databases in account wrangler d1 list # Get info about specific database wrangler d1 info # Execute SQL file against LOCAL database (for development) # --local flag uses local SQLite, not production wrangler d1 execute --local --file=./schema.sql # Execute SQL file against REMOTE/PRODUCTION database # WARNING: This modifies production data! wrangler d1 execute --remote --file=./schema.sql # Execute inline SQL locally wrangler d1 execute --local --command="SELECT * FROM users" # Execute inline SQL remotely wrangler d1 execute --remote --command="SELECT * FROM users" # D1 Migrations (recommended for schema changes) # Docs: https://developers.cloudflare.com/d1/reference/migrations/ # Create a new migration file (creates migrations/ directory) wrangler d1 migrations create # Creates: migrations/0001_.sql # Apply migrations to LOCAL database wrangler d1 migrations apply --local # Apply migrations to REMOTE/PRODUCTION database wrangler d1 migrations apply --remote # List migration status wrangler d1 migrations list # ============================================================================ # KV NAMESPACE OPERATIONS # Docs: https://developers.cloudflare.com/kv/reference/kv-commands/ # KV is Cloudflare's key-value storage (eventually consistent) # ============================================================================ # Create a new KV namespace # Returns namespace ID to add to wrangler config wrangler kv namespace create # Create preview namespace (for wrangler dev) wrangler kv namespace create --preview # List all KV namespaces wrangler kv namespace list # Delete a KV namespace wrangler kv namespace delete --namespace-id= # Put a key-value pair (local development) wrangler kv key put --binding=KV "my-key" "my-value" --local # Put a key-value pair (production) wrangler kv key put --binding=KV "my-key" "my-value" # Get a value by key wrangler kv key get --binding=KV "my-key" # Delete a key wrangler kv key delete --binding=KV "my-key" # List keys in namespace wrangler kv key list --binding=KV # Bulk upload from JSON file # File format: [{"key": "k1", "value": "v1"}, ...] wrangler kv bulk put --binding=KV ./data.json # ============================================================================ # R2 OBJECT STORAGE OPERATIONS # Docs: https://developers.cloudflare.com/r2/api/wrangler/ # R2 is Cloudflare's S3-compatible object storage (zero egress fees) # ============================================================================ # Create a new R2 bucket wrangler r2 bucket create # List all R2 buckets wrangler r2 bucket list # Delete an R2 bucket (must be empty) wrangler r2 bucket delete # Upload object to bucket wrangler r2 object put / --file=./myfile.txt # Download object from bucket wrangler r2 object get / # Delete object from bucket wrangler r2 object delete / # ============================================================================ # SECRETS MANAGEMENT # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#secret # Secrets are encrypted environment variables for production # ============================================================================ # Add/update a secret (interactive prompt for value) # Secret is encrypted and stored securely wrangler secret put # Add secret with value from stdin (useful for CI/CD) echo "secret-value" | wrangler secret put # List all secrets (shows names only, not values) wrangler secret list # Delete a secret wrangler secret delete # Add secret to specific environment wrangler secret put --env staging # ============================================================================ # TYPESCRIPT TYPES GENERATION # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#types # Generates TypeScript types for your bindings # ============================================================================ # Generate types from wrangler config # Creates worker-configuration.d.ts with Env interface wrangler types # Output to specific file wrangler types --env-interface CloudflareBindings # ============================================================================ # LOGS AND DEBUGGING # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#tail # ============================================================================ # Stream real-time logs from deployed Worker # Shows console.log output, errors, and request info wrangler tail # Filter logs by status wrangler tail --status=error # Filter by HTTP method wrangler tail --method=POST # Filter by search string wrangler tail --search="error" # Filter by IP address wrangler tail --ip=1.2.3.4 # Tail specific environment wrangler tail --env staging # Output as JSON (useful for piping to other tools) wrangler tail --format=json # ============================================================================ # QUEUES OPERATIONS # Docs: https://developers.cloudflare.com/queues/configuration/wrangler-commands/ # ============================================================================ # Create a new queue wrangler queues create # List all queues wrangler queues list # Delete a queue wrangler queues delete # ============================================================================ # DURABLE OBJECTS # Docs: https://developers.cloudflare.com/durable-objects/ # Note: Durable Objects are defined in code and deployed with wrangler deploy # Migrations are specified in wrangler config # ============================================================================ # No specific CLI commands - DO is configured in wrangler.toml: # "durable_objects": { "bindings": [{ "name": "MY_DO", "class_name": "MyClass" }] } # "migrations": [{ "tag": "v1", "new_classes": ["MyClass"] }] # ============================================================================ # HYPERDRIVE (External Postgres Connection Pooling) # Docs: https://developers.cloudflare.com/hyperdrive/ # ============================================================================ # Create Hyperdrive config (connects to external Postgres) wrangler hyperdrive create --connection-string="postgres://user:pass@host:5432/db" # List Hyperdrive configs wrangler hyperdrive list # Get Hyperdrive config details wrangler hyperdrive get # Delete Hyperdrive config wrangler hyperdrive delete # ============================================================================ # WORKERS AI # Docs: https://developers.cloudflare.com/workers-ai/ # Note: AI binding is configured in wrangler.toml: "ai": { "binding": "AI" } # No specific CLI commands - models are accessed via env.AI.run() # ============================================================================ # ============================================================================ # PROJECT INITIALIZATION # Docs: https://developers.cloudflare.com/workers/get-started/guide/ # ============================================================================ # Create new project interactively npm create cloudflare@latest # Create with specific name npm create cloudflare@latest my-app # Create with specific template npm create cloudflare@latest my-app -- --template hono npm create cloudflare@latest my-app -- --template worker-typescript # ============================================================================ # AUTHENTICATION # Docs: https://developers.cloudflare.com/workers/wrangler/commands/#login # ============================================================================ # Login to Cloudflare (opens browser) wrangler login # Check login status wrangler whoami # Logout wrangler logout ``` --- ## Platform Constraints | Service | Constraint | Notes | |---------|------------|-------| | Workers | 30s CPU (paid), 10ms (free) | Use streaming for long operations | | Workers | 128MB memory | | | D1 | 1000 rows per query | Use LIMIT/OFFSET pagination | | D1 | 100KB per row | | | D1 | Transactions via `db.batch()` | | | KV | Eventually consistent | Not for real-time coordination | | KV | 25MB per value | | | R2 | 5TB per object | S3-compatible API | | Durable Objects | Single-threaded per instance | Use for coordination, WebSockets | ### Common Gotchas 1. **Clone Request** before reading body twice: `const clone = request.clone()` 2. **nodejs_compat flag** required for most Node.js APIs 3. **Not all Node.js APIs** available even with nodejs_compat (e.g., `async_hooks`) 4. **Clerk @clerk/backend** requires BOTH `secretKey` AND `publishableKey` 5. **D1 batch** for transactions: `await db.batch([stmt1, stmt2])` 6. **authorizedParties** recommended for Clerk to prevent CSRF attacks --- ## Pre-Implementation Checklist Before writing code: - [ ] Detected project language/framework from manifest files - [ ] Fetched and read relevant documentation URLs - [ ] Verified current API signatures (especially Clerk SDK) - [ ] Created/verified wrangler config with correct binding names - [ ] Generated TypeScript types with `wrangler types` - [ ] Set up `.dev.vars` for local secrets (both CLERK keys!) - [ ] Added `.dev.vars` to `.gitignore` --- ## Project Structure (Reference) ``` / ├── src/ │ ├── index.ts # Worker entry point │ ├── routes/ # Route handlers │ ├── middleware/ # Auth, logging │ ├── services/ # Business logic │ ├── db/ │ │ ├── schema.ts # Drizzle schema │ │ └── migrations/ # D1 migrations │ └── types.ts # Env interface, shared types ├── wrangler.jsonc # Wrangler configuration ├── drizzle.config.ts # Drizzle config (if using) ├── .dev.vars # Local secrets (gitignored!) ├── .gitignore # Must include .dev.vars ├── package.json ├── tsconfig.json └── CLAUDE.md # This file ``` --- ## Notes