fix(nest): run method-level guards on routes synthesized by router-contract @Implement - #1775
Conversation
… by router-contract @implement
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/client
@orpc/contract
@orpc/experimental-durable-iterator
@orpc/hey-api
@orpc/interop
@orpc/json-schema
@orpc/nest
@orpc/openapi
@orpc/openapi-client
@orpc/otel
@orpc/experimental-pino
@orpc/experimental-publisher
@orpc/experimental-publisher-durable-object
@orpc/experimental-ratelimit
@orpc/react
@orpc/react-query
@orpc/experimental-react-swr
@orpc/server
@orpc/shared
@orpc/solid-query
@orpc/standard-server
@orpc/standard-server-aws-lambda
@orpc/standard-server-fastify
@orpc/standard-server-fetch
@orpc/standard-server-node
@orpc/standard-server-peer
@orpc/svelte-query
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/vue-colada
@orpc/vue-query
@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
packages/nest/src/implement.ts—Object.setPrototypeOf(target[methodName], descriptor.value!)makes each synthesized router-route function inherit from the original handler, so Nest's prototype-walkingReflectorresolves method-level metadata (@UseGuards,@UsePipes,@UseFilters,@SetMetadata) in either decorator order.packages/nest/src/implement.test.ts— 4 new e2e tests booting a Nest app with aCanActivateguard, covering@UseGuardsabove and below@Implement, asserting deny→403 (handler never invoked) and allow→200.
The fix is well-targeted: decorators evaluate bottom-up, so metadata written above @Implement lands on descriptor.value only after the existing eager own-metadata copy loop has run — meaning a copied approach literally cannot work for the above case, and prototype inheritance is the right mechanism. I verified the new tests genuinely fail against the previous code (unauthenticated requests returned 200 and the guard never ran) and pass with the fix, across both decorator orderings.
The documented @UseInterceptors limitation (a user interceptor placed above @Implement is shadowed by oRPC's own ImplementInterceptor own-metadata) is inherent to Nest's array-metadata merge and accurately described.
openrouter/deepseek/deepseek-v4-flash-0731 (free via Pullfrog for OSS) | 𝕏
|
Run failed. View the logs →
|
a50be84 to
d728c37
Compare

Method-level
@UseGuards(and@UsePipes,@UseFilters,@SetMetadata) on a handler decorated with a router-contract@Implementwere silently ignored: Nest stores that metadata on the decorated function object, but@Implementregisters freshly synthesized functions as the actual routes, so the guards never ran. Routes that looked protected were publicly reachable. Each synthesized route function now inherits from the original method via its prototype chain, so Nest resolves the metadata from the registered callback in either decorator order.Fixes
@SetMetadatanow execute on every route synthesized from a router contract, whether the decorator is placed above or below@Implement. An eager metadata copy could not achieve this: decorators evaluate bottom-up, so anything written above@Implementattaches its metadata after@Implementhas already run.@UseInterceptorsis merged ahead of oRPC's own interceptor when placed below@Implement. Placing it above still loses it — an inherent limit of Nest's array-metadata scheme, since the synthesized route must define its own interceptor entry.@Implement, class-level enhancers, and global providers were unaffected and remain so.Testing
CanActivateguard on the router method in both decorator orderings: denied requests get 403 with the procedure handler never invoked, allowed requests succeed. All 4 new tests fail against the previous code (unauthenticated requests returned 200).@orpc/nestsuite passes (29 tests).