ctx
in queries is often being used as inputs when some are defined in the middlewares.
This is not taken in account with experimental app dir support, as the cacheTag
only caches the inputs, but never the context.
Therefore, I am suffering a bug when a query that just uses ctx
to retrieve user-specific data from ctx.session
, is being cached with no inputs []
, and I am able to see the cached data of another user.
Experimental tRPC's NextJS AppRouter layout support involves caching of query inputs, and is implemented in the next way:
trpc/packages/next/src/app-dir/links/nextCache.ts
Lines 34 to 43 in b72c7aa
While it works for most of the cases, problems arise when your query logic depends on trpc's middleware
s.
Here's an example implementation of a protected procedure taken from the tRPC's documentation:
import { initTRPC, TRPCError } from '@trpc/server';
export const t = initTRPC.context<Context>().create();
// you can reuse this for any procedure
export const protectedProcedure = t.procedure.use(async function isAuthed(
opts,
) {
const { ctx } = opts;
// `ctx.user` is nullable
if (!ctx.user) {
// ^?
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return opts.next({
ctx: {
// β
user value is known to be non-null now
user: ctx.user,
// ^?
},
});
});
t.router({
// this is accessible for everyone
hello: t.procedure
.input(z.string().nullish())
.query((opts) => `hello ${opts.input ?? opts.ctx.user?.name ?? 'world'}`),
admin: t.router({
// this is accessible only to admins
secret: protectedProcedure.query((opts) => {
return {
secret: 'sauce',
};
}),
}),
});
As you can see, the ctx.user?.name
in this example is implicitly becoming an input, because you use it in the query implementation, yet it's not being included in the cacheKey
!
In this particular case, which is taken from the docs, the consequence is that the first user querying the hello
query, would make it cache the response hello alice
(i.e), then when the second user queries the same procedure (bob i.e.), he would see hello alice
as it was cached with a cacheKey
of []
as inputs to the functions simply don't exist.
contextCache
or similar to be implemented in the options of experimental_nextCacheLink
to specify which fields from the context should be cached, to still make use of caching.
This would also involve changing the way the cacheTag
is generated. Specifically, it would need to have the cachedContextValues
(or similar) as input, to pass the values that should be cached.
Also, due to the nature of NextJS caching, the cacheTag
should not be longer than 256 characters, therefore the cacheTag
should rely on a hash of all inputs, instead of passing the stringified data.
A hacky patch is implemented at https://github.com/trpc/trpc/discussions/5447
noCache
procedure middleware to opt out from caching, which then would be used inprotectedProcedure
's by default, as a developer might depend onctx
data.
@KATT, https://discord.com/channels/867764511159091230/1204483271342301264/1204483291701317732
Discussions ref: https://github.com/trpc/trpc/discussions/5447
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too