Files
arena-plm-fiori/worker/src/http/problem.ts
T
2026-09-16 01:47:28 -07:00

38 lines
1.3 KiB
TypeScript

/**
* RFC 9457 Problem Details for HTTP APIs — the governed contract
* (docs/api/worker.openapi.json) specifies `application/problem+json` for all
* error responses (400/401/404/502/503).
*
* We omit the `type` member: per RFC 9457 §3.1.1 an absent `type` is
* equivalent to "about:blank", in which case `title` mirrors the HTTP status
* phrase — which is exactly what we send.
*/
import type { FastifyReply } from 'fastify';
export const PROBLEM_CONTENT_TYPE = 'application/problem+json';
export interface ProblemDetails {
title: string;
status: number;
detail?: string;
}
export function sendProblem(
reply: FastifyReply,
status: number,
title: string,
detail?: string,
/** Extra response headers (e.g. `Retry-After` on 503). */
headers: Record<string, string> = {},
): FastifyReply {
const body: ProblemDetails = detail === undefined ? { title, status } : { title, status, detail };
// Serialize explicitly: Fastify's default JSON serializer targets
// application/json, so send a pre-serialized string under the problem type.
reply.code(status).header('content-type', `${PROBLEM_CONTENT_TYPE}; charset=utf-8`);
for (const [name, value] of Object.entries(headers)) {
reply.header(name, value);
}
return reply.send(JSON.stringify(body));
}