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.
Get Started with Wrangler CLI โ the official step-by-step guide.
You need two things before starting:
| Requirement | How to get it |
|---|---|
| Node.js (v16.13+) | nodejs.org โ LTS version recommended |
| Cloudflare account | Sign up free โ no credit card required |
Verify Node is installed:
node --version
# v20.x or higher is ideal
Cloudflare provides a CLI scaffolding tool. Run this in your terminal:
npm create cloudflare@latest -- my-first-worker
When prompted, choose:
Then enter the project directory:
cd my-first-worker
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:
| Part | Purpose |
|---|---|
export default | ES module syntax โ Workers use standard modules, not CommonJS |
fetch(request, env, ctx) | Runs on every incoming HTTP request |
request | The incoming Request object (URL, headers, method, body) |
env | Bindings โ KV namespaces, secrets, other services |
ctx | Execution context โ mainly ctx.waitUntil() for background tasks |
new Response() | What gets sent back to the client |
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.
Wrangler watches your files. Edit src/index.js, save, and refresh the browser โ changes appear instantly.
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):
| URL | Response |
|---|---|
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-else | 404 JSON error |
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.
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.
Your deployed Worker automatically gets SSL/TLS encryption and Cloudflare's DDoS protection. No configuration needed โ it's on by default for every Worker.
Workers on the free plan are generous for learning and small projects:
| Limit | Value |
|---|---|
| Requests per day | 100,000 |
| CPU time per invocation | 10 ms |
| Worker size (compressed) | 1 MB |
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.
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.