Modular Router Thoughts

Hello, I hope I can convey clearly what I hope to accomplish. To start, I have a monorepo, with a few frontends and lots of packages. some of those packages relate to external services. For instance @org/asana and @org/zoom I have another package, that is my big-ole-router that builds the trpc router and imports all those other packages, builds middlewares to add clients, builds procedures to scope each service.... This was great when I had the router mounted in a mega App I used to have, but now I am making smaller apps with more scoped features.... I don't really want to import the mega router into each app with lots of unneeded procedures. I also don't want to create trpc routers over and over with different combos of middlewares/procedures..... Is there a nice way to be able to piece-meal a router together with the service specific pieces? Should I attach scoped service routers to difference endpoints? I don't know the best way to go about solving this problem and I would love some guidance from the community. Thank you!
12 Replies
Alex / KATT 🐱
one way would be to define the procedures without routers then you can compose them however you want you can have a flat namespace with a lot of very long proc names in an object and then router(_.pick(procs, ['getUser, 'createUser', ..]))
kevin.hill.fortunabmc
I was under the impression that you can only call initTrpc once to get your t and then export the aliases like t.middleware and t.procedure... so, with this example, instead of building the zoom stuff in the trpc monorepo package how would I build procedures without routers? When I tried to export t.procedure or the protectedProcedure I made with middleware, from my @org/api and use it in @org/zoom, I got an error about using something that is instantiating itself. (Sorry, I forget the exact error since I moved on from the other day when I was stuck)
kevin.hill.fortunabmc
But I can't stop thinking about how to make a click-together router where the services can bring their own procedures and middleware https://discord.com/channels/867764511159091230/1109939580389572728/1111427640394530887 Actually, if I am remembering right... I think I had a cycles issue where @org/api can't export protectedProcedure for @org/asana to use to create asanaProcedure because then I would have to import it back in @org/api. Unless that is what you meant with your reply.... 🤔 I want @org/api to provide the auth, so I can protect all procedures..... I'm trying to wrap my head around this all, so can @org/api init, and create protectedProcedure and export that. Then any service can import and keep building (no routers yet anywhere!) .... (no cycles yet) then in my actual NextJS app, import both the api and the service and build the router there??? Instead of trimming, I did like you suggested. I pulled all the scoped routers into their service specific package, then removed all the router() calls and export them as procedures.
kevin.hill.fortunabmc
They are easily exported from their package, and then turned into the router, in the app. I realize now... @org/api shouldn't have everything
kevin.hill.fortunabmc
It has some local procedures, and imported procedures
Alex / KATT 🐱
looking pretty nice! what are you using trpc for btw? where do you work? 😄 (sorry, i've been in the matrix with RSC all day - looks like you figured out what you want to do though?)
kevin.hill.fortunabmc
I am using tRPC with to rapidly build internal apps for my team. We have lots of services we use, but no good birds-eye-view of all our users. I work for https://www.fortunabmc.com/ I use NextJS and tRPC and, for example, make a simple app that ties Azure and Asana together to be able to have a drop-down of users, populated by tRPC and Azure, and then it can create tasks in Asana using info from MsGraph or maybe we need something with Asana and Zoom, or Salesforce and Dropbox. tRPC has made it so easy to modularize the different clients into procedures, protect it all with Clerk and a middleware. It's so nice to be able to do all of that with type safety I think I am running into a new issue.... Since @org/api created t with initTRPC to make my auth middleware to make my protectedProcedure ok... I imported that into @org/asana to be able to make asanaProcedure and this appeared to work... But mutations are failing because a Date is going through "naked" and failing to parse as JSON. I know you are going to say superjson and I have it everywhere it should be.... I think since the app I am building also creates a t to use in the app, but now there are 2 ts, one from inside @org/api and then the other from the app? Is that what is causing it not to transform? That was it!!! I went ahead and exported t from @org/api so that I can use that instance inside the app to create the final router and it finally works!! You're a great sounding board 🤣
kevin.hill.fortunabmc
I continued on this path and now have slim, composable routers that can be defined per app with just what they need 🙂
Alex / KATT 🐱
Looking great! You could do a blog article about this! ☺