Kynetra FX

Documentation

Playground

The Kynetra FX playground runs the real framework — compiled to a ~32 KB browser bundle — entirely in your browser. Write route handlers, middleware, and contracts, click Run, and see the HTTP response immediately. No install, no server, no build step required.

Overview#

The playground is not a simulator or a mock. It executes the actual @kynetra/fx core — the same code that ships to production — in a sandboxed in-browser environment powered by @kynetra/fx-browser. Requests are simulated HTTP calls routed through the real FX context and middleware pipeline, so the behaviour you see in the playground matches what runs on a Cloudflare Worker or Node server exactly.

  • Real framework code — not a DSL or simplified API.
  • TypeScript support with type-checked input in the editor.
  • Preset examples covering every major framework feature.
  • Shareable URLs — playground state is encoded in the URL hash.
  • Simulated HTTP requests sent to the in-browser app via a request panel.

Tip

The playground runs the same core framework as production. Code that works here will work in a Cloudflare Worker or Node server with only the import changed from the browser bundle to @kynetra/fx.

How it works#

The @kynetra/fx-browser package is a pre-bundled, tree-shaken build of the core framework targeting the browser environment. It weighs approximately 32 KB gzipped and exposes the same createFX, fx, middleware helpers, and plugin system that you would import from @kynetra/fx in a server environment.

When you click Run, the playground:

  • Transpiles your TypeScript in-browser using a lightweight compiler worker.
  • Evaluates the module and reads the export default as the FX application.
  • Constructs a Request object from the request panel inputs (method, path, headers, body).
  • Calls app.fetch(request) and renders the returned Response in the output panel.

Because the framework uses the standard Request / Response Web API throughout, every middleware, contract, plugin, and hook you write behaves identically whether it is executed inside the browser bundle or on a server runtime.

Note

Browser APIs (Fetch, Web Crypto, URLPattern, Streams) are available inside the playground because the bundle targets browsers. Node.js- and Cloudflare-specific globals — such as Bun, Deno, D1Database, and process — are not available. See Cloudflare Workers and Node.js for runtime-specific features.

Getting started#

1

Open the playground

Navigate to /playground in your browser. The editor opens with the Hello World preset pre-loaded so you can run code immediately.
2

Pick a preset (or start from scratch)

The preset selector at the top of the editor lists all built-in examples. Choose one to replace the editor contents, or clear the editor and write from scratch.
3

Edit the code

The editor supports TypeScript. Type annotations, generics, and imports from the browser bundle are all valid. The editor highlights syntax errors inline before you run.
4

Send a request

Fill in the request panel on the right: pick a method, enter a path (e.g. /users/42), optionally add headers or a JSON body. Then click Run or press Cmd+Enter (Ctrl+Enter on Windows and Linux).
5

Read the output

The output panel shows the HTTP status, response headers, and body. Errors thrown by your code appear as stack traces in the error panel below the output.

Preset examples#

The playground ships seven presets that demonstrate common patterns. Each preset is a self-contained, runnable example.

Hello World

The minimal createFX app — a single route returning JSON. Good as a starting point when you want to experiment with something specific.

hello-world.ts
import { createFX } from '@kynetra/fx'
 
const app = createFX()
 
app.get('/', (ctx) => ctx.json({ hello: 'world' }))
 
return app

Middleware stack

Demonstrates requestId, logger, cors, and secureHeaders stacked in the correct order. The requestId middleware injects a unique ID header on every request; the others add standard security and CORS headers to every response.

middleware.ts
import { createFX, cors, logger } from '@kynetra/fx'
import { requestId, secureHeaders } from '@kynetra/fx-middleware'
 
const app = createFX()
 
app.use(requestId())
app.use(logger())
app.use(cors({ origin: '*' }))
app.use(secureHeaders())
 
app.get('/', (ctx) => ctx.json({ hello: 'world' }))
 
app.get('/users/:id', (ctx) => {
return ctx.json({ id: ctx.params.id, name: 'Alice' })
})
 
app.post('/echo', async (ctx) => {
const body = await ctx.jsonBody()
return ctx.json({ echo: body })
})
 
return app

Contracts and validation

Shows app.route with an fx.object input schema. The framework validates the incoming request body against the schema before calling the handler; invalid bodies produce a 422 response automatically. See Contracts for the full API.

contracts.ts
import { createFX, fx } from '@kynetra/fx'
 
const app = createFX()
 
app.route({
method: 'POST',
path: '/greet',
input: fx.object({
name: fx.string().min(1),
age: fx.optional(fx.number().min(0)),
}),
handler: (ctx) => {
const { name, age } = ctx.input
return ctx.json({ message: `Hello, ${name}!`, age })
},
})
 
app.route({
method: 'GET',
path: '/items',
query: fx.object({
page: fx.optional(fx.string()),
limit: fx.optional(fx.string()),
}),
handler: (ctx) => {
const { page = '1', limit = '20' } = ctx.query
return ctx.json({ page: Number(page), limit: Number(limit), items: [] })
},
})
 
