typesafe permissions

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. : )
N
Nick429d ago
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
I
isitayush426d ago
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?
N
Nick426d ago
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
I
isitayush426d ago
Understood! Thank You. I'll try this & if it works I'll update it here & let you know.
AK
Alex / KATT 🐱426d ago
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(() => '...')
})
I
isitayush425d ago
thanks alex! both this & nick's solution works great.
UU
Unknown User403d ago
AK
Alex / KATT 🐱403d ago
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
UU
Unknown User403d ago
AK
Alex / KATT 🐱403d ago
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
AK
Alex / KATT 🐱403d ago
Define Procedures | tRPC
Procedures in tRPC are very flexible primitives to create backend functions; they use a builder pattern which means you can create reusable base procedures for different parts of your backend application.
UU
Unknown User403d ago
AK
Alex / KATT 🐱403d ago
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
UU
Unknown User402d ago
More 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 querHow to use querykeys from react-queryI am trying to implement a search query to an api that i am fetching via a procedure, i also read onDistribute typesafe tRPC Client in an NPM libraryHi ! super fan of trpc over here. We are building a javascript sdk for our API that is essentiallyWebsocket 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 accesWebSocket connection hangs after significant amount of data passed through the connection.Hey! I've been really enjoying using tRPC on my latest project, but have gotten stuck getting websoCannot 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 aIs there an example of a real world non trivial app?Something that includes type inference, nested fields, calculated fields, nested React components thHow to organise output types?I'm having a hard time trying to figure out what the best way to organise output types and I was wonExtending 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