npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nest-problem-details-filter

v1.8.0

Published

A NestJS exception filter to convert JSON responses to RFC 9457 (formerly RFC 7807)-compliant Problem Details for HTTP APIs. Standardizes HTTP error responses and sets Content-Type to application/problem+json.

Readme

NestHttpProblemDetails (RFC 9457 / RFC 7807)

npm version install size License: MIT Main pipeline CodeRabbit Pull Request Reviews Coverage Status

Make NestJS return RFC 9457 (formerly RFC 7807)-compliant Problem Details for HTTP APIs. Drop in one filter — no code changes — and every error in your app starts speaking application/problem+json.

Keywords: RFC 9457, RFC 7807, Problem Details, HTTP API errors, NestJS, application/problem+json.

Quick start

npm i nest-problem-details-filter
// main.ts
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { HttpExceptionFilter } from 'nest-problem-details-filter';
import { AppModule } from './app/app.module';

const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter(app.get(HttpAdapterHost)));

Every HttpException your app throws now serializes to application/problem+json:

- HTTP/1.1 404 Not Found
- Content-Type: application/json
- { "statusCode": 404, "message": "Dragon not found" }
+ HTTP/1.1 404 Not Found
+ Content-Type: application/problem+json
+ { "type": "not-found", "title": "Dragon not found", "status": 404 }

See Usage for module setup, Retry-After, validation errors, and Swagger.

Features

  • RFC 9457 / RFC 7807 Compliant - Standardized Problem Details for HTTP APIs
  • Retry-After header support - Per RFC 9110 §10.2.3, opt-in via ProblemDetailsException or any HttpException subclass exposing retryAfter
  • Swagger / OpenAPI decorator (optional) - @ApiProblemResponse() via nest-problem-details-filter/swagger subpath auto-documents application/problem+json without forcing @nestjs/swagger on users who don't need it
  • Docs / runtime alignment - Shared resolvers guarantee OpenAPI examples match the wire format (status-to-type map, title fallbacks, base-URI resolution)
  • Flexible validation error handling - Three approaches from zero-config to full RFC 9457 JSON Pointer compliance (see Validation errors)
  • Zero runtime dependencies - Core filter has no runtime dependencies

Table of contents:

Usage

Install the library with:

# npm
npm i nest-problem-details-filter

# or, pnpm
pnpm i nest-problem-details-filter

Then check NestJS documentation on how to bind exception filters.

As a global filter

In main.ts add app.useGlobalFilters(new HttpExceptionFilter(app.get(HttpAdapterHost))) as the following

import { NestFactory, HttpAdapterHost } from '@nestjs/core';
import { HttpExceptionFilter } from 'nest-problem-details-filter';
import { AppModule } from './app/app.module';

async function bootstrap() {
  ...

  const app = await NestFactory.create(AppModule);

  app.useGlobalFilters(new HttpExceptionFilter(app.get(HttpAdapterHost)));

  ...
}

Note that the app.get(HttpAdapterHost) argument is needed because the HttpExceptionFilter works for any kind of NestJS HTTP adapter!

HttpExceptionFilter accepts a base URI for if you want to return absolute URIs for your problem types, e.g:

app.useGlobalFilters(new HttpExceptionFilter(app.get(HttpAdapterHost), 'https://example.org'));

Will return:

{
  "type": "https://example.org/not-found",
  "title": "Dragon not found",
  "status": 404,
  "detail": "Could not find any dragon with ID: 99"
}

Suppressing detail in production

Recommended for production deployments. The detail field can expose internal error messages to clients. Use the suppressDetail option to omit it — either always, or based on custom logic.

When you use Nest's built-in exceptions (e.g. NotFoundException, BadRequestException) or throw a plain HttpException without a custom ProblemDetailsException, the filter maps the exception's built-in message directly into detail. This means stack traces, database error strings, or other sensitive messages can leak to the client without any extra effort on your part.

For example, throwing new InternalServerErrorException('Database connection timeout') will produce:

{
  "type": "internal-server-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "Database connection timeout"
}

The suppressDetail option accepts either a boolean or a callback:

import { HttpExceptionFilter, SuppressDetail } from 'nest-problem-details-filter';

// Always suppress detail on every response:
app.useGlobalFilters(new HttpExceptionFilter(app.get(HttpAdapterHost), '', undefined, true));

// Or suppress selectively with a callback:
app.useGlobalFilters(new HttpExceptionFilter(app.get(HttpAdapterHost), '', undefined, ({ status }) => status >= 500));

With the callback above, detail is stripped from all 5xx responses while remaining visible on 4xx responses (where it is typically safe and useful, e.g. validation messages). See docs/usage.md for the full API including the SUPPRESS_DETAIL_KEY DI token for module usage.

As a module

The library ships as a dynamic module. Use register() (or registerAsync()) to configure it, and HTTP_EXCEPTION_FILTER_KEY to bind it to APP_FILTER:

import { APP_FILTER } from '@nestjs/core';
import { NestProblemDetailsModule, HTTP_EXCEPTION_FILTER_KEY } from 'nest-problem-details-filter';

