# Workerd Patterns ## Multi-Service Architecture ```capnp const config :Workerd.Config = ( services = [ (name = "frontend", worker = ( modules = [(name = "index.js", esModule = embed "frontend/index.js")], compatibilityDate = "2024-01-15", bindings = [(name = "API", service = "api")] )), (name = "api", worker = ( modules = [(name = "index.js", esModule = embed "api/index.js")], compatibilityDate = "2024-01-15", bindings = [(name = "DB", service = "postgres"), (name = "CACHE", kvNamespace = "kv")] )), (name = "postgres", external = (address = "db.internal:5432", http = ())), (name = "kv", disk = (path = "/var/kv", writable = true)) ], sockets = [(name = "http", address = "*:8080", http = (), service = "frontend")] ); ``` ## Durable Objects ```capnp const worker :Workerd.Worker = ( modules = [(name = "index.js", esModule = embed "index.js"), (name = "room.js", esModule = embed "room.js")], compatibilityDate = "2024-01-15", bindings = [(name = "ROOMS", durableObjectNamespace = "Room")], durableObjectNamespaces = [(className = "Room", uniqueKey = "v1")], durableObjectStorage = (localDisk = "/var/do") ); ``` ## Dev vs Prod Configs ```capnp # Use parameter bindings for env-specific config const baseWorker :Workerd.Worker = ( modules = [(name = "index.js", esModule = embed "src/index.js")], compatibilityDate = "2024-01-15", bindings = [(name = "API_URL", parameter = (type = text))] ); const prodWorker :Workerd.Worker = ( inherit = "base-service", bindings = [(name = "API_URL", text = "https://api.prod.com")] ); ``` ## HTTP Reverse Proxy ```capnp services = [ (name = "proxy", worker = (serviceWorkerScript = embed "proxy.js", compatibilityDate = "2024-01-15", bindings = [(name = "BACKEND", service = "backend")])), (name = "backend", external = (address = "internal:8080", http = ())) ] ``` ## Local Development **Recommended:** Use Wrangler ```bash wrangler dev # Uses workerd internally ``` **Direct workerd:** ```bash workerd serve config.capnp --socket-addr http=*:3000 --verbose ``` **Environment variables:** ```capnp bindings = [(name = "DATABASE_URL", fromEnvironment = "DATABASE_URL")] ``` ## Testing ```bash workerd test config.capnp workerd test config.capnp --test-only=test.js ``` Test files must be included in `modules = [...]` config. ## Production Deployment ### Compiled Binary (Recommended) ```bash workerd compile config.capnp myConfig -o production-server ./production-server ``` ### Docker ```dockerfile FROM debian:bookworm-slim RUN apt-get update && apt-get install -y ca-certificates COPY workerd /usr/local/bin/ COPY config.capnp /etc/workerd/ COPY src/ /etc/workerd/src/ EXPOSE 8080 CMD ["workerd", "serve", "/etc/workerd/config.capnp"] ``` ### Systemd ```ini # /etc/systemd/system/workerd.service [Service] ExecStart=/usr/bin/workerd serve /etc/workerd/config.capnp --socket-fd http=3 Restart=always User=nobody ``` See systemd socket activation docs for complete setup. ## Framework Integration ### Hono ```javascript import { Hono } from 'hono'; const app = new Hono(); app.get('/', (c) => c.text('Hello Hono!')); app.get('/api/:id', async (c) => { const id = c.req.param('id'); const data = await c.env.KV.get(id); return c.json({ id, data }); }); export default app; ``` ### itty-router ```javascript import { Router } from 'itty-router'; const router = Router(); router.get('/', () => new Response('Hello itty!')); router.get('/api/:id', async (request, env) => { const { id } = request.params; const data = await env.KV.get(id); return Response.json({ id, data }); }); export default { fetch: (request, env, ctx) => router.handle(request, env, ctx) }; ``` ## Best Practices 1. **Use ES modules** over service worker syntax 2. **Explicit bindings** - no global namespace assumptions 3. **Type safety** - define `Env` interfaces (use `wrangler types`) 4. **Service isolation** - split concerns into multiple services 5. **Pin compat date** in production after testing 6. **Use ctx.waitUntil()** for background tasks 7. **Handle errors gracefully** with try/catch 8. **Configure resource limits** on caches/storage ## Common Patterns ### Error Handling ```javascript export default { async fetch(request, env, ctx) { try { return await handleRequest(request, env); } catch (error) { console.error("Request failed", error); return new Response("Internal Error", {status: 500}); } } }; ``` ### Background Tasks ```javascript export default { async fetch(request, env, ctx) { const response = new Response("OK"); // Fire-and-forget background work ctx.waitUntil( env.ANALYTICS.put(request.url, Date.now()) ); return response; } }; ``` See [configuration.md](./configuration.md) for config syntax, [api.md](./api.md) for runtime APIs, [gotchas.md](./gotchas.md) for common errors.