Skip to content

fix(api): coerce numeric query params, which the new global pipe rejected - #204

Merged
0xdevcollins merged 1 commit into
mainfrom
fix/numeric-query-params
Aug 9, 2026
Merged

fix(api): coerce numeric query params, which the new global pipe rejected#204
0xdevcollins merged 1 commit into
mainfrom
fix/numeric-query-params

Conversation

@0xdevcollins

Copy link
Copy Markdown
Owner

The regression

#199 installed a global ValidationPipe. That made @IsNumber() run against query strings for the first time — and query values are always strings, so:

GET /v1/payments?limit=2
→ 400 {"message":["limit must be a number conforming to the specified constraints"]}

Every paginated list endpoint using PaymentFiltersDto was affected: page, limit, minAmount, maxAmount.

It broke because validation started working. Before the pipe, nothing ran and the string went through untouched, so this was invisible until the pipe made the constraint real.

The fix

@Type(() => Number) on the four query fields, converting before validation.

Deliberately not enableImplicitConversion: true on the global pipe: that flag also coerces request bodies, and an amount field that quietly accepts "5" for 5 is not what you want on the endpoints that move money. Query DTOs are where the coercion is genuinely needed, so that is where it goes.

Verified against the running API

request before after
?limit=2 400 200
?limit=2&page=1 400 200
?limit=abc 400 400
?status=NOPE 400 400

How it was found

Auditing leftover worktree branches for unmerged work — one of them touched the payments list, so I exercised the endpoint directly. No existing e2e case passed a numeric query parameter, which is why CI stayed green through the break. Three cases added to close that gap.

Tests: 287 unit, 18 e2e (3 new).

…cted

Installing the global ValidationPipe in #199 made `@IsNumber()` run against
query strings for the first time. Query values are always strings, so
`GET /v1/payments?limit=2` started coming back 400 with "limit must be a number
conforming to the specified constraints". Every paginated list endpoint using
PaymentFiltersDto was affected — page, limit, minAmount and maxAmount alike.

It broke precisely because validation started working: before the pipe, nothing
ran, and the string went through untouched.

`@Type(() => Number)` converts the four query fields before validation. Done on
the DTO rather than by enabling `enableImplicitConversion` globally, because
that flag also coerces request bodies — and a body field that quietly accepts
"5" for 5 is not what you want on the endpoints that move money.

Found by exercising the running API while auditing leftover worktree branches;
the existing e2e suites never passed a numeric query parameter, so nothing
caught it. They do now.

Tests: 287 unit, 18 e2e (3 new).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
useroutr-www Ready Ready Preview Aug 9, 2026 7:42pm

@0xdevcollins
0xdevcollins merged commit 812ce36 into main Aug 9, 2026
8 checks passed
@0xdevcollins
0xdevcollins deleted the fix/numeric-query-params branch August 9, 2026 19:54
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.

1 participant