HTTP Problem Details (RFC 7807)
raw JSON →This library provides an implementation of HTTP Problem Details (RFC 7807) for Node.js applications, enabling standardized machine-readable error responses for HTTP APIs. As of version 0.1.7, it offers classes like `ProblemDocument` and `ProblemDocumentExtension` to construct detailed error objects that conform to the specification, including standard fields like `type`, `title`, `status`, `detail`, and `instance`, as well as support for extension members. The package is built with TypeScript, providing strong typing out of the box, and explicitly validates `type` and `instance` fields as valid URIs, throwing errors on invalid input. While the project is in its early stages of development (pre-1.0.0), it encourages contributions and aims for strict RFC compliance, differentiating itself by offering a focused, unopinionated foundation for problem detail generation.
Common errors
error Error: Invalid URI provided for 'type' ↓
type property is a well-formed URI, such as 'https://example.com/errors/bad-request' or 'about:blank'. error Error: Invalid URI provided for 'instance' ↓
instance, typically a path or URL pointing to the specific occurrence of the problem, e.g., '/orders/12345/items/failed'. error TypeError: ProblemDocument is not a constructor ↓
const { ProblemDocument } = require('http-problem-details');. For ESM, use import { ProblemDocument } from 'http-problem-details';. Warnings
gotcha The `type` and `instance` properties of `ProblemDocument` require valid URI references. Providing non-URI strings will result in an error being thrown during object construction. ↓
breaking As the library is pre-1.0.0 (currently 0.1.7), the API is not yet considered stable. Future minor or patch releases may introduce breaking changes without a major version increment, aligning with common pre-1.0 development practices. ↓
Install
npm install http-problem-details yarn add http-problem-details pnpm add http-problem-details Imports
- ProblemDocument wrong
const ProblemDocument = require('http-problem-details').ProblemDocument;correctimport { ProblemDocument } from 'http-problem-details'; - ProblemDocumentExtension wrong
const ProblemDocumentExtension = require('http-problem-details').ProblemDocumentExtension;correctimport { ProblemDocumentExtension } from 'http-problem-details'; - ProblemDocument and ProblemDocumentExtension (CommonJS) wrong
const ProblemDocument = require('http-problem-details'); // Incorrect destructuringcorrectconst { ProblemDocument, ProblemDocumentExtension } = require('http-problem-details');
Quickstart
import { ProblemDocument, ProblemDocumentExtension } from 'http-problem-details';
import { Request, Response } from 'express';
// Example of an Express error handler generating a Problem Document
export const errorHandler = (err: any, req: Request, res: Response, next: Function) => {
console.error(err);
let problemDocument: ProblemDocument;
// Handle a specific custom error, e.g., 'InsufficientFundsError'
if (err.name === 'InsufficientFundsError') {
const extension = new ProblemDocumentExtension({
balance: err.currentBalance, // assuming err has currentBalance property
required: err.amountRequired,
transactions: ['/account/123/tx/456', '/account/123/tx/789']
});
problemDocument = new ProblemDocument({
type: 'https://example.com/probs/out-of-credit',
title: 'You do not have enough credit.',
detail: err.message || 'Your current balance is insufficient for this operation.',
status: 403, // Forbidden
instance: req.originalUrl // Reflects the specific request
}, extension);
} else if (err.status) {
// Handle errors that already have an HTTP status
problemDocument = new ProblemDocument({
type: 'about:blank',
title: err.message || 'An unexpected error occurred.',
status: err.status,
detail: err.detail,
instance: req.originalUrl
});
} else {
// Default catch-all for unknown errors
problemDocument = new ProblemDocument({
type: 'about:blank',
title: 'Internal Server Error',
status: 500,
detail: 'An unexpected server error occurred.',
instance: req.originalUrl
});
}
res.status(problemDocument.status || 500).json(problemDocument);
};
// To demonstrate usage:
// class InsufficientFundsError extends Error {
// constructor(message: string, public currentBalance: number, public amountRequired: number) {
// super(message);
// this.name = 'InsufficientFundsError';
// }
// }
//
// const mockReq: Request = { originalUrl: '/api/checkout/item/123' } as Request;
// const mockRes: Response = { status: jest.fn().mockReturnThis(), json: jest.fn() } as unknown as Response;
// const mockNext = jest.fn();
//
// const error = new InsufficientFundsError('Not enough funds to purchase item.', 30, 50);
// errorHandler(error, mockReq, mockRes, mockNext);
//
// console.log(mockRes.json.mock.calls[0][0]);