Skip to content

Cloudflare Workers

Deploy EdgeZero applications to Cloudflare Workers using WebAssembly.

Prerequisites

  • Wrangler CLI
  • worker-builder: cargo install worker-builder
  • Rust wasm32-unknown-unknown target: rustup target add wasm32-unknown-unknown

Project Setup

When scaffolding with edgezero new my-app, the Cloudflare adapter includes:

crates/my-app-adapter-cloudflare/
├── Cargo.toml
├── wrangler.toml
└── src/
    ├── lib.rs
    └── main.rs

wrangler.toml

The Wrangler manifest configures your Worker:

toml
name = "my-app"
main = "build/worker/shim.mjs"
compatibility_date = "2024-01-01"

[build]
command = "edgezero build --adapter cloudflare"

Entrypoint

The Cloudflare Wasm entrypoint in src/lib.rs wires the adapter:

rust
use my_app_core::App;
use worker::*;

#[event(fetch)]
pub async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
    edgezero_adapter_cloudflare::run_app::<App>(include_str!("../../edgezero.toml"), req, env, ctx).await
}

run_app reads config-store metadata generated by edgezero_core::app! and injects the configured Cloudflare binding automatically. If you implement Hooks manually, pass the manifest source string directly to run_app.

The low-level dispatch() helper remains available only for fully manual wiring and does not inject config-store metadata. Prefer run_app or dispatch_with_config for normal use. dispatch_with_config_handle exists for advanced/manual cases where you already have a prepared ConfigStoreHandle.

Building

Build for Cloudflare's Wasm target:

bash
# Using the CLI
edgezero build --adapter cloudflare

Local Development

Run locally with Wrangler:

bash
# Using the CLI
edgezero serve --adapter cloudflare

This starts a local server at http://127.0.0.1:8787.

Deployment

Deploy to Cloudflare Workers:

bash
# Using the CLI
edgezero deploy --adapter cloudflare

# Or directly
wrangler deploy --cwd crates/my-app-adapter-cloudflare

Fetch API

Cloudflare Workers use the global fetch API for outbound requests:

rust
use edgezero_adapter_cloudflare::CloudflareProxyClient;
use edgezero_core::proxy::ProxyService;

let client = CloudflareProxyClient;
let response = ProxyService::new(client).forward(request).await?;

Unlike Fastly, there's no backend configuration needed - Workers can fetch any URL directly.

Logging

EdgeZero does not install a Cloudflare logger by default. Use your preferred logger (for example console_log or your own log implementation), and view output in Wrangler or the Cloudflare dashboard.

Logging status

Cloudflare logging is opt-in; install a logger (such as console_log) in your entrypoint if you need structured output.

Context Access

Access Cloudflare-specific APIs via the request context extensions:

rust
use edgezero_core::context::RequestContext;
use edgezero_adapter_cloudflare::CloudflareRequestContext;

async fn handler(ctx: RequestContext) -> Result<Response, EdgeError> {
    if let Some(cf_ctx) = CloudflareRequestContext::get(ctx.request()) {
        // Access Cloudflare-specific data
        let env = cf_ctx.env();
        let ctx = cf_ctx.ctx();
        // ...
    }

    // ...
}

Environment Variables & Secrets

Define variables in wrangler.toml:

toml
[vars]
API_URL = "https://api.example.com"

# Secrets are set via wrangler CLI
# wrangler secret put API_KEY

Access in handlers via the Cloudflare context or environment bindings.

Config Store

Cloudflare does not expose a Fastly-style mutable config-store product, so EdgeZero maps [stores.config] to a single JSON string binding in wrangler.toml [vars]:

toml
# edgezero.toml
[stores.config]
name = "app_config"
toml
# wrangler.toml
[vars]
app_config = '{"greeting":"hello from config store","feature.new_checkout":"false"}'

At runtime the adapter parses that JSON object and injects it as ctx.config_store(). If the configured binding is missing or contains invalid JSON, the adapter logs a warning and skips config-store injection for that request.

KV Storage

Use Cloudflare KV for edge storage:

toml
# wrangler.toml
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123"

Access via the Cloudflare environment bindings in your handler.

Durable Objects

For stateful edge computing, configure Durable Objects:

toml
# wrangler.toml
[durable_objects]
bindings = [
  { name = "COUNTER", class_name = "Counter" }
]

Streaming

Cloudflare Workers support streaming via ReadableStream. The adapter automatically converts Body::stream to Cloudflare's streaming format.

See the Streaming guide for examples and patterns.

Testing

Run contract tests for the Cloudflare adapter:

bash
WASM_BINDGEN_VERSION=$(
  awk '
    $1 == "name" && $3 == "\"wasm-bindgen\"" { in_pkg=1; next }
    in_pkg && $1 == "version" {
      gsub(/"/, "", $3)
      print $3
      exit
    }
  ' Cargo.lock
)
cargo install wasm-bindgen-cli --version "$WASM_BINDGEN_VERSION" --locked --force
export CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner
cargo test -p edgezero-adapter-cloudflare --features cloudflare --target wasm32-unknown-unknown --test contract

These tests use wasm-bindgen-test-runner and execute the adapter's real wasm32 request path. The CLI version must exactly match the workspace's wasm-bindgen version from Cargo.lock.

Manifest Configuration

Configure the Cloudflare adapter in edgezero.toml. See Configuration for the full manifest reference.

Comparison with Fastly

FeatureCloudflare WorkersFastly Compute
Targetwasm32-unknown-unknownwasm32-wasip1
Outbound requestsGlobal fetchDynamic backends (derived from URI)
StorageKV, Durable Objects, R2KV Store, Object Store
Loggingconsole.logLog endpoints
CLIWranglerFastly CLI

Next Steps

Released under the Apache License 2.0.