GETTING STARTED
Quick Start
This walks through the smallest complete Futon app — a handful of routes, some middleware, and a Node runtime adapter to actually serve requests.
1. Build the App
import { Futon, json, logger, cors } from '@rasenganjs/futon'; export const app = new Futon(); app.use(logger()); app.use(cors()); app.get('/', async () => json({ message: 'Hello from Futon!' })); app.get('/users/:id', async (ctx) => { const { id } = ctx.params; return json({ id }); }); app.notFound(async () => json({ error: 'Not Found' }, { status: 404 })); app.onError(async (error, ctx) => { console.error(error); return json({ error: error.message }, { status: 500 }); });
2. Serve It with the Node Adapter
Futon doesn't bind a port itself — that's the runtime adapter's job. For Node:
import { NodeDevAdapter } from '@rasenganjs/runtime/adapters/node'; import { app } from './app.js'; const adapter = new NodeDevAdapter({ port: 3000 }); await adapter.serve(app);
Run it with tsx or plain Node (with a build step):
npx tsx index.ts # Server listening on http://localhost:3000
3. Try It Out
curl http://localhost:3000/ # {"message":"Hello from Futon!"} curl http://localhost:3000/users/42 # {"id":"42"}
What Just Happened
app.use(logger())andapp.use(cors())registered global middleware, run for every request in the order they were added.app.get("/users/:id", ...)registered a route —:idis a named path parameter, available onctx.params.app.notFound()andapp.onError()gave Futon fallback handlers for unmatched routes and uncaught exceptions.NodeDevAdaptertook theFutoninstance and turned it into an actual listening HTTP server.
No server required for testing
You can skip the adapter entirely and call app.fetch(request) directly —
this is how Futon's own test suite exercises routes and middleware without
opening a real socket.
Next Steps
- Learn how routing and route patterns work.
- Explore the built-in middleware (CORS, compression, auth, and more).
- See the full list of runtime adapters for Bun and Cloudflare Workers.
Installation
Router