return app

Auth

Demonstrates jwtAuth middleware and the requireAuth guard. The playground generates a mock JWT so you can test the auth flow end-to-end without a real signing key. See Auth for full configuration options.

WASM guest

Uses a fake WASM guest module to demonstrate the wasmHandler integration. The guest receives a WasmRequest ABI and returns a WasmResponse. In production you would load an actual .wasm binary; the playground uses a JavaScript stand-in that mirrors the same interface. See WASM for the full polyglot guide.

SaaS kernel

Spins up a createSaasKernel instance with an in-memory store and wires it into a small API. You can create organisations, add members, and query feature flags — all using the real SaaS kernel code. See SaaS kernel for production setup.

OpenAPI

Calls generateOpenAPI on a small routed app and returns the spec as JSON. Send a GET /openapi.json request to see the generated document. See OpenAPI for full generation options.

Editor tips#

Keyboard shortcuts

  • Cmd+Enter / Ctrl+Enter — run the current code.
  • Cmd+S / Ctrl+S — also triggers a run (mirrors the "save and run" habit).
  • Standard editor shortcuts (undo, redo, find, multi-cursor) are all available.

The playground encodes the entire editor contents and the current request configuration into the URL hash whenever you run. Copy the URL from your browser's address bar to share a runnable example with colleagues or in a bug report. Visiting the URL restores the editor and request panel exactly as you left them.

Tip

Share links are a great way to attach a minimal reproducible example to a GitHub issue or a support request. The recipient does not need to install anything — they click the link and run the code immediately.

TypeScript

The editor understands TypeScript. Type annotations, generics, as casts, and non-null assertions are all valid. The type-checker runs in a worker so it does not block the editor; squiggles appear within a second of you stopping typing.

Types for @kynetra/fx are bundled with the browser build, so autocomplete and hover documentation work out of the box for all core exports including FXContext, FXMiddleware, fx.* schema types, and all middleware options.

Capabilities and limitations#

What you can do

  • Define routes with any method — GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD.
  • Stack middleware using app.use.
  • Validate inputs and query strings with fx schemas or Standard Schema adapters.
  • Use plugins, hooks, error handlers, and response helpers.
  • Simulate multiple requests against the same app instance in sequence.
  • Use Web Crypto, URLPattern, Streams, and other browser-available APIs.
  • Import from multiple modules within the same editor session using dynamic import().

Limitations

  • No outbound network requests — fetch() calls to external URLs are blocked by the browser sandbox.
  • No file system access — Node.js fs, path, and similar modules are unavailable.
  • No Cloudflare-specific globals — D1Database, KVNamespace, and other binding types exist as types only and cannot be instantiated.
  • No persistent state between page reloads — in-memory stores reset when you close the tab.
  • Bundle size is fixed — you cannot install additional npm packages inside the playground.

Full runnable example#

The example below covers routing, middleware, path parameters, request body parsing, and error handling in a single file. Copy it into the playground to explore interactively.

full-example.ts
import { createFX, cors, logger, fx } from '@kynetra/fx'
import { requestId } from '@kynetra/fx-middleware'
 
// In-memory store (resets each time you click Run)
const users = new Map<string, { id: string; name: string; email: string }>([
['1', { id: '1', name: 'Alice', email: 'alice@example.com' }],
['2', { id: '2', name: 'Bob', email: 'bob@example.com' }],
])
 
const app = createFX()
 
// Global middleware
app.use(requestId())
app.use(logger())
app.use(cors({ origin: '*' }))
 
// List users
app.get('/users', (ctx) => {
return ctx.json({ users: Array.from(users.values()) })
})
 
// Get user by ID
app.get('/users/:id', (ctx) => {
const user = users.get(ctx.params.id)
if (!user) return ctx.json({ error: 'not found' }, { status: 404 })
return ctx.json(user)
})
 
// Create user (validated)
app.route({
method: 'POST',
path: '/users',
input: fx.object({
name: fx.string().min(1).max(100),
email: fx.string().min(5),
}),
handler: (ctx) => {
const id = String(users.size + 1)
const user = { id, ...ctx.input }
users.set(id, user)
return ctx.json(user, { status: 201 })
},
})
 
// Delete user
app.delete('/users/:id', (ctx) => {
if (!users.has(ctx.params.id)) {
return ctx.json({ error: 'not found' }, { status: 404 })
}
users.delete(ctx.params.id)
return ctx.json({ deleted: true })
})
 
// Health check
app.get('/health', (ctx) =>
ctx.json({ status: 'ok', ts: new Date().toISOString() })
)
 
return app

Next steps#

Once you have a working prototype in the playground, moving it to production requires only minimal changes:

  • Cloudflare Workers — wrap the app with cloudflare(app) from @kynetra/fx-cloudflare and deploy with Wrangler.
  • Node.js — call serve(app) from @kynetra/fx-node to start an HTTP server.
  • Contracts — explore the full schema API in the Contracts documentation.
  • WASM — learn how to bundle real compiled guests in the WASM guide.