Skip to content

wooter

JSR JSR Score

[!WARNING] wooter is beta and WIP. Core ideas are implemented, but rough edges still exist. Avoid high-impact production usage until v100.0.0.

[!NOTE] wooter uses epoch semver.

wooter is a TypeScript router with a different mental model than Express-like routers.

  • Promise-separated response lifecycle: your handler execution and the request response are tracked separately.
  • Fetch-native runtime model: use router.fetch(request) anywhere the Fetch API exists.
  • Structural routing with chemin: routes are patterns, not path strings.
  • Cooperative middleware pipeline: middleware composition is explicit and type-safe.
import { c, Wooter } from "@bronti/wooter"
const router = new Wooter()
router.route(c.chemin(), "GET", ({ resp }) => {
resp(new Response("hi"))
})
router.route(c.chemin("user", c.pNumber("id")), "GET", ({ params, resp }) => {
resp.json({ id: params.get("id") })
})
router.route(c.chemin("after"), "GET", async ({ resp }) => {
resp(new Response("sent"))
await Promise.resolve()
console.log("this runs after responding")
})
export default router
  1. Structural routing with chemin: Route definitions are composed from typed path pieces. Matching and parameter typing come from the pattern itself.

  2. Execution and response are separate: A handler must resolve exactly one response with ctx.resp() before completion. You can still perform work after response resolution.

  3. Middleware must be cooperative: Middleware should call .next() or .forward() unless it resolves a response itself. It should not swallow errors it does not handle.

  4. safeExit() is a control-flow signal: Use ctx.safeExit() only after ctx.resp() when you intentionally want to stop execution immediately.

Errors before response resolution reject router.fetch() and belong to the request lifecycle.

Errors after response resolution cannot be attached to the request anymore and are routed to the router catchStrayErrors sink.

By default, stray errors are rethrown, which may crash your process if unhandled.

  • Do not drop the chain: call .next() or .forward(), or respond explicitly.
  • Do not hoard errors: if you catch an error and do not fully handle it, rethrow it.
  • Prefer ctx.safeExit() over throwing ControlFlowBreak directly in middleware libraries.