seN49S
tRPC3y ago
1 reply
seN49

Tranform output data using middleware after handler has run - possible?

As the title says, been trying for a couple of hours to modify the return type of the data using middleware. So far unsuccesful.

The idea: Handler returns data, audit middleware logs the full entity, then pipe through another middleware that transforms the full entity into some DTO.

DX wise the nicest solutions I have come up so far is using the middleware pipe:

Derived example:
// audit-middleware.ts
export const auditMiddleware = experimental_standaloneMiddleware<{
    ctx,
    meta
}>().create(async ({ next, ctx, meta, rawInput }) => {
    const result = await next({ ctx });

    if (result.ok && meta?.auditProxy) {
        await meta.auditProxy(ctx, result.data, rawInput);
    }

    return result;
});

// procedure.ts
const protectedUserCommandProcedure = <TAuditData, TData>(mapper: (data: TAuditData) => TData) => procedure.use(auditMiddleware.unsable_pipe(async({ctx, next}) => {
const result = await next({ ctx });
                if (result.ok) {
                    result.data = mapper(result.data as TAuditData);
                    return result;
                }

                return result;
})


Usage:
const createSomethingProcedure = protectedUserCommandProcedure(data => ({ ...data, foo: 'bar'})).meta({auditProxy}).mutation(someHandler);


This is the only solution so far that seems to get all typings correct UNTIL it gets to the client. The client sadly still only sees the mutation / query return type and ignores the middleware one.

I can put together a sandbox as well, but I wonder if I'm simply missing some official way on how to update the return type.

The very last solution would be to run audit and the transformer in the mutation:
.mutation() => {
  const data = await someHandler();
  await audit(data);
  return mapper(data);
}


But would prefer handling it through the middleware if possible
Was this page helpful?