Your First Worker

Lesson 2 ยท Cloudflare Workers ยท ~10 minutes

Static sites are great, but sometimes you need server-side logic โ€” an API endpoint, a redirect rule, or a dynamic response. Cloudflare Workers let you run JavaScript at the edge, close to your users, with zero server management. In this lesson you'll build and deploy a simple JSON API.

๐Ÿ“– Primary Source

Get Started with Wrangler CLI โ€” the official step-by-step guide.

Prerequisites

You need two things before starting:

RequirementHow to get it
Node.js (v16.13+)nodejs.org โ€” LTS version recommended
Cloudflare accountSign up free โ€” no credit card required

Verify Node is installed:

node --version
# v20.x or higher is ideal

Scaffold the project

Cloudflare provides a CLI scaffolding tool. Run this in your terminal:

npm create cloudflare@latest -- my-first-worker

When prompted, choose:

  1. What would you like to start with? โ†’ Hello World example
  2. Which template? โ†’ Worker only
  3. Which language? โ†’ JavaScript

Then enter the project directory:

cd my-first-worker

Understand the code structure

Open src/index.js. You'll see something like this:

export default {
  async fetch(request, env, ctx) {
    return new Response("Hello World!");
  },
};

That's the entire Worker. Here's what each piece does:

PartPurpose
export defaultES module syntax โ€” Workers use standard modules, not CommonJS
fetch(request, env, ctx)Runs on every incoming HTTP request
requestThe incoming Request object (URL, headers, method, body)
envBindings โ€” KV namespaces, secrets, other services
ctxExecution context โ€” mainly ctx.waitUntil() for background tasks
new Response()What gets sent back to the client

Run locally

Start the local development server:

npx wrangler dev

Open http://localhost:8787 in your browser. You should see "Hello World!" โ€” your Worker is running locally.

Hot reload included

Wrangler watches your files. Edit src/index.js, save, and refresh the browser โ€” changes appear instantly.

Build a multi-route API

A static "Hello World" isn't useful. Let's build a simple JSON API with multiple routes. Replace the contents of src/index.js with:

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const path = url.pathname;

    // Route: JSON greeting
    if (path === "/api/hello") {
      const name = url.searchParams.get("name") || "world";
      return new Response(
        JSON.stringify({ greeting: `Hello, ${name}!` }),
        {
          headers: { "Content-Type": "application/json" },
        }
      );
    }

    // Route: Current time
    if (path === "/api/time") {
      return new Response(
        JSON.stringify({ utc: new Date().toISOString() }),
        {
          headers: { "Content-Type": "application/json" },
        }
      );
    }

    // Route: Welcome page
    if (path === "/") {
      return new Response(
        "Welcome to my first Worker! Try /api/hello or /api/time",
        {
          headers: { "Content-Type": "text/plain" },
        }
      );
    }

    // Fallback: 404
    return new Response(
      JSON.stringify({ error: "Not found" }),
      {
        status: 404,
        headers: { "Content-Type": "application/json" },
      }
    );
  },
};

Save the file and test each route in your browser (Wrangler should still be running):

URLResponse
http://localhost:8787/Plain text welcome message
http://localhost:8787/api/hello{"greeting":"Hello, world!"}
http://localhost:8787/api/hello?name=Kiro{"greeting":"Hello, Kiro!"}
http://localhost:8787/api/time{"utc":"2026-07-02T..."}
http://localhost:8787/anything-else404 JSON error
Pattern: URL routing

The pattern new URL(request.url).pathname is the foundation of all Worker routing. For larger projects you can use libraries like itty-router, but simple if checks work great for small APIs.

Deploy to production

When you're happy with it locally, deploy to Cloudflare's network:

npx wrangler deploy

The first time, Wrangler will open a browser tab to authenticate your Cloudflare account. After that, you'll see output like:

Published my-first-worker (0.5 sec)
  https://my-first-worker.YOUR-SUBDOMAIN.workers.dev

That URL is live. Your API is now running on Cloudflare's global network โ€” over 300 locations worldwide.

Instant security

Your deployed Worker automatically gets SSL/TLS encryption and Cloudflare's DDoS protection. No configuration needed โ€” it's on by default for every Worker.

Free tier limits

Workers on the free plan are generous for learning and small projects:

LimitValue
Requests per day100,000
CPU time per invocation10 ms
Worker size (compressed)1 MB
CPU time โ‰  wall clock time

The 10ms limit is CPU time, not total duration. If your Worker makes a fetch() call to an external API that takes 200ms to respond, that waiting time doesn't count. Only the milliseconds your code actively runs on the CPU matter.

What you built

In under 10 minutes you created a dynamic API endpoint that:

Next lesson, we'll add persistent storage with KV so your Worker can remember things between requests.

When a Worker makes a fetch() call to an external API that takes 200ms, how much of that counts toward your 10ms CPU limit?
Correct! The CPU limit only measures time your code spends actively executing on the processor. Network I/O โ€” waiting for fetch() responses, DNS lookups, TLS handshakes โ€” is "wall clock time" and doesn't count against the 10ms CPU budget.
Not quite. Cloudflare only counts actual CPU execution time โ€” the milliseconds your JavaScript is actively running. Time spent waiting for network responses (fetch calls, DNS, etc.) is excluded from the CPU limit.
๐Ÿ’ฌ Questions? Ask me anything. "What's the difference between CPU time and duration?" or "Can I use TypeScript instead?" โ€” I'm here to help.
โ† Back Next โ†’