@owly/runtime (TypeScript / JavaScript)
A Hono-style router and response helpers. Handlers go from a
boilerplate addEventListener('fetch', …) listener to one-line route
registrations.
import { createApp } from '@owly/runtime';
const app = createApp();
app.get('/api/leaderboard/:scope', (c) => {
const scope = c.param('scope');
const page = Number(c.query('page') ?? '1');
return c.json({ scope, page });
});
app.post('/api/echo', async (c) => {
const body = await c.json<{ msg: string }>();
return c.json({ echoed: body.msg });
});
app.listen();
createApp()
Returns an App. Register routes, then call listen() once at the end to wire
up the runtime's fetch event.
const app = createApp();
Routing
app.get(path, handler);
app.post(path, handler);
app.put(path, handler);
app.delete(path, handler);
app.patch(path, handler);
app.all(path, handler); // any method
- Patterns use
:namefor path params:/users/:id/posts/:postId. - Matching is a linear scan; first match wins.
- A path that matches but with the wrong method returns 405; no match returns 404.
- Handlers see the full request path, including the prefix the function is mounted at in
owly.yaml. A function mounted at/api/pageregistersapp.get('/api/page', …).
app.listen();
listen() registers the handler with the runtime. Call it last.
The Context (c)
Every handler receives a Context:
interface Context {
readonly req: Request;
readonly url: URL;
readonly params: Record<string, string>;
param(name: string): string | undefined; // a :path param
query(name: string): string | undefined; // first query value
queries(): URLSearchParams; // all query params
header(name: string): string | undefined; // a request header
// Body parsers (lazy)
json<T = unknown>(): Promise<T>;
text(): Promise<string>;
// Response builders
body(body: BodyInit | null, init?: ResponseInit): Response;
json(data: unknown, init?: ResponseInit): Response;
text(body: string, init?: ResponseInit): Response;
html(body: string, init?: ResponseInit): Response;
notFound(): Response;
}
The json and text names are overloaded by arity: called with no arguments
they parse the request body; called with data they build a response.
app.post('/save', async (c) => {
const data = await c.json<{ name: string }>(); // parse
return c.json({ saved: data.name }, { status: 201 }); // build
});
Response builders set a sensible Content-Type without clobbering one you
supply explicitly.
Build
owly build type-checks and bundles each handler — including npm dependencies
like @owly/runtime — and compiles it for deployment. See
Build & deploy.