@yeliex/fastify-problem-details
v1.4.3
Published
Problem Details for HTTP APIs (RFC 9457) implementation for fastify
Maintainers
Readme
@yeliex/fastify-problem-details
Fastify integration for RFC 9457 Problem Details.
This package re-exports everything from @yeliex/problem-details, and adds Fastify plugin utilities.
Install
pnpm add @yeliex/fastify-problem-details fastifyQuick Start
import Fastify from 'fastify';
import { fastifyProblemDetails } from '@yeliex/fastify-problem-details';
const app = Fastify();
await app.register(fastifyProblemDetails);
app.get('/users/:id', async (_request, reply) => {
return reply.problem(404, 'User not found', { code: 'USER_NOT_FOUND' });
});
await app.listen({ port: 3000 });Plugin Features
After register(fastifyProblemDetails):
app.httpErrorsrequest.acceptsProblemJsonreply.problem(...)- default global
setErrorHandler - default global
setNotFoundHandler
reply.problem(...)
Overloads:
reply.problem(problem: ProblemDetail)
reply.problem(status: number, init?: ProblemDetailInit)
reply.problem(status: number, detail?: string, init?: ProblemDetailInit)Behavior:
- returns
application/problem+jsonwhenAcceptprefers it - otherwise returns
application/json - response status always equals
problem.status - in
fastifyErrorHandler, ifreply.statusCodeis already>= 400, it takes precedence andproblem.statusis synchronized to it - when the thrown error is already a
ProblemDetail, itsstatusandtitleare preserved
Options (reply.problem(..., options) or plugin register options):
responseStack?: booleanincludestackin response payload whentrueresponseFilter?: (input: unknown) => unknownpost-process outgoing problem payload beforesend
Example:
import { httpErrors } from '@yeliex/fastify-problem-details';
const PROBLEM_PRIVATE = Symbol.for('private');
await app.register(fastifyProblemDetails, {
responseFilter: (input) => {
const {
[PROBLEM_PRIVATE]: privateData,
...rest
} = input as Record<string | symbol, unknown>;
// keep internal context for logs/tracing, but never expose it directly
return {
...rest,
traceId: (privateData as { traceId?: string } | undefined)?.traceId,
};
},
});
app.get('/users/:id', async (_request, reply) => {
const error = new httpErrors.BadRequest('something went wrong', {
[PROBLEM_PRIVATE]: { traceId: 'req-123' },
});
return reply.problem(error);
});httpErrors
Built-in typed HTTP error constructors (re-exported from @yeliex/problem-details/http-error).
All access patterns are supported:
import { httpErrors } from '@yeliex/fastify-problem-details';
new httpErrors.NotFound('missing');
new httpErrors[404]('missing');
new httpErrors['404']('missing');Each constructor extends ProblemDetail and supports the same options/extensions.
toProblemDetail(error) in Fastify package
@yeliex/fastify-problem-details exports its own toProblemDetail that is tuned for runtime HTTP handling:
Errorwith emptymessagefalls back to status phrase (for example404 -> Not Found)- preserves
statusCode,cause,stack, and extra fields
If you are not using Fastify, create a conversion function in your own application
that maps your runtime errors to new ProblemDetail(...).
License
MIT
