Gabriel
Gabriel9mo ago

Why deprecate experimental_standaloneMiddleware?

I am reading https://trpc.io/docs/server/middlewares#experimental-standalone-middlewares, but it's still not clear to me why experimental_standaloneMiddleware was deprecated. It says we can use .concat() to use "standalone" middlewares. However, I don't see how they are related exactly? The example shows we can use a plugin. This would take in a new procedure to do concating of procedures. But this is not exactly what I want. In my code I have this:
const todoAppInstalledMiddleware experimental_standaloneMiddleware<{
ctx: TProtectedProcedureContext;
}>().create(async ({ ctx, next }) => {
const foundPermission = await ctx.prisma.teamAppRole.findFirst({
where: {
AppPermissions: {
some: {
id: "234234324-my-todo-app-id-here-yay",
},
},
Team: {
id: ctx.session.user.activeTeamId,
},
Users: {
some: {
id: ctx.session.user.id,
},
},
},
select: { id: true },
});

if (!foundPermission)
throw new TRPCError({
code: "UNAUTHORIZED",
message: `You don't have permission to do this. Contact an administrator if you believe it is an error.`,
});

return next({ ctx });
});
const todoAppInstalledMiddleware experimental_standaloneMiddleware<{
ctx: TProtectedProcedureContext;
}>().create(async ({ ctx, next }) => {
const foundPermission = await ctx.prisma.teamAppRole.findFirst({
where: {
AppPermissions: {
some: {
id: "234234324-my-todo-app-id-here-yay",
},
},
Team: {
id: ctx.session.user.activeTeamId,
},
Users: {
some: {
id: ctx.session.user.id,
},
},
},
select: { id: true },
});

if (!foundPermission)
throw new TRPCError({
code: "UNAUTHORIZED",
message: `You don't have permission to do this. Contact an administrator if you believe it is an error.`,
});

return next({ ctx });
});
Doing this via concat() feels unnatural. I just want to define a middleware that I can use in multiple procedures where my session is not null (protected context). What is the recommended way of doing this?
4 Replies
Gabriel
GabrielOP9mo ago
I also have this use of a more generic middleware that requires an input:
export const appInstalledMiddleware = experimental_standaloneMiddleware<{
ctx: TProtectedProcedureContext;
input: { appId: KodixAppId };
}>().create(async ({ ctx, input, next }) => {
const team = await ctx.prisma.team.findUnique({
where: { id: ctx.session.user.activeTeamId },
select: {
ActiveApps: {
select: {
id: true,
},
},
},
});

if (!team?.ActiveApps.some((x) => x.id === input.appId))
throw new TRPCError({
code: "UNAUTHORIZED",
message: `${getAppName(input.appId)} is not installed`,
});

return next({ ctx });
});
export const appInstalledMiddleware = experimental_standaloneMiddleware<{
ctx: TProtectedProcedureContext;
input: { appId: KodixAppId };
}>().create(async ({ ctx, input, next }) => {
const team = await ctx.prisma.team.findUnique({
where: { id: ctx.session.user.activeTeamId },
select: {
ActiveApps: {
select: {
id: true,
},
},
},
});

if (!team?.ActiveApps.some((x) => x.id === input.appId))
throw new TRPCError({
code: "UNAUTHORIZED",
message: `${getAppName(input.appId)} is not installed`,
});

return next({ ctx });
});
aselcuktuncer
aselcuktuncer8mo ago
I'm having the same problem. Is there any explanation?
Sandvich
Sandvich8mo ago
Any reason why you're using standalone middleware instead of regular middleware? Standalone is just for using the same middleware across different tRPC instances but it sounds like you're using one router and many procedures?
aselcuktuncer
aselcuktuncer7mo ago
because 'Creating middlewares using t.middleware has the limitation that the Context type is tied to the Context type of the tRPC instance'. i want to create a standalone middleware like this: const projectAccessMiddleware = experimental_standaloneMiddleware<{ ctx: { allowedProjects: string[] }; // defaults to 'object' if not defined input: { projectId: string }; // defaults to 'unknown' if not defined // 'meta', not defined here, defaults to 'object | undefined' }>().create((opts) => { and then i just want to use it in the router like this: .use(projectAccessMiddleware); using concat() and creating a plugin result in creating a new procedure as far as i understand but i do not want to create a new procedure i just want to use a middleware. as like in the example code input and ctx objects may include different values like userId, projectId etc which comes from input(). it was really easy to create new standalone middlewares earlier and now we have have to create a new produceure or *concat() * plugins to existing procedures to later use in the router? i mean now it looks like standalone middlewares are tied to procedures directly and this is not the exact alternative for experimental_standaloneMiddleware right?