Kynetra FX

Documentation

Cloudflare Workers

Kynetra FX ships a first-class Cloudflare Workers adapter via @kynetra/fx-cloudflare. One function call — cloudflare(app) — converts any FX application into a standard Workers export, giving you zero cold-starts, global distribution, and full access to Cloudflare bindings like D1, KV, R2, and Queues.

Overview#

The cloudflare() function (also exported as toCloudflareHandler()) wraps your FX application and returns the standard { fetch(request, env, ctx) } export shape that Cloudflare Workers expects. Your application logic stays identical — the adapter bridges Cloudflare's request/response types to the FX context model.

Because app.fetch(request) is portable by design, the same application can run on Bun, Deno, Node.js, or any other FX-supported runtime without changes to your route handlers. Swapping cloudflare(app) for the Node adapter is sufficient to self-host.

  • Zero cold-starts — V8 isolates spin up in microseconds, not seconds.
  • Globally distributed — your code runs at the edge closest to the user automatically.
  • No long-running processes — the Workers execution model is stateless and event-driven.
  • Bindings available via ctx.env — typed through your Env interface.
  • Full Web Crypto, Fetch, Streams, and URLPattern support included.

Installation#

1

Install the adapter package

Add @kynetra/fx-cloudflare alongside the core framework.
npm install @kynetra/fx @kynetra/fx-cloudflare
2

Install Wrangler

Wrangler is the Cloudflare CLI for local development, deployment, and managing bindings.
npm install --save-dev wrangler
3

Create wrangler.toml

Add a wrangler.toml at the root of your project. See the wrangler.toml setup section below for a full example.
4

Start the local dev server

npx wrangler dev

Minimal Worker#

Export the result of cloudflare(app) as the default export of your Worker entry file. Wrangler reads this export automatically.

src/index.ts
import { createFX } from '@kynetra/fx'
import { cloudflare } from '@kynetra/fx-cloudflare'
 
const app = createFX({ runtime: 'cloudflare' })
 
app.get('/', (ctx) => ctx.json({ hello: 'world' }))
 
app.get('/env', (ctx) => ctx.json({ bucket: ctx.env.BUCKET_NAME }))
 
return cloudflare(app)

Tip

Pass { runtime: 'cloudflare' } to createFX so the framework can optimise for the Workers execution environment — for example, avoiding Node.js-specific APIs and enabling edge-specific features like geo headers.

wrangler.toml setup#

The wrangler.toml file declares your Worker's entry point, compatibility date, environment variables, and binding declarations. Bindings defined here become properties on the env object passed to every request.

wrangler.toml
name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-01-01"
 
[vars]
APP_ENV = "production"
 
