cloudflare-full-stack-integration
ClaudeSkillz: For when you need skills, but lazier
npx skills add https://github.com/jackspace/claudeskillz --skill cloudflare-full-stack-integrationSKILL.md
Cloudflare Full-Stack Integration Patterns
Production-tested patterns for React + Cloudflare Workers + Hono + Clerk authentication.
When to Use This Skill
Use this skill when you need to:
- Connect a React frontend to a Cloudflare Worker backend
- Implement authentication with Clerk in a full-stack app
- Set up API calls that automatically include auth tokens
- Fix CORS errors between frontend and backend
- Prevent race conditions with auth loading
- Configure environment variables correctly
- Set up D1 database access from API routes
- Create protected routes that require authentication
What This Skill Provides
Templates
Frontend (templates/frontend/):
lib/api-client.ts- Fetch wrapper with automatic token attachmentcomponents/ProtectedRoute.tsx- Auth gate pattern with loading states
Backend (templates/backend/):
middleware/cors.ts- CORS configuration for dev and productionmiddleware/auth.ts- JWT verification with Clerkroutes/api.ts- Example API routes with all patterns integrated
Config (templates/config/):
wrangler.jsonc- Complete Workers configuration with bindings.dev.vars.example- Environment variables setupvite.config.ts- Cloudflare Vite plugin configuration
References (references/):
common-race-conditions.md- Complete guide to auth loading issues
Critical Architectural Insights
1. @cloudflare/vite-plugin Runs on SAME Port
Key Insight: The Worker and frontend run on the SAME port during development.
// ✅ CORRECT: Use relative URLs
fetch('/api/data')
// ❌ WRONG: Don't use absolute URLs or proxy
fetch('http://localhost:8787/api/data')
Why: The Vite plugin runs your Worker using workerd directly in the dev server. No proxy needed!
2. CORS Must Be Applied BEFORE Routes
// ✅ CORRECT ORDER
app.use('/api/*', cors())
app.post('/api/data', handler)
// ❌ WRONG ORDER - Will cause CORS errors
app.post('/api/data', handler)
app.use('/api/*', cors())
3. Auth Loading is NOT a Race Condition
Most "race conditions" are actually missing isLoaded checks:
// ❌ WRONG: Calls API before token ready
useEffect(() => {
fetch('/api/data') // 401 error!
}, [])
// ✅ CORRECT: Wait for auth to load
const { isLoaded, isSignedIn } = useSession()
useEffect(() => {
if (!isLoaded || !isSignedIn) return
fetch('/api/data') // Now token is ready
}, [isLoaded, isSignedIn])
4. Environment Variables Have Different Rules
Frontend (Vite):
- MUST start with
VITE_prefix - Defined in
.envfile - Access:
import.meta.env.VITE_VARIABLE_NAME
Backend (Workers):
- NO prefix required
- Defined in
.dev.varsfile (dev) or wrangler secrets (prod) - Access:
env.VARIABLE_NAME
5. D1 Bindings Are Always Available
D1 is accessed via bindings - no connection management needed:
// ✅ CORRECT: Direct access via binding
const { results } = await env.DB.prepare('SELECT * FROM users').run()
// ❌ WRONG: No need to "connect" first
const connection = await env.DB.connect() // This doesn't exist!
Step-by-Step Integration Guide
Step 1: Project Setup
# Create project with Cloudflare Workers + React
npm create cloudflare@latest my-app
cd my-app
# Install dependencies
npm install hono @clerk/clerk-react @clerk/backend
npm install -D @cloudflare/vite-plugin @tailwindcss/vite
Step 2: Configure Vite
Copy templates/config/vite.config.ts to your project root.
Key points:
- Includes
cloudflare()plugin - No proxy configuration needed
- Sets up path aliases for clean imports
Step 3: Configure Wrangler
Copy templates/config/wrangler.jsonc to your project root.
Update:
- Replace
namewith your app name - Add D1/KV/R2 bindings as needed
- Set
run_worker_first: ["/api/*"]for API routes
Step 4: Set Up Environment Variables
Create .dev.vars (gitignored):
CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
CLERK_SECRET_KEY=sk_test_xxxxx
Create .env for frontend:
VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
Step 5: Add CORS Middleware
Copy templates/backend/middleware/cors.ts to your backend.
Apply in your main worker file:
import { corsMiddleware } from './middleware/cors'
app.use('/api/*', (c, next) => corsMiddleware(c.env)(c, next))
CRITICAL: Apply this BEFORE defining routes!
Step 6: Add Auth Middleware
Copy templates/backend/middleware/auth.ts to your backend.
Apply to protected routes:
import { jwtAuthMiddleware } from './middleware/auth'
app.use('/api/protected/*', jwtAuthMiddleware(c.env.CLERK_SECRET_KEY))
Step 7: Set Up API Client
Copy templates/frontend/lib/api-client.ts to your frontend.
Use in your App component:
import { useApiClient } from '@/lib/api-client'
function App() {
useApiClient() // Set up token access
return <YourApp />
}
Step 8: Create Protected Routes
Copy `templat
...