feat(server): add MethodOverrideHandlerPlugin - #1805
Conversation
Allow overriding the HTTP method of a POST request via a query parameter (e.g. POST /todos/1?method=DELETE routed as DELETE), so HTML forms, which only support GET and POST, can invoke PUT/PATCH/DELETE procedures. The parameter is stripped before input decoding; disallowed values are silently ignored.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/experimental-effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/nest
@orpc/next
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
MethodOverrideHandlerPlugin— new@orpc/server/pluginsexport that rewrites a POST request's method (stripping the override query param) before routing; defaults to?method=withPUT/PATCH/DELETEtargets, both configurable. Placedafter = ['~batch']and prepended as a routing interceptor, mirroringRequestCompressionHandlerPluginexactly, so only the original envelope is overridden — batch sub-requests (which route by RPC name) are unaffected.- Interceptor unit tests — cover override, case-insensitive matching, repeated params (last wins, all stripped), preservation of other params and the hash, disallowed values (including
GET/HEAD/POST), custom param name and method set, plus anRPCHandlerintegration test. - OpenAPI integration tests — form-encoded POST routed to a DELETE operation with clean input; plain POST left unmatched; no override-param leak into
detailedqueryinput; disallowed value silently ignored. - Docs page — new
/docs/plugins/method-overridepage with a setup example and an honest:::warningdocumenting theSimpleCsrfProtectionHandlerPluginincompatibility.
I verified the interceptor composition: since each plugin prepends and intercept runs index 0 outermost, the after = ['~batch'] ordering places this interceptor outside the batch interceptor, exactly matching the stated intent of overriding the original request only. The parseStandardUrl/URLSearchParams handling is consistent with how the existing batch plugin consumes the same search fragment, and the test assertions are specific enough to fail if the described bugs were present.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
Initialize before ~batch so each sub-request passes through the override individually instead of only the outer batch request.
There was a problem hiding this comment.
ℹ️ No new critical issues — one verification gap inline.
Reviewed changes (delta since the prior pullfrog review of 8013866)
- Batch-aware ordering flip —
MethodOverrideHandlerPluginnow declaresbefore = ['~batch'](wasafter), so the method override applies to each batch sub-request rather than the original batch envelope, with the doc comment updated to match ("override batch sub-request methods, not the original batch request"). - No new tests in this delta for the changed batch behavior — the added unit,
RPCHandler, and OpenAPI tests all exercise a single non-batch POST.
I verified the ordering semantics: before = ['~batch'] sorts the plugin before batch (init runs first, leaving batch outermost), so its interceptor sits inside batch and re-runs on each sub-request via the batch plugin's per-sub-request next(...) — matching how response-headers (before = ['~batch']) applies per sub-response, whereas request-compression uses after for original-only. The flip's stated intent is achieved; my one concern is that this changed behavior is unverified by a test.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Adds
MethodOverrideHandlerPluginto@orpc/server/plugins. A POST request carrying amethodquery parameter (e.g.POST /todos/1?method=DELETE) is routed and executed as that method, so HTML forms, which only support GET and POST, can invoke procedures routed as PUT, PATCH, or DELETE.Behavior
method) and allowed targets (PUT,PATCH,DELETE) are configurable.inputStructure: 'detailed').after = ['~batch']).Notes for reviewers
RPCHandlerandOpenAPIHandler; it is most useful with the latter, where the method decides route matching.SimpleCsrfProtectionHandlerPlugin, which blocks thenavigatefetch mode real form submissions use.Testing
RPCHandlerintegration test inpackages/server, andOpenAPIHandlerintegration tests intests/openapi(form-encoded POST hits a DELETE route with clean input, plain POST stays unmatched, no param leak into detailedquery).pnpm type:check,pnpm lint, andpnpm docs:validatepass.