@Module({
  imports: [
    NestProblemDetailsModule.register({
      baseUri: 'https://api.example.org/problems',
      httpErrorsMap: { 418: 'teapot-error' },
      suppressDetail: ({ status }) => status >= 500,
    }),
  ],
  providers: [
    {
      provide: APP_FILTER,
      useExisting: HTTP_EXCEPTION_FILTER_KEY,
    },
  ],
})
export class AppModule {}

For config-driven setups, registerAsync() resolves options from a factory:

NestProblemDetailsModule.registerAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    baseUri: config.get('PROBLEMS_BASE_URI'),
  }),
});

Importing NestProblemDetailsModule directly (without calling register()) still works and uses sensible defaults. The legacy pattern of overriding BASE_PROBLEMS_URI_KEY, HTTP_ERRORS_MAP_KEY and SUPPRESS_DETAIL_KEY providers manually is also still supported.

See:

Throwing exceptions

To produce a Problem Details response, throw either:

  • ProblemDetailsException — accepts a flat RFC 9457 payload directly (recommended for new code), or
  • a native HttpException (NotFoundException, ForbiddenException, custom subclasses, ...) — the filter recognizes the standard Nest payload shape.
import { ProblemDetailsException } from 'nest-problem-details-filter';

throw new ProblemDetailsException({
  type: 'out-of-credit',
  title: 'You do not have enough credit.',
  status: 403,
  detail: 'Your balance is 30, but that costs 50.',
  balance: 30,
});

type is optional; when omitted, the filter resolves it from its status-to-type map (or falls back to about:blank, per RFC 9457 §4.2.1).

Retry-After header

Pass retryAfter as a number (delta-seconds), Date (absolute), or pre-formatted string on any retriable error response. The filter sets the Retry-After header per RFC 9110 §10.2.3 and strips the value from the JSON body. Common cases are 429 Too Many Requests (rate limiting) and 503 Service Unavailable (maintenance / backpressure), but the library imposes no status restriction.

throw new ProblemDetailsException({
  type: 'rate-limit-exceeded',
  title: 'Too Many Requests',
  status: 429,
  detail: 'Quota exceeded.',
  retryAfter: 3600, // → "Retry-After: 3600"
});

The filter reads retryAfter from any HttpException instance (duck-typed), so you can extend Nest's built-in exceptions instead:

import { ServiceUnavailableException } from '@nestjs/common';
import { RetryAfterValue } from 'nest-problem-details-filter';

class MaintenanceException extends ServiceUnavailableException {
  constructor(public readonly retryAfter: RetryAfterValue) {
    super('Maintenance window in progress.');
  }
}

throw new MaintenanceException(300); // → "Retry-After: 300"

See docs/usage.md for the full set of examples (including the native HttpException form and Retry-After details).

Swagger / OpenAPI

If you use @nestjs/swagger, import @ApiProblemResponse from the nest-problem-details-filter/swagger subpath to document application/problem+json responses:

import { ApiProblemResponse } from 'nest-problem-details-filter/swagger';

@Controller('dragons')
export class DragonsController {
  @Get(':id')
  @ApiProblemResponse({ status: 404, type: 'not-found', title: 'Dragon not found' })
  @ApiProblemResponse({ status: 429, type: 'rate-limit-exceeded', retryAfter: 3600 })
  findOne(@Param('id') id: string) { ... }
}

The decorator is stackable: apply once per status code. It auto-generates:

  • The canonical ProblemDetails schema under content['application/problem+json']
  • A response example with type, title, and status
  • The Retry-After header schema when retryAfter is provided

If your filter is configured with a BASE_PROBLEMS_URI, pass the same value as baseUri so the OpenAPI docs match the runtime wire format:

@ApiProblemResponse({ status: 404, type: 'not-found', baseUri: 'https://api.example.com/problems' })
// OpenAPI example type → "https://api.example.com/problems/not-found"

Likewise, if you override the default status-to-type map via HTTP_ERRORS_MAP_KEY, pass the same map as httpErrors:

@ApiProblemResponse({ status: 404, httpErrors: { 404: 'missing-resource' } })
// OpenAPI example type → "missing-resource" (not the built-in default)

By default the decorator inlines the schema in every response so it works out of the box. If you want a named ProblemDetails entry in Swagger UI's Schemas section, call addProblemDetailsSchema() after creating the document:

import { addProblemDetailsSchema } from 'nest-problem-details-filter/swagger';

const document = SwaggerModule.createDocument(app, builder);
addProblemDetailsSchema(document);

See docs/usage.md for the full decorator API (custom schemas, explicit examples, headers, etc.).

Preview: copy tests/fixtures/swagger-document.json and paste it into editor.swagger.io to see how the decorator renders in Swagger UI.

Example response

# curl -i http://localhost:3333/api/dragons/99?title=true&details=true

HTTP/1.1 404 Not Found
Content-Type: application/problem+json; charset=utf-8
Content-Length: 109
...

