Initial Commit
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# `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 the `Response` to the Hono
|
||||
context with `content-type: application/problem+json`.
|
||||
- `renderProblem(err)` — pure render returning a `ProblemDetails`
|
||||
object (used by tests + alt transports).
|
||||
- `PROBLEM_JSON_CONTENT_TYPE` constant for downstream re-use.
|
||||
- Native `Error` paths preserve the `name` so debugging upstream
|
||||
failures stays readable.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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): Response`
|
||||
- `renderProblem(err: unknown): ProblemDetails`
|
||||
- `interface 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 `type` field is built as `urn: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.name` fallback (`"InternalError"`) protects against
|
||||
thrown objects whose `name` was wiped by a downstream serializer.
|
||||
- `renderProblem` returns a `ProblemDetails` literal so consumers that
|
||||
need to merge additional fields (e.g. `instance`) can spread it.
|
||||
Reference in New Issue
Block a user