fix(server): reject invalid HTTP methods with 400 in app route handlers#1048
Conversation
Vinext previously uppercased the request method and fell through to the normal 405 response for any unrecognized method. Next.js rejects non-standard methods with 400 before dispatch. - Add isValidHTTPMethod() predicate to app-route-handler-runtime.ts - Return empty 400 in dispatchAppRouteHandler() before auto-OPTIONS/405 - Port Next.js test: HEADER => 400 Bad Request Next.js source reference: https://github.com/vercel/next.js/blob/canary/packages/next/src/server/route-modules/app-route/module.ts#L390-L392 https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/app-routes/app-custom-routes.test.ts#L531-L538
commit: |
There was a problem hiding this comment.
Pull request overview
This PR aligns vinext App Router route handler dispatch behavior with Next.js by rejecting non-standard HTTP methods with a 400 Bad Request response before any auto-OPTIONS or 405 handling occurs.
Changes:
- Added an
isValidHTTPMethod()type-guard based onROUTE_HANDLER_HTTP_METHODS. - Updated
dispatchAppRouteHandler()to return an empty 400 response for invalid methods before resolving the handler method. - Added a unit test (ported from Next.js) verifying
"HEADER"is rejected with 400 and does not trigger cache/regeneration logic.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| tests/app-route-handler-dispatch.test.ts | Adds coverage ensuring invalid HTTP methods return 400 and bypass route execution/cache/regeneration paths. |
| packages/vinext/src/server/app-route-handler-runtime.ts | Introduces isValidHTTPMethod() co-located with the route handler method constants. |
| packages/vinext/src/server/app-route-handler-dispatch.ts | Performs early invalid-method rejection with 400 before OPTIONS/405 resolution and further processing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
/bigbonk review |
There was a problem hiding this comment.
Clean, well-scoped fix. The Next.js source confirms the behavior — isHTTPMethod() in packages/next/src/server/web/http.ts is a case-sensitive check against the same 7 methods, and resolveHandler() in module.ts returns () => new Response(null, { status: 400 }) for invalid methods before any dispatch logic.
The placement in dispatchAppRouteHandler is correct — it's the shared dispatch path used by both the RSC entry (entries/app-rsc-entry.ts:579) and production server, so this covers both dev and prod without duplication.
Test is thorough: verifies 400 status, empty body, middleware header passthrough, context cleanup, and uses throwing stubs to prove no cache/regeneration/handler code runs. LGTM.
| * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/server/web/http.ts | ||
| */ | ||
| export function isValidHTTPMethod(maybeMethod: string): maybeMethod is RouteHandlerHttpMethod { | ||
| return (ROUTE_HANDLER_HTTP_METHODS as readonly string[]).includes(maybeMethod); |
There was a problem hiding this comment.
Nit: Next.js uses .includes(maybeMethod as HTTP_METHOD) (casting the argument) rather than casting the array. Both work, but the Next.js pattern avoids widening the array type:
| return (ROUTE_HANDLER_HTTP_METHODS as readonly string[]).includes(maybeMethod); | |
| return ROUTE_HANDLER_HTTP_METHODS.includes(maybeMethod as RouteHandlerHttpMethod); |
Non-blocking — the current version is functionally identical and both are safe since includes does === comparison.
|
Review submitted. Approved with one minor nit about the array casting style in |
Summary
Vinext previously uppercased the request method and fell through to the normal 405 response for any unrecognized method. Next.js rejects non-standard methods with 400 Bad Request before dispatch.
Changes
isValidHTTPMethod()predicate toapp-route-handler-runtime.ts, co-located with the existingROUTE_HANDLER_HTTP_METHODSconstant.dispatchAppRouteHandler()before any auto-OPTIONS or 405 logic, matching Next.js behavior.HEADER=> 400.Next.js source reference
packages/next/src/server/route-modules/app-route/module.ts#L390-L392test/e2e/app-dir/app-routes/app-custom-routes.test.ts#L531-L538packages/next/src/server/web/http.tsVerification
vp test run tests/app-route-handler-dispatch.test.ts tests/app-route-handler-policy.test.ts tests/app-route-handler-runtime.test.ts— 25/25 passingvp test run tests/app-router.test.ts -t "route handler"— 28/28 passing