{
  "type": "not-found",
  "title": "Dragon not found",
  "status": 404,
  "detail": "Could not find any dragon with ID: 99"
}

OpenAPI schema

Full JSON Schema and OpenAPI 3.0 definitions are available in docs/openapi.md.

components:
  schemas:
    ProblemDetails:
      type: object
      description: >
        Problem Details object as defined by RFC 9457 (formerly RFC 7807).
        Returned with media type `application/problem+json`.
      required:
        - type
        - title
        - status
      properties:
        type:
          type: string
          format: uri-reference
          maxLength: 1024
          default: 'about:blank'
          description: >
            A URI reference that identifies the problem type. Per RFC 9457
            this is a URI-reference (may be relative). Defaults to
            "about:blank" when not provided.
          example: 'about:blank'
        title:
          type: string
          maxLength: 1024
          description: >
            A short, human-readable summary of the problem type. It should
            not change from occurrence to occurrence of the problem, except
            for purposes of localization.
          example: 'Not Found'
        status:
          type: integer
          format: int32
          minimum: 100
          maximum: 599
          description: >
            The HTTP status code generated by the origin server for this
            occurrence of the problem.
          example: 404
        detail:
          type: string
          maxLength: 4096
          description: >
            A human-readable explanation specific to this occurrence of the
            problem.
          example: 'Could not find any dragon with ID: 99'
        instance:
          type: string
          format: uri-reference
          maxLength: 1024
          description: >
            A URI reference that identifies the specific occurrence of the
            problem.
          example: '/dragons/99'
      additionalProperties: true

Documentation

Check the docs/ folder for usage examples and the OpenAPI schema.

Validation errors

The filter ships three flexible approaches for surfacing class-validator validation errors — all using the errors RFC 9457 extension member (not detail, per §3.1.4).

Peer dependency: the helpers below require class-validator (already a NestJS validation standard). Install it alongside the filter:

npm install class-validator

Approach 1 — Zero config

Just register ValidationPipe + HttpExceptionFilter. The filter detects the string[] message Nest emits and moves it to errors automatically (why not using details as array?):

{
  "type": "bad-request",
  "title": "Bad Request",
  "status": 400,
  "detail": "Bad Request",
  "errors": ["username must be longer than or equal to 3 characters", "email must be an email"]
}

Approach 2 — Field-map via BadRequestException

Use mapClassValidatorErrors() in exceptionFactory for per-field grouping with dotted-path nesting:

import { mapClassValidatorErrors } from 'nest-problem-details-filter/class-validator-mappers';

new ValidationPipe({
  exceptionFactory: (e) => new BadRequestException({ message: 'Validation failed', errors: mapClassValidatorErrors(e) }),
});
{
  "type": "bad-request",
  "title": "Validation failed",
  "status": 400,
  "errors": {
    "email": ["must be an email"],
    "address.street": ["should not be empty"]
  }
}

Approach 3 — ProblemDetailsException (field-map or RFC pointer array)

Use toValidationProblemDetails() for a one-liner that returns a ProblemDetailsException directly:

import { toValidationProblemDetails } from 'nest-problem-details-filter/class-validator-mappers';

// Field-map (default)
new ValidationPipe({ exceptionFactory: (e) => toValidationProblemDetails(e) });

// RFC 9457 JSON Pointer array
new ValidationPipe({ exceptionFactory: (e) => toValidationProblemDetails(e, { usePointers: true }) });

Pointer format output:

{
  "type": "validation-error",
  "title": "Validation Failed",
  "status": 400,
  "errors": [
    { "detail": "must be an email", "pointer": "#/email" },
    { "detail": "should not be empty", "pointer": "#/address/street" }
  ]
}

See docs/usage.md for the full walkthrough including nested objects, custom validators, and all options.

Development

Tests

The library includes reusable integration tests that run against real NestJS applications backed by Express and Fastify to verify that the filter works correctly with each HTTP adapter.

# Run all tests (unit + integration)
npm test

# Watch mode
npm run test:watch

# With coverage
npm run test:cov

Mock app

A local NestJS server is available for manual testing and exploring the Swagger / OpenAPI output. It reuses the same controllers as the integration tests, decorated with @ApiProblemResponse so the generated spec includes application/problem+json response examples.

# Start the mock app (reuses the same controllers as the integration tests)
npm run start:mock
  • API endpoints: http://localhost:3000/api/test/...
  • Swagger UI: http://localhost:3000/api

The mock app automatically restarts when you edit tests/mock-main.ts or tests/test-app.module.ts.

Lint & format

npm run lint        # ESLint check (no auto-fix; fails on issues)
npm run lint:fix    # ESLint with --fix
npm run format      # Prettier (src/**/*.ts)

Build

npm run build       # Compiles src/ → dist/ via nest build

Resources

Contributing

We welcome contributions! Please see our CONTRIBUTING.md for guidelines on how to contribute to this project.

Security

For security-related issues, please review our SECURITY.md for responsible disclosure guidelines.

License

This project is licensed under the MIT License - see the LICENSE file for details