# Part 1: Understanding Cloudflare Workers & Edge Middleware

# Introduction — The Rise of Edge Computing

In the last decade, we’ve witnessed a massive shift from monoliths → microservices → serverless. Each step moved compute closer to flexibility, but not necessarily closer to users.

Edge computing changes that. It allows you to run code within the CDN network itself, milliseconds from the user. Cloudflare Workers make this possible by letting developers run JavaScript and TypeScript code in more than 300 data centers globally.

Running logic at the edge improves latency, enhances security, reduces costs, and scales automatically without traditional server provisioning. In this series, we will build production-ready middleware for HTTP and WebSocket traffic. The focus will be on rate limiting, but the patterns we discuss will apply to caching, authentication, and logging.

## What Are Cloudflare Workers?

Cloudflare Workers are small JavaScript or TypeScript programs that run inside V8 isolates the same sandbox used by Chrome and Node.js, but stripped down and optimized for edge execution.

When a request hits your domain, it passes through Cloudflare’s network. Your Worker can then inspect or modify the request, authenticate or rate-limit the client, cache responses, transform data, or proxy WebSocket connections before reaching your origin servers.

Workers start in less than one millisecond and can handle millions of requests concurrently. They are ideal for ultra-fast, high-scale logic that runs close to the user.

### How Workers Differ from Lambda and Friends

| Feature | Cloudflare Worker | AWS Lambda | Vercel Functions |
| --- | --- | --- | --- |
| Startup Time | ~1 ms (no cold start) | 100-500 ms | 100-300 ms |
| Runtime | V8 Isolate (Web APIs) | Node.js Container | Node.js |
| Max CPU Time | 50 ms (default plan) | up to 15 min | 10 s |
| Deploy Scope | Global edge | Single region | Region + edge cache |
| State Model | Durable Objects & KV | DB / External | DB / External |

**Workers are designed for short-lived, high-performance workloads. They are not suitable for long-running tasks or heavy compute operations.**

### The V8 Isolate Runtime ⚙️ - Browser DNA at the Edge

Because Cloudflare Workers run inside a V8 isolate, the environment is more like a browser than Node.js.

**What Works**:

* Standard Web APIs such as `fetch`, `Request`, `Response`, `URL`, `Headers`, `FormData`, `ReadableStream`, and `Crypto`.
    
* Modern ECMAScript syntax including `async/await` and modules.
    
* Cloudflare bindings such as `KV`, `R2`, `Durable Objects`, `D1`, `Queues`, and `AI` integrations.
    

**What Does Not Work:**

Node.js-specific APIs such as `fs`, `net`, `tls`, `dgram`, `path`, `os`, `cluster`, and `child_process` are not available. Packages that depend on these will fail.

**This means not all npm packages will work. Any library that depends on Node internals will break.**

### Alternatives and Patterns

| Node Package Type | Edge-Safe Alternative |
| --- | --- |
| HTTP client (`axios`) | Native `fetch` API |
| Router (`express`) | `hono` or `itty-router` |
| Cache / Storage | Cloudflare KV / R2 |
| Crypto helpers | WebCrypto API |
| Env config | Worker `Env` bindings / `wrangler.toml` |

**A quick check:** if the code runs in Chrome’s DevTools console, it probably runs in a Cloudflare Worker.

## Why This Matters for Middleware Design

Building middleware on Workers means embracing Web APIs and message-based architecture, not Node servers.

Instead of spinning up a Redis client, you might use a Durable Object that acts as a distributed counter. Instead of file I/O, you rely on Cloudflare KV for persistence. Instead of Express middleware, you define lightweight fetch handlers.

These constraints lead to simpler, safer, and globally scalable patterns, but they also force you to design differently.

## The Idea of Edge Middleware

Think of middleware as the logic between a user’s request and your origin response.

```markdown
         ┌────────────────────┐
Client → │   Edge Middleware  │ → Backend API
         └────────────────────┘
                  ▲
            Logging, Auth,
            Rate Limiting
```

At the edge, middleware can decide within milliseconds whether a request deserves to reach your backend or not.

Some of the benefits of this approach are:

* Better Security: Bad actors can be dropped early, even before reaching your back-end service.
    
* Improved Performance: Reduce round-trips to origin.
    
* Cost: Save bandwidth and compute.
    
* Observability: Capture metrics closer to users, which can be further exported to Loki/Grafana/Mimir.
    

## When to Use Edge Middleware

Below are some real, production-ready workloads handled daily by Cloudflare Workers.

| Scenario | Edge Solution | Benefit |
| --- | --- | --- |
| API rate limiting | Worker + Durable Object | Global throttling |
| Auth token check | Worker middleware | Drop unauthorized before origin |
| Request logging | Worker → Logs API | Low-latency telemetry |
| Feature flags | Worker KV lookup | Rollouts per region |
| WebSocket gateway | Worker + Durable Object hub | Real-time control at edge |

## Why Edge Rate Limiting Beats Traditional Approaches

Traditional rate limiting relies on in-memory counters or Redis. This introduces latency and single points of failure.

Edge rate limiting solves this by:

* Storing counters in Durable Objects, automatically sharded by user key.
    
* Evaluating requests close to the user.
    
* Providing consistent global enforcement per client.
    

This results in sub-millisecond enforcement with minimal backend dependency.

## Durable Objects (DO)

Durable Objects are stateful actors that maintain consistent state across requests.

A Durable Object can:

* Store data in memory.
    
* Persist data between requests.
    
* Receive messages via fetch().
    
