seN49
seN499mo ago

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;
})
// 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);
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);
}
.mutation() => {
const data = await someHandler();
await audit(data);
return mapper(data);
}
But would prefer handling it through the middleware if possible
1 Reply
seN49
seN499mo ago
Found a reddit link with a similar use case... Here the OP wants to enrich the return type: https://www.reddit.com/r/sveltejs/comments/12n376g/type_error_trpc_middleware_enriching_result/ No solution though