2.6 KiB
2.6 KiB
routes/problem-json — RFC 7807 problem+json helper
Purpose
Bridges thrown errors from the route handlers into RFC 7807
application/problem+json responses. Reads the HTTP status from the
error's context.statusCode when present so the canonical
ImportError raised by the service / plugin layer surfaces with the
correct HTTP code without a code → status lookup table in the router.
Unknown throwables fall back to a generic 500 so the router never
crashes — the renderer is the safety net for non-SuaveplanError
exceptions that escape upstream code.
Features
respondProblemJson(c, err)— writes theResponseto the Hono context withcontent-type: application/problem+json.renderProblem(err)— pure render returning aProblemDetailsobject (used by tests + alt transports).PROBLEM_JSON_CONTENT_TYPEconstant for downstream re-use.- Native
Errorpaths preserve thenameso debugging upstream failures stays readable.
Usage: Basic Example
import { respondProblemJson } from "@suaveplan/api-plugin-import";
app.get("/foo", (c) => {
try {
throw new ImportError(IMPORT_CODES.NOT_FOUND, "missing", {
context: { statusCode: 404 },
});
} catch (err) {
return respondProblemJson(c, err);
}
});
Usage: Advanced Example
import { renderProblem } from "@suaveplan/api-plugin-import";
// Pure render for use in alternate transports (gRPC adapter, queue worker)
const body = renderProblem(new Error("boom"));
// body === { type: "urn:suaveplan:error:internal", title: "Error", status: 500, detail: "boom" }
const message = JSON.stringify(body);
publishToDeadLetterQueue(message);
API Reference
respondProblemJson(c: HonoLike, err: unknown): ResponserenderProblem(err: unknown): ProblemDetailsinterface HonoLike { header(name, value); body(data, status): Response }interface ProblemDetails { type; title; status; detail; code?; [k: string]: unknown }const PROBLEM_JSON_CONTENT_TYPE = "application/problem+json"
Implementation Notes
- The
typefield is built asurn:suaveplan:error:<lowercased-code>so consumers can branch on the URN without parsing the body. - Status hints below 400 (or missing) collapse to 500 — clients should never receive a problem+json body with a 2xx status code.
- The
Error.namefallback ("InternalError") protects against thrown objects whosenamewas wiped by a downstream serializer. renderProblemreturns aProblemDetailsliteral so consumers that need to merge additional fields (e.g.instance) can spread it.