seN49
seN49
TtRPC
Created by seN49 on 11/10/2023 in #❓-help
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
2 replies
TtRPC
Created by seN49 on 3/30/2023 in #❓-help
errorFormatter ignored when using appRouter.createCaller
My errorFormatter works correctly in the actual application using an adapter, e.g.
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext: createTRPCContextFromExpress,
}),
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext: createTRPCContextFromExpress,
}),
But in my tests where I just use createCaller, the errorFormatter is never called. I created a quick sandbox to show this behavior: https://stackblitz.com/edit/trpc-server-inferred-type-cannot-be-named-x2pseq?file=src/index.ts When calling getFoo() I am expecting the errorFormatter to be called and transform the error to { message: 'Huge error' }, but when using the appRouter.createCaller(), the errorFormatter is never called. Any idea what I am missing?
4 replies
TtRPC
Created by seN49 on 2/27/2023 in #❓-help
async createContext for Express Adapter
Been debugging an odd behavior for the past hour, it seems like that an async function does not work in the express adapter. Is that supposed to work or is that the expected behavior? If this is a bug i can toss together a quick sandbox, but figured I check first whether this is expected or not. If this is expected behavior, should the async calls happen in middleware instead? Seems a bit at odds. Thoughts?
6 replies