Files
skills/docs/api-stack/api/api-router-hono/src/adapter/adapter.md
2026-05-29 08:20:56 +01:00

3.1 KiB

adapter — Hono-backed Router adapter

(no submodules)

Purpose

HonoRouter is The concrete Router adapter for the API plugin system. It implements every public method of the abstract Router contract from @suaveplan/api-router (all 7 verb methods, all, use, mount, match, collidesWith, chain) and mirrors every route registration onto a private Hono Hono instance. The abstract base owns the match table and collision detection; the Hono instance owns the wire-side dispatch when the host wires a real listener via getHonoApp().

The adapter exists so a plugin's RouteSpec[] can serve traffic through Hono's fast trie router while still being introspectable by the host's collision detector, blue-green slot manager, and OpenAPI emitter — all of which speak the abstract Router surface.

Features

  • Subclasses Router, inheriting match/collidesWith/mount verbatim.
  • Mirrors registrations onto a private Hono app with a 501 stub handler so the abstract pipeline owns dispatch.
  • getHonoApp() exposes the underlying Hono instance for ops integrations and direct middleware installation.
  • createHonoRouter() factory returns the adapter typed as the abstract Router so the runRouterConformance harness accepts it unchanged.
  • Wraps Hono-internal registration throws in RouterHonoError (ROUTER_HONO.HONO_INTERNAL_ERROR).

Usage: Basic

import { createHonoRouter } from "@suaveplan/api-router-hono/adapter";

const router = createHonoRouter({ plugin: "billing" });
router.get("/invoices/:id", () => null);
const match = router.match("GET", "/invoices/inv_42");

Usage: Advanced

import { HonoRouter } from "@suaveplan/api-router-hono/adapter";
import { Hono } from "hono";

const router = new HonoRouter({ plugin: "tenants" });
router.post("/tenants", { auth: { required: true } }, () => null);
router.mount(
    "/admin",
    (() => {
        const child = new HonoRouter({ plugin: "admin" });
        child.get("/healthz", () => null);
        return child;
    })()
);

const app: Hono = router.getHonoApp();
app.use("*", async (c, next) => {
    c.header("x-app", "suaveplan");
    await next();
});

API Reference

  • class HonoRouter extends Router — public constructor new HonoRouter({ plugin? }).
  • router.getHonoApp(): Hono — accessor.
  • createHonoRouter(options?): Router — factory returning the conformance-friendly Router view.
  • Throws RouterHonoError with ROUTER_HONO.HONO_INTERNAL_ERROR on a Hono-side registration failure.

Implementation Notes

mirror() normalises trailing slashes and dispatches to the appropriate Hono verb method. HEAD is registered via Hono.on("HEAD", path, ...) because the runtime Hono surface omits a dedicated .head() shorthand. The mirror stub returns a 501 because in-process dispatch goes through the abstract Router.match() path — the Hono app is wired only when the host attaches a fetch adapter. The match table from the abstract base is the single source of truth for the per-request pipeline (design doc §6).