HTTP Problem Details (RFC 7807)

raw JSON →
0.1.7 verified Thu Apr 23 auth: no javascript

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.

error Error: Invalid URI provided for 'type'
cause The `type` property passed to the ProblemDocument constructor was not a valid URI string.
fix
Ensure the 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'
cause The `instance` property passed to the ProblemDocument constructor was not a valid URI string.
fix
Provide a valid URI for 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
cause Attempting to use `require('http-problem-details')` directly as a constructor, or not correctly destructuring named exports in CommonJS.
fix
For CommonJS, use const { ProblemDocument } = require('http-problem-details');. For ESM, use import { ProblemDocument } from 'http-problem-details';.
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.
fix Ensure `type` and `instance` are valid URI strings (e.g., 'https://example.com/probs/my-error' or '/api/errors/123'). You can use a URI validation library if inputs are user-generated.
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.
fix Pin exact versions (e.g., `"http-problem-details": "0.1.7"`) in `package.json` to prevent unexpected breaking changes. Review release notes carefully when upgrading to new pre-1.0 versions.
npm install http-problem-details
yarn add http-problem-details
pnpm add http-problem-details

This quickstart demonstrates how to integrate `http-problem-details` within an Express.js error handling middleware to generate RFC 7807 compliant error responses, including custom extension members.

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]);