Cloudflare Workers: A Complete Beginner's Guide
This guide takes you from zero to deploying your first Cloudflare Worker. We’ll start with manual setup so you understand what’s happening, then show you how AI coding assistants can speed up your workflow.
Quick Navigation:
- What Are Cloudflare Workers?
- Video Tutorials
- Manual Setup (Learn the Basics)
- Your First Worker
- Adding Databases & Storage
- AI-Assisted Development
- Git Integration & CI/CD
- Common Mistakes
Video Tutorials
Section titled “Video Tutorials”Prefer learning by watching? These official Cloudflare tutorials will get you started:
Watch this 30-minute video or follow the written guide below - your choice!
Ready to code? Skip to Manual Setup to start building.
What Are Cloudflare Workers?
Section titled “What Are Cloudflare Workers?”Imagine you built a website and someone in Tokyo visits it. If your server is in New York, their request has to travel across the Pacific Ocean, get processed, and travel back. That’s slow.
Cloudflare Workers run your code in 300+ cities worldwide. When someone visits your site, the nearest server handles their request. Tokyo user? Tokyo server. London user? London server.
Traditional Server:User (Tokyo) → → → → → Server (New York) → → → → → User (Tokyo) [500ms round trip]
Cloudflare Worker:User (Tokyo) → Server (Tokyo) → User (Tokyo) [50ms round trip]Why Should You Care?
Section titled “Why Should You Care?”| Benefit | What It Means |
|---|---|
| Free tier | 100,000 requests/day free - enough for learning and small projects |
| No server management | No Linux commands, no updates, no security patches |
| Instant deployment | Code goes live in seconds, not minutes |
| Scales automatically | 1 user or 1 million users - same code, same price |
| Great for portfolios | Impress recruiters with deployed projects, not localhost demos |
What Can You Build?
Section titled “What Can You Build?”- APIs - Backend for your mobile app or website
- Redirects - Short URLs like bit.ly
- Authentication - Login systems
- Chatbots - Discord/Slack bots
- Scrapers - Fetch data from other websites
- Full websites - With Cloudflare Pages
Prerequisites
Section titled “Prerequisites”Before we start, you need:
1. A Cloudflare Account (Free)
Section titled “1. A Cloudflare Account (Free)”- Go to dash.cloudflare.com
- Click “Sign Up”
- Use your email (school email works fine)
- Verify your email
2. Node.js Installed
Section titled “2. Node.js Installed”Node.js lets you run JavaScript on your computer (not just in browsers).
Check if you have it:
node --versionIf you see a version number (like v20.10.0), you’re good. If not:
- Windows: Download from nodejs.org (LTS version)
- Mac:
brew install node(if you have Homebrew) or download from nodejs.org - Linux:
sudo apt install nodejs npm(Ubuntu/Debian)
3. A Code Editor
Section titled “3. A Code Editor”Any text editor works, but we recommend:
- VS Code - Free, popular, great extensions
- Cursor - VS Code fork with built-in AI
- Claude Code - Terminal-based AI coding assistant (works alongside any editor)
4. Basic Terminal Knowledge
Section titled “4. Basic Terminal Knowledge”You’ll type commands in:
- Windows: PowerShell or Command Prompt
- Mac: Terminal
- Linux: Terminal
Don’t worry - we’ll show you exactly what to type.
Manual Setup (The Learning Path)
Section titled “Manual Setup (The Learning Path)”Let’s set everything up step by step. This might seem like extra work, but understanding these steps makes debugging easier later.
Step 1: Install Wrangler
Section titled “Step 1: Install Wrangler”Wrangler is Cloudflare’s command-line tool. It’s how you create, test, and deploy Workers.
npm install -g wranglerVerify it worked:
wrangler --versionYou should see something like 3.x.x.
Step 2: Log In to Cloudflare
Section titled “Step 2: Log In to Cloudflare”wrangler loginThis opens your browser. Click “Allow” to give Wrangler permission to deploy code to your account.
Step 3: Create Your Project Folder
Section titled “Step 3: Create Your Project Folder”mkdir my-first-workercd my-first-workerStep 4: Initialize the Project
Section titled “Step 4: Initialize the Project”npm init -yThis creates a package.json file that tracks your project’s dependencies.
Step 5: Create the Wrangler Config
Section titled “Step 5: Create the Wrangler Config”Create a file called wrangler.toml (this tells Wrangler about your project):
name = "my-first-worker"main = "src/index.js"compatibility_date = "2024-01-01"
[observability]enabled = trueStep 6: Create Your Code Folder
Section titled “Step 6: Create Your Code Folder”mkdir srcNow your project structure looks like:
my-first-worker/├── package.json├── wrangler.toml└── src/ └── (your code goes here)Your First Worker
Section titled “Your First Worker”The Code
Section titled “The Code”Create src/index.js:
export default { async fetch(request, env, ctx) { return new Response("Hello from my first Cloudflare Worker! 🎉"); },};Let’s break this down:
export default { // This makes your code available to Cloudflare async fetch(request, env, ctx) { // "fetch" runs every time someone visits your Worker // "request" = info about the visitor (URL, headers, etc.) // "env" = your environment variables and databases // "ctx" = advanced stuff (ignore for now) return new Response("Hello from my first Cloudflare Worker! 🎉"); // Send text back to the visitor },};Test Locally
Section titled “Test Locally”Before deploying, test on your computer:
wrangler devYou’ll see:
⎔ Starting local server...Ready on http://localhost:8787Open http://localhost:8787 in your browser. You should see your message!
Press Ctrl+C to stop the local server.
Deploy to the World
Section titled “Deploy to the World”wrangler deployYou’ll see something like:
Uploaded my-first-worker (1.23 sec)Published my-first-worker (0.45 sec) https://my-first-worker.YOUR-SUBDOMAIN.workers.devThat URL is live! Share it with friends. It works from anywhere in the world.
Making It Interactive
Section titled “Making It Interactive”Let’s make a more useful Worker that responds differently based on the URL.
A Simple API
Section titled “A Simple API”Replace your src/index.js:
export default { async fetch(request, env, ctx) { // Get the URL path (e.g., "/hello" from "https://example.com/hello") const url = new URL(request.url); const path = url.pathname;
// Route to different responses if (path === "/") { return new Response("Welcome to my API! Try /hello or /time"); }
if (path === "/hello") { // Get the name from ?name=YourName const name = url.searchParams.get("name") || "stranger"; return new Response(`Hello, ${name}! 👋`); }
if (path === "/time") { const now = new Date().toISOString(); return Response.json({ currentTime: now }); }
// 404 for unknown paths return new Response("Not found", { status: 404 }); },};Test it:
wrangler devThen visit:
http://localhost:8787/- Welcome messagehttp://localhost:8787/hello- “Hello, stranger!”http://localhost:8787/hello?name=Alex- “Hello, Alex!”http://localhost:8787/time- Current time as JSON
Understanding the Code
Section titled “Understanding the Code”const url = new URL(request.url);// Parses "https://example.com/hello?name=Alex" into parts
const path = url.pathname;// Gets "/hello"
const name = url.searchParams.get("name");// Gets "Alex" from "?name=Alex"
return Response.json({ currentTime: now });// Returns JSON instead of plain text// Automatically sets Content-Type: application/jsonLevel Up: Adding Databases & Storage
Section titled “Level Up: Adding Databases & Storage”Cloudflare offers several storage options. Here’s when to use each:
| Service | Use Case | Example |
|---|---|---|
| KV | Simple key-value data | User sessions, feature flags |
| D1 | Relational data (SQL) | User accounts, blog posts |
| R2 | Files and images | Profile pictures, uploads |
Adding KV (Key-Value Storage)
Section titled “Adding KV (Key-Value Storage)”KV is like a giant dictionary: you store values by keys.
1. Create a KV namespace:
wrangler kv namespace create "MY_KV"You’ll see:
🌀 Creating namespace with title "my-first-worker-MY_KV"✨ Success! Add the following to your wrangler.toml:[[kv_namespaces]]binding = "MY_KV"id = "abc123..."2. Add it to wrangler.toml:
name = "my-first-worker"main = "src/index.js"compatibility_date = "2024-01-01"
[[kv_namespaces]]binding = "MY_KV"id = "abc123..." # Use YOUR actual ID from the command output3. Use it in your code:
export default { async fetch(request, env, ctx) { const url = new URL(request.url);
if (url.pathname === "/visit") { // Get current count (or 0 if not set) const count = parseInt(await env.MY_KV.get("visits") || "0");
// Increment and save const newCount = count + 1; await env.MY_KV.put("visits", newCount.toString());
return new Response(`You are visitor #${newCount}!`); }
return new Response("Visit /visit to be counted!"); },};4. Test locally:
wrangler devVisit /visit multiple times - the count increases!
Adding D1 (SQL Database)
Section titled “Adding D1 (SQL Database)”D1 is SQLite at the edge - perfect for structured data.
1. Create a database:
wrangler d1 create my-database2. Add to wrangler.toml:
[[d1_databases]]binding = "DB"database_name = "my-database"database_id = "your-database-id" # From command output3. Create a table (migration file):
Create migrations/0001_create_users.sql:
CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);4. Run the migration:
# Locally (for testing)wrangler d1 execute my-database --local --file=migrations/0001_create_users.sql
# Productionwrangler d1 execute my-database --remote --file=migrations/0001_create_users.sql5. Use in your code:
export default { async fetch(request, env, ctx) { const url = new URL(request.url);
if (url.pathname === "/users" && request.method === "GET") { // Get all users const { results } = await env.DB.prepare( "SELECT * FROM users ORDER BY created_at DESC" ).all();
return Response.json(results); }
if (url.pathname === "/users" && request.method === "POST") { // Add a user const body = await request.json();
await env.DB.prepare( "INSERT INTO users (name, email) VALUES (?, ?)" ).bind(body.name, body.email).run();
return new Response("User created!", { status: 201 }); }
return new Response("Try GET or POST /users"); },};Test with curl:
# Add a usercurl -X POST http://localhost:8787/users \ -H "Content-Type: application/json" \ -d '{"name": "Alex", "email": "alex@example.com"}'
# List userscurl http://localhost:8787/usersAI-Assisted Development
Section titled “AI-Assisted Development”Once you understand the basics, AI coding assistants can dramatically speed up your workflow.
Using Claude Code
Section titled “Using Claude Code”Claude Code is Anthropic’s CLI for AI-assisted development.
Setup:
# Install (requires Claude Pro/Max subscription)npm install -g @anthropic-ai/claude-code
# Start in your projectcd my-first-workerclaudeExample prompts for Workers:
Create a Cloudflare Worker that:- Has a /shorten endpoint to create short URLs- Stores them in KV- Redirects /go/:code to the original URL- Returns JSON errors with proper status codesAdd a D1 database to store click analytics:- Track timestamp, IP country, and user agent- Add a /stats/:code endpoint to view analyticsSet up wrangler.toml for this project with:- KV namespace for URLs- D1 database for analytics- Environment variables for admin authUsing Cursor
Section titled “Using Cursor”Cursor is VS Code with built-in AI.
Workflow:
- Open your project in Cursor
- Press
Cmd+K(Mac) orCtrl+K(Windows) to open the AI prompt - Describe what you want
Example:
Create a REST API for a todo list with:- GET /todos - list all- POST /todos - create one- DELETE /todos/:id - delete oneUse D1 for storage. Include proper error handling.Cursor generates the code inline. Review it, then accept or modify.
Using GitHub Copilot
Section titled “Using GitHub Copilot”If you use VS Code with GitHub Copilot:
- Start typing a comment describing what you want
- Copilot suggests code
- Press
Tabto accept
// Create a function that validates email addresses// and returns true/falseCopilot will suggest the implementation.
Effective Prompts for Workers
Section titled “Effective Prompts for Workers”Be specific about Cloudflare APIs:
# Good"Use Cloudflare KV to cache API responses for 1 hour"
# Vague"Add caching"Mention the environment:
# Good"This runs on Cloudflare Workers - use the fetch handler patternand access KV through env.MY_KV"
# Missing context"Store data in a key-value store"Ask for error handling:
"Add try-catch blocks that return proper JSON errorswith status codes (400 for bad input, 500 for server errors)"Git Integration & Automatic Deployments
Section titled “Git Integration & Automatic Deployments”Manual wrangler deploy works, but real projects use Git for automatic deployments.
Option 1: GitHub Actions (Recommended)
Section titled “Option 1: GitHub Actions (Recommended)”Every time you push code to GitHub, it automatically deploys.
1. Create a Cloudflare API token:
- Go to Cloudflare Dashboard → Profile → API Tokens
- Click “Create Token”
- Use the “Edit Cloudflare Workers” template
- Copy the token (you’ll only see it once!)
2. Add secrets to GitHub:
- Go to your repo → Settings → Secrets and variables → Actions
- Add these secrets:
CLOUDFLARE_API_TOKEN= your tokenCLOUDFLARE_ACCOUNT_ID= from your Cloudflare dashboard URL
3. Create .github/workflows/deploy.yml:
name: Deploy Worker
on: push: branches: - main # Deploy when you push to main
jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20'
- name: Install dependencies run: npm install
- name: Deploy to Cloudflare uses: cloudflare/wrangler-action@v3 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}4. Push and watch it deploy:
git add .git commit -m "Add GitHub Actions deployment"git push origin mainGo to your repo → Actions tab to watch the deployment.
Option 2: Cloudflare Pages (For Full Websites)
Section titled “Option 2: Cloudflare Pages (For Full Websites)”If you’re building a website with a Worker backend, use Cloudflare Pages:
- Go to Cloudflare Dashboard → Workers & Pages
- Click “Create” → “Pages” → “Connect to Git”
- Select your GitHub repo
- Configure build settings:
- Build command:
npm run build - Output directory:
dist
- Build command:
- Click “Save and Deploy”
For Workers functions with Pages:
Put your Worker code in a functions/ folder:
my-project/├── src/ # Frontend code├── functions/ # Worker code (automatic!)│ └── api/│ └── hello.js├── package.json└── wrangler.tomlfunctions/api/hello.js:
export async function onRequest(context) { return new Response("Hello from Pages Function!");}This automatically creates an API at /api/hello.
Option 3: Direct Git Integration
Section titled “Option 3: Direct Git Integration”Cloudflare can deploy directly from your repo without GitHub Actions:
- Workers & Pages → Create → Workers
- Click “Connect to Git”
- Authorize Cloudflare to access your GitHub
- Select your repo and branch
Cloudflare handles the rest - pushes trigger deployments automatically.
Environment Variables & Secrets
Section titled “Environment Variables & Secrets”Never put passwords or API keys in your code!
Setting Variables
Section titled “Setting Variables”For local development, create .dev.vars:
API_KEY=your-secret-keyDATABASE_URL=postgres://localhost/mydbFor production, use the dashboard or CLI:
# Set a secret (hidden in logs)wrangler secret put API_KEY# Then type or paste the value
# Set a plain variable (in wrangler.toml)In wrangler.toml:
[vars]ENVIRONMENT = "production"MAX_ITEMS = "100"Using Variables in Code
Section titled “Using Variables in Code”export default { async fetch(request, env, ctx) { // Access secrets and variables through 'env' const apiKey = env.API_KEY; const maxItems = parseInt(env.MAX_ITEMS);
if (!apiKey) { return new Response("API key not configured", { status: 500 }); }
// Use the variables... },};Common Mistakes & How to Fix Them
Section titled “Common Mistakes & How to Fix Them”Mistake 1: “Error: No account id found”
Section titled “Mistake 1: “Error: No account id found””Problem: Wrangler doesn’t know your Cloudflare account.
Fix:
wrangler login# Or add to wrangler.toml:account_id = "your-account-id"Mistake 2: “ReferenceError: require is not defined”
Section titled “Mistake 2: “ReferenceError: require is not defined””Problem: Workers use ES modules, not CommonJS.
Wrong:
const axios = require('axios'); // ❌Right:
import axios from 'axios'; // ✅ (but see below)Better: Use the built-in fetch instead of axios:
const response = await fetch('https://api.example.com/data');const data = await response.json();Mistake 3: KV/D1 Returns null or undefined
Section titled “Mistake 3: KV/D1 Returns null or undefined”Problem: You’re testing locally but didn’t create local storage.
Fix for KV:
wrangler kv namespace create "MY_KV" --preview# Add the preview_id to wrangler.tomlFix for D1:
# Run migrations locally firstwrangler d1 execute my-database --local --file=migrations/0001_init.sqlMistake 4: “Error 10021: Script too large”
Section titled “Mistake 4: “Error 10021: Script too large””Problem: Your Worker exceeds the 1MB limit (compressed).
Fixes:
- Remove unused dependencies
- Use dynamic imports for large libraries
- Consider splitting into multiple Workers
- Upgrade to Workers Paid ($5/month) for 10MB limit
Mistake 5: CORS Errors in Browser
Section titled “Mistake 5: CORS Errors in Browser”Problem: Your frontend can’t call your Worker API.
Fix: Add CORS headers:
function corsHeaders() { return { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", };}
export default { async fetch(request, env, ctx) { // Handle preflight requests if (request.method === "OPTIONS") { return new Response(null, { headers: corsHeaders() }); }
// Your normal response with CORS headers return new Response("Hello", { headers: corsHeaders(), }); },};Mistake 6: “Error: D1_ERROR: no such table”
Section titled “Mistake 6: “Error: D1_ERROR: no such table””Problem: Migrations weren’t run on production.
Fix:
# Run on production (note: --remote not --local)wrangler d1 execute my-database --remote --file=migrations/0001_init.sqlProject Ideas for Beginners
Section titled “Project Ideas for Beginners”Build these to practice and add to your portfolio:
1. Link Shortener (Beginner)
Section titled “1. Link Shortener (Beginner)”- Store short codes in KV
- Redirect to original URLs
- Track click counts
2. Joke API (Beginner)
Section titled “2. Joke API (Beginner)”- Return random jokes from a list
- Add categories (/joke/programming, /joke/dad)
- Let users submit jokes
3. Webhook Relay (Intermediate)
Section titled “3. Webhook Relay (Intermediate)”- Receive webhooks from services (GitHub, Stripe)
- Log them to D1
- Forward to Discord/Slack
4. Image Resizer (Intermediate)
Section titled “4. Image Resizer (Intermediate)”- Accept image uploads to R2
- Resize on request using Workers
- Cache results in KV
5. Full CRUD API (Intermediate)
Section titled “5. Full CRUD API (Intermediate)”- Users, posts, comments
- Authentication with JWTs
- Rate limiting
Quick Reference
Section titled “Quick Reference”Essential Commands
Section titled “Essential Commands”# Create new projectwrangler init my-project
# Run locallywrangler dev
# Deploy to productionwrangler deploy
# View logswrangler tail
# Create KV namespacewrangler kv namespace create "NAME"
# Create D1 databasewrangler d1 create my-db
# Run D1 migrationwrangler d1 execute my-db --remote --file=migrations/001.sql
# Set secretwrangler secret put SECRET_NAMEProject Structure
Section titled “Project Structure”my-worker/├── src/│ └── index.js # Main Worker code├── migrations/ # D1 database migrations│ └── 0001_init.sql├── wrangler.toml # Cloudflare config├── package.json # Node.js dependencies├── .dev.vars # Local secrets (don't commit!)└── .gitignore # Ignore node_modules, .dev.varsMinimal wrangler.toml
Section titled “Minimal wrangler.toml”name = "my-worker"main = "src/index.js"compatibility_date = "2024-01-01"
[observability]enabled = trueMinimal Worker
Section titled “Minimal Worker”export default { async fetch(request, env, ctx) { return new Response("Hello World!"); },};Next Steps
Section titled “Next Steps”More Video Tutorials
Section titled “More Video Tutorials”Want to go deeper? Check out these official tutorials:
| Video | What You’ll Learn | Duration |
|---|---|---|
| Stateful Apps with Workers | KV caching, D1 databases, external APIs | ~20 min |
| Deploy React to Workers | Migrating existing React apps | ~15 min |
| Build Rust Powered Apps | Workers with Rust, global databases | ~25 min |
| Cloudflare Workflows Intro | Event-driven architecture | ~20 min |
Other Learning Platforms
Section titled “Other Learning Platforms”| Platform | Course | Notes |
|---|---|---|
| egghead.io | Introduction to Cloudflare Workers | Free, by Kristian Freeman |
| Cloudflare Labs | Interactive learning path | Hands-on exercises |
| FreeCodeCamp | Build an AI Chatbot Widget | Project-based tutorial |
Documentation & Examples
Section titled “Documentation & Examples”| Resource | Description |
|---|---|
| Official Docs | Comprehensive reference for all Workers features |
| Workers Examples | Official templates for common use cases |
| D1 Tutorials | Database guides (Comments API, Staff Directory) |
| KV Tutorials | Key-value storage guides |
Community
Section titled “Community”- Cloudflare Discord - Get help from other developers
- Community Forums - Search existing solutions
- @CloudflareDev - Latest announcements
Level Up
Section titled “Level Up”- Learn TypeScript - Better autocomplete and fewer bugs
- Try Hono - Fast framework built for Workers
- Use Drizzle ORM - Type-safe database queries
- Build something real - Best way to learn!
Related Resources
Section titled “Related Resources”- Project Rules & Lessons Learned - Common bugs and fixes
- Claude Code Guide - AI-assisted development
- Software Engineering - Development resources
Questions? Found an error? Open an issue or contribute to this guide.