Handlers
The default export is a Worker. Add only the handlers you need.
fetch — HTTP
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url)
const path = url.pathname.replace(/\/+$/, '') || '/'
if (path === '/' && request.method === 'GET') {
return new Response('<h1>store</h1>', {
headers: { 'content-type': 'text/html; charset=utf-8' },
})
}
if (path === '/api/products' && request.method === 'GET') {
const { results } = await env.DB.prepare(
'select id, name, price_cents from products where active = 1',
).all()
return Response.json({ products: results })
}
return Response.json({ error: 'not found' }, { status: 404 })
},
}Gateway routes by Host (Preview). The Worker still sees pathname /api/products from / (no /{project}/{version} prefix). Use relative links in HTML.
ctx.waitUntil(promise) is available as celld implements it.
scheduled — Cron
wrangler:
"triggers": { "crons": ["0 * * * *"] }export default {
async fetch(request, env) { /* … */ },
async scheduled(controller, env, ctx) {
await env.CACHE.put('cron:last-run', String(controller.scheduledTime))
},
}Runs only while the version is ready (a live process). Archived versions do not tick. Cron.
Queues — produce from HTTP
wrangler producers + env.QUEUE.send:
await env.FULFILLMENT.send({
type: 'order.placed',
order_id: 42,
at: Date.now(),
})Producer vs consumer
A queue consumer must export queue(). If that script has no fetch, celld compiles a 404 stub. HTTP fetch + queue() on one module is not the supported cellp pattern — keep a producer Worker for HTTP (dev/examples/queue) and a separate consumer if you need queue().
Workflows — class + create
import { WorkflowEntrypoint } from 'cloudflare:workers'
export class OrderReport extends WorkflowEntrypoint {
async run(event, step) {
return await step.do('summarize orders', async () => {
const row = await this.env.DB.prepare(
'select count(*) as orders from orders',
).first()
return { ...row, generated_at: Date.now() }
})
}
}
export default {
async fetch(request, env) {
if (new URL(request.url).pathname === '/api/report' && request.method === 'POST') {
const instance = await env.REPORTS.create({ params: { scope: 'orders' } })
return Response.json({ id: instance.id })
}
return new Response('ok')
},
}class_name in wrangler must be OrderReport. Preview versions do not inherit in-flight instances. Workflows.
Durable Objects
export class Counter {
constructor(state, env) {
this.state = state
this.env = env
}
async fetch(request) {
let n = (await this.state.storage.get('n')) ?? 0
n++
await this.state.storage.put('n', n)
return Response.json({ n, version: this.env.VERSION_ID })
}
}
export default {
async fetch(request, env) {
const id = env.COUNTER.idFromName(env.VERSION_ID ?? 'default')
return env.COUNTER.get(id).fetch(request)
},
}Repo: dev/examples/counter. Confirm gaps in celld compat before production use.
Platform keys on env
Always present, read-only: PROJECT_ID, VERSION_ID. Your wrangler vars and Dashboard overrides sit beside them. Env.
Static HTML
You can return new Response(html, { headers: { 'content-type': 'text/html' } }) from fetch, as the commerce storefront does. wrangler assets / Workers Sites work within celld support — see Supported stacks.