tRPC middleware infer type from another protectedProcedure

Hello, protectedProcedure check and adds a not nullable user to the ctx. However the studyMiddleware does not know that user is not nullable, is there a way for the middleware to be aware of it? In my current code I had to validate the user is not nullable inside the middleware and had to manually pass the user to the ctx user: opts.ctx.user, to make user not nullable inside the mutation. Here is a sample of my code
const studyMiddleware = middleware(async (opts) => {
if (!opts.ctx.user) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Unauthorized",
});
}

const parsedInput = z
.object({
studyId: z.string().cuid(),
})
.parse(opts.input);

const study = await studyRepository.findOneByIdAndClientId({
id: parsedInput.studyId,
clientId: opts.ctx.user.client.id,
});

if (!study) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Study not found",
});
}

return opts.next({
ctx: {
...opts.ctx,
user: opts.ctx.user,
study,
},
});
});

export const budgetRouter = createTRPCRouter({
create: protectedProcedure
.use(studyMiddleware)
.input(createBudgetInputSchema)
.mutation(({ input, ctx: { user, study } }) =>
budgetService.create(input, user, study),
),
});
const studyMiddleware = middleware(async (opts) => {
if (!opts.ctx.user) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Unauthorized",
});
}

const parsedInput = z
.object({
studyId: z.string().cuid(),
})
.parse(opts.input);

const study = await studyRepository.findOneByIdAndClientId({
id: parsedInput.studyId,
clientId: opts.ctx.user.client.id,
});

if (!study) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Study not found",
});
}

return opts.next({
ctx: {
...opts.ctx,
user: opts.ctx.user,
study,
},
});
});

export const budgetRouter = createTRPCRouter({
create: protectedProcedure
.use(studyMiddleware)
.input(createBudgetInputSchema)
.mutation(({ input, ctx: { user, study } }) =>
budgetService.create(input, user, study),
),
});
S
seN49159d ago
I've found using procedure works better for types than the middleware. If you change your middleware and use .use it should probably work as you expect!