How to properly use context in tRPC?

Issue

Let’s say I have a very basic API with two sets of endpoints. One set queries and mutates properties about a User, which requires a username parameter, and one set queries and mutates properties about a Post, which requires a post ID. (Let’s ignore authentication for simplicity.) I don’t currently see a good way to implement this in a DRY way.

What makes the most sense to me is to have a separate Context for each set of routes, like this:

// post.ts
export async function createContext(
    opts?: trpcExpress.CreateExpressContextOptions
) {
    // pass through post id, throw if not present
}
type Context = trpc.inferAsyncReturnType<typeof createContext>;

const router = trpc
    .router()
    .query("get", {
        resolve(req) {
            // get post from database
            return post;
        },
    });

// similar thing in user.ts

// server.ts
const trpcRouter = trpc
    .router()
    .merge("post.", postRouter)
    .merge("user.", userRouter);

app.use(
    "/trpc",
    trpcExpress.createExpressMiddleware({
        router: trpcRouter,
        createContext,
    })
);

This complains about context, and I can’t find anything in the tRPC docs about passing a separate context to each router when merging. Middleware doesn’t seem to solve the problem either – while I can fetch the post/user in a middleware and pass it on, I don’t see any way to require a certain type of input in a middleware. I would have to throw { input: z.string() } or { input: z.number() } on every query/mutation, which of course isn’t ideal.

The docs and examples seem pretty lacking for this (presumably common) use case, so what’s the best way forward here?

Solution

This functionality has been added in (unreleased as of writing) v10. https://trpc.io/docs/v10/procedures#multiple-input-parsers

const roomProcedure = t.procedure.input(
  z.object({
    roomId: z.string(),
  }),
);
 
const appRouter = t.router({
  sendMessage: roomProcedure
    .input(
      z.object({
        text: z.string(),
      }),
    )
    .mutation(({ input }) => {
      // input: { roomId: string; text: string }
    }),
});

Answered By – scatter

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published