[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
 
[[kv_namespaces]]
binding = "KV"
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

The compatibility_date field pins the set of Cloudflare runtime APIs available to your Worker. Always advance this date when you want to adopt newer platform features, and test your Worker thoroughly after doing so.

Accessing bindings via ctx.env#

Every Cloudflare binding — D1 databases, KV namespaces, R2 buckets, secrets, and plain variables — is available on ctx.env inside your route handlers and middleware. The type of ctx.env is inferred from the generic parameter you pass to createFX, which should match your Env interface.

src/index.ts
import { createFX } from '@kynetra/fx'
import { cloudflare } from '@kynetra/fx-cloudflare'
 
interface Env {
DB: D1Database
KV: KVNamespace
BUCKET_NAME: string
JWT_SECRET: string
}
 
const app = createFX<{ Env: Env }>({ runtime: 'cloudflare' })
 
// ctx.env is typed as Env
app.get('/secret', (ctx) => {
const secret = ctx.env.JWT_SECRET
return ctx.text(secret ? 'present' : 'missing')
})
 
app.get('/kv/:key', async (ctx) => {
const value = await ctx.env.KV.get(ctx.params.key)
if (value === null) return ctx.json({ error: 'not found' }, { status: 404 })
return ctx.text(value)
})
 
return cloudflare(app)

Note

Secrets (such as JWT_SECRET) are declared in the Cloudflare dashboard under Workers > Settings > Variables and are never stored in wrangler.toml. During local development, place them in a .dev.vars file at the project root — Wrangler reads this file automatically and keeps it out of version control.

Env typing reference#

Cloudflare provides TypeScript types for every binding category. Import them from the @cloudflare/workers-types package (installed automatically by Wrangler).

NameTypeDescription
D1DatabasetypeBound D1 database — use with d1Sql() or d1Store() from @kynetra/fx-cloudflare.
KVNamespacetypeBound KV namespace — provides get, put, delete, and list methods.
R2BuckettypeBound R2 bucket — provides object storage operations.
Queue<T>typeBound Queue producer — use send() to enqueue messages.
DurableObjectNamespacetypeBound Durable Object namespace — provides idFromName() and get().
stringtypePlain environment variable or secret declared under [vars] or in the dashboard.

Edge model considerations#

Running on Cloudflare Workers means embracing the edge execution model. A few things work differently here compared to a traditional long-running server:

No persistent in-memory state

Each Worker invocation is isolated. Global variables are re-initialised on each cold start (though V8 isolates are often reused in practice). Do not rely on module-level variables to share state across requests. Use D1, KV, or Durable Objects instead.

CPU time limit

Workers on the free plan are limited to 10 ms of CPU time per request. Paid plans raise this to 30 seconds of wall-clock time. CPU-intensive operations such as WASM compilation or large in-memory sorts should be profiled carefully. See WASM languages for guidance on bundling native code.

No outbound TCP

Workers cannot open raw TCP connections. All outbound I/O must go through the Fetch API, Cloudflare Hyperdrive (for Postgres), or a binding. This means traditional database drivers that use raw sockets will not work — use D1 or a Hyperdrive-backed Postgres adapter instead.

Request context lifetime

The ctx.waitUntil(promise) API lets you extend a Worker's lifetime beyond the response so that background tasks (such as logging or cache warming) can complete asynchronously. Access the underlying execution context via ctx.executionCtx.

src/index.ts
app.post('/events', async (ctx) => {
const event = await ctx.jsonBody()
 
// Respond immediately, flush analytics in the background
ctx.executionCtx.waitUntil(
fetch('https://analytics.internal/ingest', {
method: 'POST',
body: JSON.stringify(event),
})
)
 
return ctx.json({ ok: true })
})

Compatibility date#

Cloudflare uses the compatibility_date field in wrangler.toml to gate breaking changes to the Workers runtime. Set it to the date you want to target, and advance it deliberately when you are ready to adopt newer behaviour.

  • Use a recent date (within the past year) for new projects to get the latest APIs and bug fixes.
  • Check the Cloudflare compatibility flags reference before advancing the date on an existing Worker.
  • The compatibility_flags array lets you opt in to individual flags ahead of their default date.
wrangler.toml
# Opt in to specific flags before they become the default
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat", "streams_enable_constructors"]

Warning

Advancing the compatibility_date can change runtime behaviour for existing Workers. Always test in a staging environment — deploy to a non-production environment with npx wrangler deploy --env staging — before rolling out to production.

Deploying#

Use Wrangler to deploy your Worker to the Cloudflare global network. The command bundles your TypeScript, uploads the script, and outputs the deployment URL.

# Deploy to production
npx wrangler deploy
 
# Deploy to a named environment (defined in wrangler.toml under [env.staging])
npx wrangler deploy --env staging

To set up continuous deployment, point a GitHub Actions workflow (or any CI system) atwrangler deploy and supply your CLOUDFLARE_API_TOKEN as a secret. Wrangler picks up the token from the environment automatically.

.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx wrangler deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

Note

See D1 adapter for running the SaaS kernel on Cloudflare's managed SQLite, WASM languages for bundling compiled code, and self-hosting for deploying to your own infrastructure instead.