* Guarantee one instance per ID worldwide.
    

Each API key or client can have a dedicated Durable Object instance. This is ideal for token buckets, request counters, or WebSocket hubs.

```md
      +---------------------------+
      | Durable Object: user_123  |
      | tokens = 5 of 10          |
      | lastRefill = 1699999999   |
      +---------------------------+
```

---

## Architecture Vision for This Series

```md
                ┌────────────────────────────┐
                │        Cloudflare Edge     │
                │ ┌────────────────────────┐ │
Client → → → →  │ │ Worker (Middleware)    │ │ → Backend
                │ │ - Auth & Rate Limit    │ │
                │ │ - WS Proxy Gateway     │ │
                │ └────────────────────────┘ │
                │ ┌────────────────────────┐ │
                │ │ Durable Object(s)      │ │
                │ │ - Per-user counters    │ │
                │ │ - Token buckets        │ │
                │ └────────────────────────┘ │
                └────────────────────────────┘
```

The beauty of this design is that it scales automatically — no central Redis, no replication nightmares, no cold starts.

## Setting Up the Environment

1. Install Wrangler
    

```bash
npm install -g wrangler
```

Confirm:

```bash
npx wrangler --version
```

*Note:* At the time of writing, Wrangler requires at least Node.js v20.0.0. Consider using a Node.js version manager such as [nvm](https://github.com/nvm-sh/nvm) to switch node versions.

2. Login to Cloudflare
    

```bash
npx wrangler login
```

3. Initialize a Worker Project
    

```bash
npx wrangler init edge-middleware
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763476646424/9cfe4595-a640-4d4c-b272-7c2db2dc3008.png align="center")

Response to all prompts are shared in the screenshot above. Your file/folder structure should be similar to this:

```bash
.
├── node_modules\
├── package-lock.json
├── package.json
├── src
│   └── index.ts
├── test
│   ├── env.d.ts
│   ├── index.spec.ts
│   └── tsconfig.json
├── tsconfig.json
├── vitest.config.mts
├── worker-configuration.d.ts
└── wrangler.jsonc
```

We’ll define the RateLimiter Durable Object class in Part 2.

---

## Your First Hello-World Worker

Try a simple fetch handler:

```ts
//src/index.ts
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    return new Response("Hello from Cloudflare worker", {
      headers: { "content-type": "text/plain" },
    });
  },
} satisfies ExportedHandler<Env>;
```

Run locally:

```bash
npx wrangler dev
```

Visit [http://localhost:8787](http://localhost:8787) → you’ll see the response instantly.

---

## Practice: Simple Edge Middleware

Let’s add basic validation before proxying to a backend:

```ts
//src/index.ts
export default {
  async fetch(req: Request) {
    const url = new URL(req.url);

    if (!url.pathname.startsWith("/api")) {
      return new Response("Forbidden", { status: 403 });
    }

    const backend = await fetch("https://api.yourbackend.com" + url.pathname, {
      headers: req.headers,
      method: req.method,
      body: req.body,
    });

    const res = new Response(backend.body, backend);
    res.headers.set("x-edge-checked", "true");
    return res;
  },
};
```

You’ve now written an edge gateway that can inspect, block, and modify traffic before it reaches your origin. For local testing, I’ll run a basic HTTP server locally and use that as my backend endpoint.

```bash
python3 -m http.server 8001
```

You can replace `https://api.yourbackend.com` in the src/index.ts with `http://localhost:8001` if testing locally too.

### Debugging and Local Testing

Wrangler provides a powerful local runtime which simulates the Cloudflare environment, including Durable Objects and bindings. You can get it started by running:

```bash
npx wrangler dev
```

#### Simulate requests:

```bash
for i in {1..10}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8787/api/test; done
```

### Deployment

Deploying your worker to Cloudflare makes it globally accessible, and can be achieved by running:

```bash
npx wrangler deploy
```

Your Worker is instantly available and the link is displayed in the terminal, something like this: `https://<your-project-id>.workers.dev`. You can also add a custom domain to the worker via the Cloudflare worker dashboard.

## Observability and Metrics

Cloudflare gives built-in analytics, but you can push custom metrics too:

* **Workers Logs API**: stream structured logs.
    
* **Durable Object storage**: aggregate usage per key.
    
* **Integrations**: send logs to Grafana/Loki/Mimir (LGTM stack).
    
* **Wrangler tail**: live-view logs during dev.
    

Since we interested in the live-view logs of the deployed worker, we can run:

```bash
npx wrangler tail
```

## Closing Thoughts

Cloudflare Workers change how we think about deploying logic on the internet. Instead of running code on a single server or region, the platform pushes your logic to the edge, closer to every user. This makes Workers feel fast in practice, even when doing simple tasks like modifying headers, validating requests, or shaping traffic before it reaches your backend.

Because Workers run inside a V8 isolate, they follow a “browser-style” environment. This means you won’t have access to traditional Node.js modules, but you do get lightweight execution, instant cold starts, and a highly secure sandbox. Once you work within those boundaries, the platform becomes surprisingly flexible and powerful.

Part 1 focused on building an intuition for Workers and the concept of middleware at the edge. In Part 2, we move from ideas to implementation. We’ll build a practical HTTP rate limiter using Durable Objects, compare popular algorithms, and walk through how they behave in real-world workloads.

[Next Up: **\[Part 2: Building an HTTP Rate Limiter with Durable Objects\]**](https://sorxcode.com/part-2-building-an-http-rate-limiter-with-cloudflare-workers)
