@neutrino-io/hono-middleware
Shared Hono.js middleware for Neutrino Cloudflare Worker services.
Package: @neutrino-io/hono-middleware
Three small Hono utilities every Neutrino Cloudflare Worker service uses to keep request handling consistent — request-id propagation, NNO-shaped 404 envelopes, and NNO-shaped error envelopes.
import { requestId, notFound, onError } from "@neutrino-io/hono-middleware";All three helpers expect a Hono environment with a requestId context variable:
type Env = { Variables: { requestId: string } };The requestId() middleware sets that variable; notFound() and onError() read it to thread the same id into error envelopes. Wire requestId() first, then the rest:
import { Hono } from "hono";
import { requestId, notFound, onError } from "@neutrino-io/hono-middleware";
const app = new Hono<Env>();
app.use("*", requestId());
app.notFound(notFound());
app.onError(onError());requestId()
Hono middleware that ensures every request has a unique id available on the context and echoes it back to the client.
function requestId<
E extends { Variables: { requestId: string } },
>(): MiddlewareHandler<E>;On each request the middleware:
- Reads the
X-Request-Idheader. If missing, generates a UUID v4 viacrypto.randomUUID(). - Stores the id on the Hono context (
c.set("requestId", id)). - Awaits
next(). - Echoes the id back as the
X-Request-Idresponse header.
Downstream handlers read the id via c.get("requestId").
app.use("*", requestId());
app.get("/", (c) => {
const id = c.get("requestId");
return c.json({ ok: true, requestId: id });
});notFound()
Hono NotFoundHandler that emits the canonical NNO error envelope for unmatched routes.
function notFound<
E extends { Variables: { requestId: string } },
>(): NotFoundHandler<E>;Returns HTTP 404 with body:
{
"error": {
"code": "NOT_FOUND",
"message": "Not Found",
"details": {},
"requestId": "<requestId from context>"
}
}app.notFound(notFound());onError()
Hono ErrorHandler that normalises every unhandled error into the NNO envelope.
function onError<
E extends { Variables: { requestId: string } },
>(): ErrorHandler<E>;- For
HTTPException(Hono-thrown), returns the exception's status withcode: "HTTP_ERROR"and the exception's message. - For any other error, logs it via
console.error("[onError]", err)and returns 500 withcode: "INTERNAL_ERROR"and a generic message. - The
requestIdis read from the context.
app.onError(onError());HTTPException output (e.g. a 401 thrown via throw new HTTPException(401, { message: "Invalid token" })):
{
"error": {
"code": "HTTP_ERROR",
"message": "Invalid token",
"requestId": "<requestId>"
}
}Unhandled error output:
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred",
"requestId": "<requestId>"
}
}NNO error envelope
All three helpers (and every NNO service following the convention) emit the same shape:
{
error: {
code: string // machine-readable code, e.g. "NOT_FOUND", "HTTP_ERROR", "INTERNAL_ERROR"
message: string // human-readable description
requestId: string // id of the failing request, threaded from context
details?: unknown // optional structured details (e.g. validation field errors)
}
}Downstream services and clients can rely on the envelope being present whenever the response is a 4xx/5xx originating from a NNO worker that wires notFound() + onError().