T
tRPC

❓-help

typesafe permissions

Iisitayush2/17/2023
Hi, So I wanted to infer all the procedures from my router recursively & assign a permission (string[]) to each one them. I wrote the following,
type GetProceduresRecusivelyAndAssignPermissions<T extends AnyRouter> = {
[K in keyof T]: T[K] extends AnyProcedure
? { permissions: string[] }
: T[K] extends AnyRouter
? GetProceduresRecusivelyAndAssignPermissions<T[K]>
: never;
};

const permissions: GetProceduresRecusivelyAndAssignPermissions<
typeof appRouter
> = {
example: { getData: { permissions: ["canGetData"] } },
};
type GetProceduresRecusivelyAndAssignPermissions<T extends AnyRouter> = {
[K in keyof T]: T[K] extends AnyProcedure
? { permissions: string[] }
: T[K] extends AnyRouter
? GetProceduresRecusivelyAndAssignPermissions<T[K]>
: never;
};

const permissions: GetProceduresRecusivelyAndAssignPermissions<
typeof appRouter
> = {
example: { getData: { permissions: ["canGetData"] } },
};
It works however, I get a red squiggly (error) under the example in my permissions object with the following error message.
Type '{ getData: { permissions: string[]; }; }' is missing the following properties from type 'GetProceduresRecusivelyAndAssignPermissions<CreateRouterInner<RootConfig<{ ctx: ....
Type '{ getData: { permissions: string[]; }; }' is missing the following properties from type 'GetProceduresRecusivelyAndAssignPermissions<CreateRouterInner<RootConfig<{ ctx: ....
In my above type, I am trying to check if the key is a procedure, then simply returning the permissions. Otherwise checking if the key is a router then repeating it recursively. I'm unable to figure out what I'm doing wrong. Can someone help fix this. : )
Nnlucas2/17/2023
Would it not be cleaner to use a Middleware and Procedure Meta to set this up? That’s the more first class way to manage access programmatically in tRPC The trouble with iterating over the routers is you may touch APIs which are considered internal and may change without notice, plus as you’ve found you run into things which aren’t really documented and you’ll be on your own figuring it out
Iisitayush2/20/2023
Apologizes for this late response. You're right. It'd be nicer if I could achieve this with Meta. However, I am not really sure how that would work. I tried creating a solution using Meta but couldn't really come up with something that would fit my case. Diving in some internal code for trpc (the GetInferenceHelpers to be more precise). I did find my self a working solution.
type MapRouterToPermissions<T extends AnyRouter> = {
[K in keyof T["_def"]["record"]]: T["_def"]["record"][K] extends infer P
? P extends AnyRouter
? MapRouterToPermissions<P>
: P extends AnyProcedure
? string[]
: never
: never;
};
type MapRouterToPermissions<T extends AnyRouter> = {
[K in keyof T["_def"]["record"]]: T["_def"]["record"][K] extends infer P
? P extends AnyRouter
? MapRouterToPermissions<P>
: P extends AnyProcedure
? string[]
: never
: never;
};
I'm not sure though what ["_def"]["record"] really hold here. If I'm not wrong Nick, The meta can be accessed in the middleware before a procedure is fired right?
Nnlucas2/20/2023
Yes ^ You just have to codify your permissions in a declarative way. My app has entitlements, and roles which are associated with multiple entitlements. Every route gets an entitlement. Every user gets a role. Middleware checks a route’s entitlement and if the user has it via their role. Quite a simple model
Iisitayush2/20/2023
Understood! Thank You. I'll try this & if it works I'll update it here & let you know.
AKAlex / KATT 🐱2/20/2023
i do a factory function instead for my proc creation
export const scopedProcedure = (scope: PermissionScope) =>
authedProcedure.meta({
scope,
});
export const scopedProcedure = (scope: PermissionScope) =>
authedProcedure.meta({
scope,
});
then a middleware in my authedProcedure do check that the user has the defined scope example
const postRouter = router({
edit: scopedProcedure('post.edit').mutation(() => '...')
})
const postRouter = router({
edit: scopedProcedure('post.edit').mutation(() => '...')
})
Iisitayush2/21/2023
thanks alex! both this & nick's solution works great.
UUUnknown User3/15/2023
Message Not Public
Sign In & Join Server To View
AKAlex / KATT 🐱3/15/2023
we haven't OSS'ed our thing at work [yet] but we do a sort of mapper between our user roles and prisma "where"-queries that are passed through AsyncLocalStorage
UUUnknown User3/15/2023
2 Messages Not Public
Sign In & Join Server To View
AKAlex / KATT 🐱3/15/2023
Base procedure with an input e.g. organizationId Do auth check where you fetch the user, check if the user is a member of the supplied org id, and get their role https://trpc.io/docs/procedures#reusable-base-procedures
UUUnknown User3/15/2023
2 Messages Not Public
Sign In & Join Server To View
AKAlex / KATT 🐱3/15/2023
If you want that granularity, maybe you wna use graphql or some other abstraction for your services like nestjs trpc doesn't "care" about your business logic and we haven't made any abstractions to make this sort of granularity easy
UUUnknown User3/16/2023
2 Messages Not Public
Sign In & Join Server To View

Looking for more? Join the community!

Recommended Posts
awaiting for procedure & logging the response.Hi, I was wondering if there is a way to handle the return object via the post-middleware's? I know createCaller Dependency Injection in Middleware ctx ?`createCaller` makes it really easy to inject dependencies via anything that's created during the `cbest practices for organizing routes/procedures?i'm trying to find some practices/styles in which people generally define routes with trpc. currentlValidating input inside middleware declaration```js const enforceUserIsCreatorOfEvent = t.middleware(({ ctx, next, input }) => { if (!input.evenFetch errors on stale pagesRecently I have been getting a lot of fetch errors on stale pages, in particular ones that have querDistribute typesafe tRPC Client in an NPM libraryHi ! super fan of trpc over here. We are building a javascript sdk for our API that is essentiallyMutation or query for something that updates db, but runs on every app load?I have 2 operations that need to run before showing my app UI. Those operations perform updates in DWebsocket is not defined errorI'm getting a "WebSocket is not defined error" on my next app connected to an express backend. Any i@trpc/server in a non-server environment Error in Azure CIIm trying to add vitest unit tests for my trpc procedures. I followed some examples and on the localHow are people handling authorization?I noticed that with V10, any mentions of `trpc-shield` are gone from the documentation. Also, it onlVitest context router callerHi, Im trying to setup vitest to test trpc. I would like to have a trpc approuter caller to be accesCannot read properties of undefined (reading 'data') of res.error.data, when trpc errors outHello everyone, I am using `@trpc/react-query` alongside `trpc` for express, and I am experiencing aExtending middlewareshttps://trpc.io/docs/middlewares#extending-middlewares Is this available?Calling a trpc endpoint inside of a trpc endpointHey all. I'm wondering how I am able to call these endpoints from within themselves? For example,Frozen input paramIs it possible to define a parameter on input schema (zod) that will have a hardcoded/frozen value wtype mismatch between tRPC return (in sveltekit) and defined typei've got this piece of code: ```ts read: async () => { const res = await trpc($page).getCards.querpre fetch serveral prodecures dynamicallyHello!. I'm developing a React Native app which needs an offline mode for some assets that the user procedure input contextIs it possible to accesss context in trpc procedure input validation? I have an aray of values in tIs it normal to separate resolvers from routers?Wondering if it's common and/or whether it's a good idea to attempt to separate procedures from the i have an infinite loop within my hook but apps works as expected.when i console log inside this hook, it repeats non-stop every few seconds, but my app is working as