mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# Workerd Runtime
|
||||
|
||||
V8-based JS/Wasm runtime powering Cloudflare Workers. Use as app server, dev tool, or HTTP proxy.
|
||||
|
||||
## ⚠️ IMPORTANT SECURITY NOTICE
|
||||
**workerd is NOT a hardened sandbox.** Do not run untrusted code. It's designed for deploying YOUR code locally/self-hosted, not multi-tenant SaaS. Cloudflare production adds security layers not present in open-source workerd.
|
||||
|
||||
## Decision Tree: When to Use What
|
||||
|
||||
**95% of users:** Use Wrangler
|
||||
- Local development: `wrangler dev` (uses workerd internally)
|
||||
- Deployment: `wrangler deploy` (deploys to Cloudflare)
|
||||
- Types: `wrangler types` (generates TypeScript types)
|
||||
|
||||
**Use raw workerd directly only if:**
|
||||
- Self-hosting Workers runtime in production
|
||||
- Embedding runtime in C++ application
|
||||
- Custom tooling/testing infrastructure
|
||||
- Debugging workerd-specific behavior
|
||||
|
||||
**Never use workerd for:**
|
||||
- Running untrusted/user-submitted code
|
||||
- Multi-tenant isolation (not hardened)
|
||||
- Production without additional security layers
|
||||
|
||||
## Key Features
|
||||
- **Standards-based**: Fetch API, Web Crypto, Streams, WebSocket
|
||||
- **Nanoservices**: Service bindings with local call performance
|
||||
- **Capability security**: Explicit bindings prevent SSRF
|
||||
- **Backwards compatible**: Version = max compat date supported
|
||||
|
||||
## Architecture
|
||||
```
|
||||
Config (workerd.capnp)
|
||||
├── Services (workers/endpoints)
|
||||
├── Sockets (HTTP/HTTPS listeners)
|
||||
└── Extensions (global capabilities)
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
```bash
|
||||
workerd serve config.capnp
|
||||
workerd compile config.capnp myConfig -o binary
|
||||
workerd test config.capnp
|
||||
```
|
||||
|
||||
## Platform Support & Beta Status
|
||||
|
||||
| Platform | Status | Notes |
|
||||
|----------|--------|-------|
|
||||
| Linux (x64) | Stable | Primary platform |
|
||||
| macOS (x64/ARM) | Stable | Full support |
|
||||
| Windows | Beta | Use WSL2 for best results |
|
||||
| Linux (ARM64) | Experimental | Limited testing |
|
||||
|
||||
workerd is in **active development**. Breaking changes possible. Pin versions in production.
|
||||
|
||||
## Core Concepts
|
||||
- **Service**: Named endpoint (worker/network/disk/external)
|
||||
- **Binding**: Capability-based resource access (KV/DO/R2/services)
|
||||
- **Compatibility date**: Feature gate (always set!)
|
||||
- **Modules**: ES modules (recommended) or service worker syntax
|
||||
|
||||
## Reading Order (Progressive Disclosure)
|
||||
|
||||
**Start here:**
|
||||
1. This README (overview, decision tree)
|
||||
2. [patterns.md](./patterns.md) - Common workflows, framework examples
|
||||
|
||||
**When you need details:**
|
||||
3. [configuration.md](./configuration.md) - Config format, services, bindings
|
||||
4. [api.md](./api.md) - Runtime APIs, TypeScript types
|
||||
5. [gotchas.md](./gotchas.md) - Common errors, debugging
|
||||
|
||||
## Related References
|
||||
- [workers](../workers/) - Workers runtime API documentation
|
||||
- [miniflare](../miniflare/) - Testing tool built on workerd
|
||||
- [wrangler](../wrangler/) - CLI that uses workerd for local dev
|
||||
185
.agents/skills/cloudflare-deploy/references/workerd/api.md
Normal file
185
.agents/skills/cloudflare-deploy/references/workerd/api.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Workerd APIs
|
||||
|
||||
## Worker Code (JS/TS)
|
||||
|
||||
### ES Modules (Recommended)
|
||||
```javascript
|
||||
export default {
|
||||
async fetch(request, env, ctx) {
|
||||
const value = await env.KV.get("key"); // Bindings in env
|
||||
const response = await env.API.fetch(request); // Service binding
|
||||
ctx.waitUntil(logRequest(request)); // Background task
|
||||
return new Response("OK");
|
||||
},
|
||||
async adminApi(request, env, ctx) { /* Named entrypoint */ },
|
||||
async queue(batch, env, ctx) { /* Queue consumer */ },
|
||||
async scheduled(event, env, ctx) { /* Cron handler */ }
|
||||
};
|
||||
```
|
||||
|
||||
### TypeScript Types
|
||||
|
||||
**Generate from wrangler.toml (Recommended):**
|
||||
```bash
|
||||
wrangler types # Output: worker-configuration.d.ts
|
||||
```
|
||||
|
||||
**Manual types:**
|
||||
```typescript
|
||||
interface Env {
|
||||
API: Fetcher;
|
||||
CACHE: KVNamespace;
|
||||
STORAGE: R2Bucket;
|
||||
ROOMS: DurableObjectNamespace;
|
||||
API_KEY: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
return new Response(await env.CACHE.get("key"));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Setup:**
|
||||
```bash
|
||||
npm install -D @cloudflare/workers-types
|
||||
```
|
||||
|
||||
```json
|
||||
// tsconfig.json
|
||||
{"compilerOptions": {"types": ["@cloudflare/workers-types"]}}
|
||||
```
|
||||
|
||||
### Service Worker Syntax (Legacy)
|
||||
```javascript
|
||||
addEventListener('fetch', event => {
|
||||
event.respondWith(handleRequest(event.request));
|
||||
});
|
||||
|
||||
async function handleRequest(request) {
|
||||
const value = await KV.get("key"); // Bindings as globals
|
||||
return new Response("OK");
|
||||
}
|
||||
```
|
||||
|
||||
### Durable Objects
|
||||
```javascript
|
||||
export class Room {
|
||||
constructor(state, env) { this.state = state; this.env = env; }
|
||||
|
||||
async fetch(request) {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === "/increment") {
|
||||
const value = (await this.state.storage.get("counter")) || 0;
|
||||
await this.state.storage.put("counter", value + 1);
|
||||
return new Response(String(value + 1));
|
||||
}
|
||||
return new Response("Not found", {status: 404});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### RPC Between Services
|
||||
```javascript
|
||||
// Caller: env.AUTH.validateToken(token) returns structured data
|
||||
const user = await env.AUTH.validateToken(request.headers.get("Authorization"));
|
||||
|
||||
// Callee: export methods that return data
|
||||
export default {
|
||||
async validateToken(token) { return {id: 123, name: "Alice"}; }
|
||||
};
|
||||
```
|
||||
|
||||
## Web Platform APIs
|
||||
|
||||
### Fetch
|
||||
- `fetch()`, `Request`, `Response`, `Headers`
|
||||
- `AbortController`, `AbortSignal`
|
||||
|
||||
### Streams
|
||||
- `ReadableStream`, `WritableStream`, `TransformStream`
|
||||
- Byte streams, BYOB readers
|
||||
|
||||
### Web Crypto
|
||||
- `crypto.subtle` (encrypt/decrypt/sign/verify)
|
||||
- `crypto.randomUUID()`, `crypto.getRandomValues()`
|
||||
|
||||
### Encoding
|
||||
- `TextEncoder`, `TextDecoder`
|
||||
- `atob()`, `btoa()`
|
||||
|
||||
### Web Standards
|
||||
- `URL`, `URLSearchParams`
|
||||
- `Blob`, `File`, `FormData`
|
||||
- `WebSocket`
|
||||
|
||||
### Server-Sent Events (EventSource)
|
||||
```javascript
|
||||
// Server-side SSE
|
||||
const { readable, writable } = new TransformStream();
|
||||
const writer = writable.getWriter();
|
||||
writer.write(new TextEncoder().encode('data: Hello\n\n'));
|
||||
return new Response(readable, {headers: {'Content-Type': 'text/event-stream'}});
|
||||
```
|
||||
|
||||
### HTMLRewriter (HTML Parsing/Transformation)
|
||||
```javascript
|
||||
const response = await fetch('https://example.com');
|
||||
return new HTMLRewriter()
|
||||
.on('a[href]', {
|
||||
element(el) {
|
||||
el.setAttribute('href', `/proxy?url=${encodeURIComponent(el.getAttribute('href'))}`);
|
||||
}
|
||||
})
|
||||
.on('script', { element(el) { el.remove(); } })
|
||||
.transform(response);
|
||||
```
|
||||
|
||||
### TCP Sockets (Experimental)
|
||||
```javascript
|
||||
const socket = await connect({ hostname: 'example.com', port: 80 });
|
||||
const writer = socket.writable.getWriter();
|
||||
await writer.write(new TextEncoder().encode('GET / HTTP/1.1\r\n\r\n'));
|
||||
const reader = socket.readable.getReader();
|
||||
const { value } = await reader.read();
|
||||
return new Response(value);
|
||||
```
|
||||
|
||||
### Performance
|
||||
- `performance.now()`, `performance.timeOrigin`
|
||||
- `setTimeout()`, `setInterval()`, `queueMicrotask()`
|
||||
|
||||
### Console
|
||||
- `console.log()`, `console.error()`, `console.warn()`
|
||||
|
||||
### Node.js Compat (`nodejs_compat` flag)
|
||||
```javascript
|
||||
import { Buffer } from 'node:buffer';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
|
||||
const buf = Buffer.from('Hello');
|
||||
const random = randomBytes(16);
|
||||
```
|
||||
|
||||
**Available:** `node:buffer`, `node:crypto`, `node:stream`, `node:util`, `node:events`, `node:assert`, `node:path`, `node:querystring`, `node:url`
|
||||
**NOT available:** `node:fs`, `node:http`, `node:net`, `node:child_process`
|
||||
|
||||
## CLI Commands
|
||||
|
||||
```bash
|
||||
workerd serve config.capnp [constantName] # Start server
|
||||
workerd serve config.capnp --socket-addr http=*:3000 --verbose
|
||||
workerd compile config.capnp constantName -o binary # Compile to binary
|
||||
workerd test config.capnp [--test-only=test.js] # Run tests
|
||||
```
|
||||
|
||||
## Wrangler Integration
|
||||
|
||||
Use Wrangler for development:
|
||||
```bash
|
||||
wrangler dev # Uses workerd internally
|
||||
wrangler types # Generate TypeScript types from wrangler.toml
|
||||
```
|
||||
|
||||
See [patterns.md](./patterns.md) for usage examples, [configuration.md](./configuration.md) for config details.
|
||||
@@ -0,0 +1,183 @@
|
||||
# Workerd Configuration
|
||||
|
||||
## Basic Structure
|
||||
```capnp
|
||||
using Workerd = import "/workerd/workerd.capnp";
|
||||
|
||||
const config :Workerd.Config = (
|
||||
services = [(name = "main", worker = .mainWorker)],
|
||||
sockets = [(name = "http", address = "*:8080", http = (), service = "main")]
|
||||
);
|
||||
|
||||
const mainWorker :Workerd.Worker = (
|
||||
modules = [(name = "index.js", esModule = embed "src/index.js")],
|
||||
compatibilityDate = "2024-01-15",
|
||||
bindings = [...]
|
||||
);
|
||||
```
|
||||
|
||||
## Services
|
||||
**Worker**: Run JS/Wasm code
|
||||
```capnp
|
||||
(name = "api", worker = (
|
||||
modules = [(name = "index.js", esModule = embed "index.js")],
|
||||
compatibilityDate = "2024-01-15",
|
||||
bindings = [...]
|
||||
))
|
||||
```
|
||||
|
||||
**Network**: Internet access
|
||||
```capnp
|
||||
(name = "internet", network = (allow = ["public"], tlsOptions = (trustBrowserCas = true)))
|
||||
```
|
||||
|
||||
**External**: Reverse proxy
|
||||
```capnp
|
||||
(name = "backend", external = (address = "api.com:443", http = (style = tls)))
|
||||
```
|
||||
|
||||
**Disk**: Static files
|
||||
```capnp
|
||||
(name = "assets", disk = (path = "/var/www", writable = false))
|
||||
```
|
||||
|
||||
## Sockets
|
||||
```capnp
|
||||
(name = "http", address = "*:8080", http = (), service = "main")
|
||||
(name = "https", address = "*:443", https = (options = (), tlsOptions = (keypair = (...))), service = "main")
|
||||
(name = "app", address = "unix:/tmp/app.sock", http = (), service = "main")
|
||||
```
|
||||
|
||||
## Worker Formats
|
||||
```capnp
|
||||
# ES Modules (recommended)
|
||||
modules = [(name = "index.js", esModule = embed "src/index.js"), (name = "wasm.wasm", wasm = embed "build/module.wasm")]
|
||||
|
||||
# Service Worker (legacy)
|
||||
serviceWorkerScript = embed "worker.js"
|
||||
|
||||
# CommonJS
|
||||
(name = "legacy.js", commonJsModule = embed "legacy.js", namedExports = ["foo"])
|
||||
```
|
||||
|
||||
## Bindings
|
||||
Bindings expose resources to workers. ES modules: `env.BINDING`, Service workers: globals.
|
||||
|
||||
### Primitive Types
|
||||
```capnp
|
||||
(name = "API_KEY", text = "secret") # String
|
||||
(name = "CONFIG", json = '{"key":"val"}') # Parsed JSON
|
||||
(name = "DATA", data = embed "data.bin") # ArrayBuffer
|
||||
(name = "DATABASE_URL", fromEnvironment = "DB_URL") # System env var
|
||||
```
|
||||
|
||||
### Service Binding
|
||||
```capnp
|
||||
(name = "AUTH", service = "auth-worker") # Basic
|
||||
(name = "API", service = (
|
||||
name = "backend",
|
||||
entrypoint = "adminApi", # Named export
|
||||
props = (json = '{"role":"admin"}') # ctx.props
|
||||
))
|
||||
```
|
||||
|
||||
### Storage
|
||||
```capnp
|
||||
(name = "CACHE", kvNamespace = "kv-service") # KV
|
||||
(name = "STORAGE", r2Bucket = "r2-service") # R2
|
||||
(name = "ROOMS", durableObjectNamespace = (
|
||||
serviceName = "room-service",
|
||||
className = "Room"
|
||||
))
|
||||
(name = "FAST", memoryCache = (
|
||||
id = "cache-id",
|
||||
limits = (maxKeys = 1000, maxValueSize = 1048576)
|
||||
))
|
||||
```
|
||||
|
||||
### Other
|
||||
```capnp
|
||||
(name = "TASKS", queue = "queue-service")
|
||||
(name = "ANALYTICS", analyticsEngine = "analytics")
|
||||
(name = "LOADER", workerLoader = (id = "dynamic"))
|
||||
(name = "KEY", cryptoKey = (format = raw, algorithm = (name = "HMAC", hash = "SHA-256"), keyData = embed "key.bin", usages = [sign, verify], extractable = false))
|
||||
(name = "TRACED", wrapped = (moduleName = "tracing", entrypoint = "makeTracer", innerBindings = [(name = "backend", service = "backend")]))
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
```capnp
|
||||
compatibilityDate = "2024-01-15" # Always set!
|
||||
compatibilityFlags = ["nodejs_compat", "streams_enable_constructors"]
|
||||
```
|
||||
|
||||
Version = max compat date. Update carefully after testing.
|
||||
|
||||
## Parameter Bindings (Inheritance)
|
||||
```capnp
|
||||
const base :Workerd.Worker = (
|
||||
modules = [...], compatibilityDate = "2024-01-15",
|
||||
bindings = [(name = "API_URL", parameter = (type = text)), (name = "DB", parameter = (type = service))]
|
||||
);
|
||||
|
||||
const derived :Workerd.Worker = (
|
||||
inherit = "base-service",
|
||||
bindings = [(name = "API_URL", text = "https://api.com"), (name = "DB", service = "postgres")]
|
||||
);
|
||||
```
|
||||
|
||||
## Durable Objects Config
|
||||
```capnp
|
||||
const worker :Workerd.Worker = (
|
||||
modules = [...],
|
||||
compatibilityDate = "2024-01-15",
|
||||
bindings = [(name = "ROOMS", durableObjectNamespace = "Room")],
|
||||
durableObjectNamespaces = [(className = "Room", uniqueKey = "v1")],
|
||||
durableObjectStorage = (localDisk = "/var/do")
|
||||
);
|
||||
```
|
||||
|
||||
## Remote Bindings (Development)
|
||||
|
||||
Connect local workerd to production Cloudflare resources:
|
||||
|
||||
```capnp
|
||||
bindings = [
|
||||
# Remote KV (requires API token)
|
||||
(name = "PROD_KV", kvNamespace = (
|
||||
remote = (
|
||||
accountId = "your-account-id",
|
||||
namespaceId = "your-namespace-id",
|
||||
apiToken = .envVar("CF_API_TOKEN")
|
||||
)
|
||||
)),
|
||||
|
||||
# Remote R2
|
||||
(name = "PROD_R2", r2Bucket = (
|
||||
remote = (
|
||||
accountId = "your-account-id",
|
||||
bucketName = "my-bucket",
|
||||
apiToken = .envVar("CF_API_TOKEN")
|
||||
)
|
||||
)),
|
||||
|
||||
# Remote Durable Object
|
||||
(name = "PROD_DO", durableObjectNamespace = (
|
||||
remote = (
|
||||
accountId = "your-account-id",
|
||||
scriptName = "my-worker",
|
||||
className = "MyDO",
|
||||
apiToken = .envVar("CF_API_TOKEN")
|
||||
)
|
||||
))
|
||||
]
|
||||
```
|
||||
|
||||
**Note:** Remote bindings require network access and valid Cloudflare API credentials.
|
||||
|
||||
## Logging & Debugging
|
||||
```capnp
|
||||
logging = (structuredLogging = true, stdoutPrefix = "OUT: ", stderrPrefix = "ERR: ")
|
||||
v8Flags = ["--expose-gc", "--max-old-space-size=2048"] # ⚠️ Unsupported in production
|
||||
```
|
||||
|
||||
See [patterns.md](./patterns.md) for multi-service examples, [gotchas.md](./gotchas.md) for config errors.
|
||||
139
.agents/skills/cloudflare-deploy/references/workerd/gotchas.md
Normal file
139
.agents/skills/cloudflare-deploy/references/workerd/gotchas.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Workerd Gotchas
|
||||
|
||||
## Common Errors
|
||||
|
||||
### "Missing compatibility date"
|
||||
**Cause:** Compatibility date not set
|
||||
**Solution:**
|
||||
❌ Wrong:
|
||||
```capnp
|
||||
const worker :Workerd.Worker = (
|
||||
serviceWorkerScript = embed "worker.js"
|
||||
)
|
||||
```
|
||||
|
||||
✅ Correct:
|
||||
```capnp
|
||||
const worker :Workerd.Worker = (
|
||||
serviceWorkerScript = embed "worker.js",
|
||||
compatibilityDate = "2024-01-15" # Always set!
|
||||
)
|
||||
```
|
||||
|
||||
### Wrong Binding Type
|
||||
**Problem:** JSON not parsed
|
||||
**Cause:** Using `text = '{"key":"value"}'` instead of `json`
|
||||
**Solution:** Use `json = '{"key":"value"}'` for parsed objects
|
||||
|
||||
### Service vs Namespace
|
||||
**Problem:** Cannot create DO instance
|
||||
**Cause:** Using `service = "room-service"` for Durable Object
|
||||
**Solution:** Use `durableObjectNamespace = "Room"` for DO bindings
|
||||
|
||||
### Module Name Mismatch
|
||||
**Problem:** Import fails
|
||||
**Cause:** Module name includes path: `name = "src/index.js"`
|
||||
**Solution:** Use simple names: `name = "index.js"`, embed with path
|
||||
|
||||
## Network Access
|
||||
|
||||
**Problem:** Fetch fails with network error
|
||||
**Cause:** No network service configured (workerd has no global fetch)
|
||||
**Solution:** Add network service binding:
|
||||
```capnp
|
||||
services = [(name = "internet", network = (allow = ["public"]))]
|
||||
bindings = [(name = "NET", service = "internet")]
|
||||
```
|
||||
|
||||
Or external service:
|
||||
```capnp
|
||||
bindings = [(name = "API", service = (external = (address = "api.com:443", http = (style = tls))))]
|
||||
```
|
||||
|
||||
### "Worker not responding"
|
||||
**Cause:** Socket misconfigured, no fetch handler, or port unavailable
|
||||
**Solution:** Verify socket `address` matches, worker exports `fetch()`, port available
|
||||
|
||||
### "Binding not found"
|
||||
**Cause:** Name mismatch or service doesn't exist
|
||||
**Solution:** Check binding name in config matches code (`env.BINDING` for ES modules)
|
||||
|
||||
### "Module not found"
|
||||
**Cause:** Module name doesn't match import or bad embed path
|
||||
**Solution:** Module `name` must match import path exactly, verify `embed` path
|
||||
|
||||
### "Compatibility error"
|
||||
**Cause:** Date not set or API unavailable on that date
|
||||
**Solution:** Set `compatibilityDate`, verify API available on that date
|
||||
|
||||
## Performance Issues
|
||||
|
||||
**Problem:** High memory usage
|
||||
**Cause:** Large caches or many isolates
|
||||
**Solution:** Set cache limits, reduce isolate count, or use V8 flags (caution)
|
||||
|
||||
**Problem:** Slow startup
|
||||
**Cause:** Many modules or complex config
|
||||
**Solution:** Compile to binary (`workerd compile`), reduce imports
|
||||
|
||||
**Problem:** Request timeouts
|
||||
**Cause:** External service issues or DNS problems
|
||||
**Solution:** Check connectivity, DNS resolution, TLS handshake
|
||||
|
||||
## Build Issues
|
||||
|
||||
**Problem:** Cap'n Proto syntax errors
|
||||
**Cause:** Invalid config or missing schema
|
||||
**Solution:** Install capnproto tools, validate: `capnp compile -I. config.capnp`
|
||||
|
||||
**Problem:** Embed path not found
|
||||
**Cause:** Path relative to config file
|
||||
**Solution:** Use correct relative path or absolute path
|
||||
|
||||
**Problem:** V8 flags cause crashes
|
||||
**Cause:** Unsafe V8 flags
|
||||
**Solution:** ⚠️ V8 flags unsupported in production. Test thoroughly before use.
|
||||
|
||||
## Security Issues
|
||||
|
||||
**Problem:** Hardcoded secrets in config
|
||||
**Cause:** `text` binding with secret value
|
||||
**Solution:** Use `fromEnvironment` to load from env vars
|
||||
|
||||
**Problem:** Overly broad network access
|
||||
**Cause:** `network = (allow = ["*"])`
|
||||
**Solution:** Restrict to `allow = ["public"]` or specific hosts
|
||||
|
||||
**Problem:** Extractable crypto keys
|
||||
**Cause:** `cryptoKey = (extractable = true, ...)`
|
||||
**Solution:** Set `extractable = false` unless export required
|
||||
|
||||
## Compatibility Changes
|
||||
|
||||
**Problem:** Breaking changes after compat date update
|
||||
**Cause:** New flags enabled between dates
|
||||
**Solution:** Review [compat dates docs](https://developers.cloudflare.com/workers/configuration/compatibility-dates/), test locally first
|
||||
|
||||
**Problem:** "Compatibility date not supported"
|
||||
**Cause:** Workerd version older than compat date
|
||||
**Solution:** Update workerd binary (version = max compat date supported)
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource/Limit | Value | Notes |
|
||||
|----------------|-------|-------|
|
||||
| V8 flags | Unsupported in production | Use with caution |
|
||||
| Compatibility date | Must match workerd version | Update if mismatch |
|
||||
| Module count | Affects startup time | Many imports slow |
|
||||
|
||||
## Troubleshooting Steps
|
||||
|
||||
1. **Enable verbose logging**: `workerd serve config.capnp --verbose`
|
||||
2. **Check logs**: Look for error messages, stack traces
|
||||
3. **Validate config**: `capnp compile -I. config.capnp`
|
||||
4. **Test bindings**: Log `Object.keys(env)` to verify
|
||||
5. **Check versions**: Workerd version vs compat date
|
||||
6. **Isolate issue**: Minimal repro config
|
||||
7. **Review schema**: [workerd.capnp](https://github.com/cloudflare/workerd/blob/main/src/workerd/server/workerd.capnp)
|
||||
|
||||
See [configuration.md](./configuration.md) for config details, [patterns.md](./patterns.md) for working examples, [api.md](./api.md) for runtime APIs.
|
||||
192
.agents/skills/cloudflare-deploy/references/workerd/patterns.md
Normal file
192
.agents/skills/cloudflare-deploy/references/workerd/patterns.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user