Skip to content

feat(common): add payment required exception for http 402#16878

Open
LOsioChico wants to merge 1 commit intonestjs:masterfrom
LOsioChico:feat/payment-required-exception
Open

feat(common): add payment required exception for http 402#16878
LOsioChico wants to merge 1 commit intonestjs:masterfrom
LOsioChico:feat/payment-required-exception

Conversation

@LOsioChico
Copy link
Copy Markdown

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

@nestjs/common exports a dedicated HttpException subclass for every standard 4xx status code in the 400-422 range, except 402.

Status Class File
400 BadRequestException bad-request.exception.ts
401 UnauthorizedException unauthorized.exception.ts
402 (missing) (missing)
403 ForbiddenException forbidden.exception.ts
404 NotFoundException not-found.exception.ts
405 MethodNotAllowedException method-not-allowed.exception.ts
406 NotAcceptableException not-acceptable.exception.ts
408 RequestTimeoutException request-timeout.exception.ts
409 ConflictException conflict.exception.ts
410 GoneException gone.exception.ts
412 PreconditionFailedException precondition-failed.exception.ts
413 PayloadTooLargeException payload-too-large.exception.ts
415 UnsupportedMediaTypeException unsupported-media-type.exception.ts
418 ImATeapotException im-a-teapot.exception.ts
421 MisdirectedException misdirected.exception.ts
422 UnprocessableEntityException unprocessable-entity.exception.ts

The status enum already has the entry, HttpStatus.PAYMENT_REQUIRED = 402, and decorators consuming it work fine:

@Get()
@HttpCode(HttpStatus.PAYMENT_REQUIRED) // works today
getPaywalledResource() { ... }

But throwing it requires the verbose form:

throw new HttpException('Payment required', HttpStatus.PAYMENT_REQUIRED);

instead of the symmetric form available for every other 4xx:

throw new PaymentRequiredException('Quota exceeded'); // does not exist

Use case: paywalls, quota walls, "subscription required" responses.

What is the new behavior?

PaymentRequiredException is exported from @nestjs/common and behaves identically to its siblings:

import { PaymentRequiredException } from '@nestjs/common';

throw new PaymentRequiredException();
// HTTP 402 { statusCode: 402, message: 'Payment Required' }

throw new PaymentRequiredException('Quota exceeded');
// HTTP 402 { statusCode: 402, message: 'Quota exceeded', error: 'Payment Required' }

throw new PaymentRequiredException({ code: 'QUOTA', remaining: 0 });
// HTTP 402 { code: 'QUOTA', remaining: 0 }

The implementation mirrors ImATeapotException (same constructor signature, same default-message strategy, same cause option support).

Files changed:

  • packages/common/exceptions/payment-required.exception.ts (new, 55 lines).
  • packages/common/exceptions/index.ts (one new export * from line, alphabetical position).
  • packages/common/test/exceptions/http.exception.spec.ts (4 one-line insertions: import + getStatus table + getResponse table + builtInErrorClasses array). This extends the existing parametric tests and exercises the same status / default-message / cause-propagation paths every other built-in exception is tested for.

All 16 tests in http.exception.spec.ts pass locally; lint is clean.

Does this PR introduce a breaking change?

  • Yes
  • No

Pure addition. No existing identifier is renamed or removed.

Other information

  • The "Built-in HTTP exceptions" page on docs.nestjs.com lists each subclass by name. Happy to open a follow-up PR against nestjs/docs.nestjs.com once this lands.
  • Class name choice: PaymentRequiredException matches the HttpStatus.PAYMENT_REQUIRED enum entry and the IANA registered status text "Payment Required".
  • Subject is lowercase to satisfy commitlint's subject-case rule (single casing style required).

402 is the only status code in the 400-422 range without a dedicated
HttpException subclass. The HttpStatus.PAYMENT_REQUIRED enum entry
already exists, but throwing it requires the verbose form
'new HttpException(msg, HttpStatus.PAYMENT_REQUIRED)'.

Add PaymentRequiredException mirroring ImATeapotException for symmetry
with the other 21 sibling exceptions, export it from the barrel, and
extend the parametric tests in http.exception.spec.ts.
@coveralls
Copy link
Copy Markdown

Coverage Report for CI Build 0

Coverage remained the same at 90.025%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 8521
Covered Lines: 7671
Line Coverage: 90.02%
Relevant Branches: 2881
Covered Branches: 2338
Branch Coverage: 81.15%
Branches in Coverage %: No
Coverage Strength: 56.43 hits per line

💛 - Coveralls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants