Next.js Discord

Discord Forum

"use cache" arguments and tags

Unanswered
Chow Chow posted this in #help-forum
Open in Discord
Chow ChowOP
I'm looking at the new "use cache" directive and considering giving it a try. I am using unstable_cache for a while now in existing projects and I was generally pretty happy with it. This new approach definitely looks better but I have on pretty important question about the argument capture for creating the cache entries.

Any serializable arguments (or props) passed to the cached function, as well as any serializable values it reads from the parent scope, will be converted to a format like JSON and automatically become a part of the cache key.

How exactly does the capture work? If I use cacheTag for the cached function, will it affect the capture in any way or are the tags purely for revalidation? I was using keyParts argument of unstable_cache to affect the caching behaviour but now I'm afraid that since it's "automatic" I won't be able to do that.

5 Replies

Chow ChowOP
https://github.com/Haaxor1689/talent-builder/blob/master/src/server/api/helpers.ts
Here are my helpers I was using with unstable_cache that I would ideally want to get rid of completely and achieve the same thing with just using "use cache" with cacheTag and cacheLife
Chow ChowOP
Ok so I rewrote it and what I feared happened, I'm getting errors that some values like callbacks and zod schema can't be serialized. Is there a way to manually specify what should the "dependencies" of the cache be so I can pass arbitrary callback to it? Here is the code:

export const serverQuery = <
    SessionType,
    Func extends ProcedureFn<Input, SessionType>,
    Input extends z.ZodTypeAny = z.ZodUndefined
>({
    input,
    session,
    queryKey,
    query
}: QueryProps<SessionType, Func, Input>): ProcedureReturn<
    SessionType,
    Func,
    Input
> &
    RevalidateType<Input> => {
    const handler = async (val: z.infer<Input>) => {
        const s = session ? await session() : null;
        return cache(async () => {
            'use cache';
            cacheTag(queryKey, `${queryKey}: ${JSON.stringify(val)}`);
            cacheLife({
                stale: 60 * 5,
                revalidate: 60 * 60 * 24 * 30,
                expire: INFINITE_CACHE
            });

            console.log('[MISS]:', queryKey);
            return (await query(input?.parse(val), s as never)) ?? null;
        })();
    };
    handler.revalidate = (val?: z.infer<Input>) => {
        console.log('[CLEAR]:', queryKey, val ?? 'ALL');
        return revalidateTag(
            val ? `${queryKey}: ${JSON.stringify(val)}` : queryKey
        );
    };
    return handler as never;
};


Where input is a zod schema and query is the actual callback that "does the work". And here is example usage:

export const getCrashReports = serverQuery({
    queryKey: 'getCrashReports',
    session: SessionType.admin,
    query: async () =>
        await db.query.crashReports.findMany({
            orderBy: asc(crashReports.created),
            limit: 50
        })
});


If there is some fundamental design issue with my solution I'm definitely open to suggestions.
Chow ChowOP
hmm nope it just doesn't work, I keep getting this error
Error: Attempted to call a temporary Client Reference from the server but it is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.
what does it have to do with client? this cache should be fully server side right?
Chow ChowOP
any reply here would be really appreciated