P.S.Nikhil
P.S.Nikhil2y ago

How to cache trpc server request next.js app router

is it the right way ctx.headers.set( "Cache-Control", "public, max-age=43200, stale-while-revalidate=120, immutable", );
13 Replies
Fossil
Fossil17mo ago
Did you figure this out? I am stumped
P.S.Nikhil
P.S.NikhilOP17mo ago
You can use react cache to handle client-side caching, but I haven't figured it out for server api level caching
Fossil
Fossil17mo ago
Yea lots of options with clientside cacheing. Kinda ridiculous there's no docs for app router cacheing Currently documented tRPC cacheing methods (https://trpc.io/docs/server/caching) won't work as they rely on setting headers which NextJs now overrides (https://nextjs.org/docs/app/api-reference/next-config-js/headers#cache-control)
Kenzo
Kenzo14mo ago
omg.. I've been trying this for so long today so, there is no way to Next caches trpc calls?
BeBoRE
BeBoRE14mo ago
You cannot set Cache-Control headers in next.config.js for pages or assets, as these headers will be overwritten in production to ensure that responses and static assets are cached effectively.
Doesn't say anything about the app router and API routes.
BeBoRE
BeBoRE14mo ago
Caching on Vercel's Edge Network
Vercel's Edge Network caches your content at the edge in order to serve data to your users as fast as possible. Learn how Vercel caches works in this guide.
Kenzo
Kenzo14mo ago
yeah but i did not found any way to modify fetch headers of TRPC anyway, Julius said (in other discord) that a good alternative if I need to force cache is use unstable cache
Fossil
Fossil13mo ago
Can you link to this Kenzo? Holy fck you're right. I feel like a tremendous pleb. This adds the correct Cache-Control headers to the response, and the server respects them. cc @Kenzo
// next.config.js
headers: async () => {
return [
{
source: "/api/trpc/yourRouteName",
headers: [
{
key: "Cache-Control",
value: "public, s-maxage=90, max-age=90",
},
],
},
];
},
// next.config.js
headers: async () => {
return [
{
source: "/api/trpc/yourRouteName",
headers: [
{
key: "Cache-Control",
value: "public, s-maxage=90, max-age=90",
},
],
},
];
},
Alex / KATT 🐱
Alex / KATT 🐱13mo ago
you can use responseMeta() as well https://trpc.io/docs/server/caching#api-response-caching oh, next.js overrides them? i didn't experience that last time i tried it
Kenzo
Kenzo13mo ago
damn I should try that hmm yeah, but I ended up using a more straigh foward way, in other discord as Julius mentioned, react cache + next unstable_cache Also, I was reading you could modify the headers of your trpc frontend calls, but not your backend ones right? or it applies to both? with unstable_cache I totally avoid having to fight against next modifying headers -- At the end, I just wanted to have my beloved stale while revalidate in TRPC easily --- Hey Alex. Let's simplify this. I have a wiki with weapons in a DB. I Want to cache the query and revalidate it when I modify the weapons table for example. Vercel fetch stale while revalidate would work perfectly. But we are using trpc. Not fetch directly. Having to modify the responseMeta is weird for this simple use case Also afaik. You can modify trpc Fetch for client calls not backend
Alex / KATT 🐱
Alex / KATT 🐱13mo ago
open for suggestions and PRs
Zefty
Zefty4w ago
Hey all, im trying to do something similar where I have some data that won't change very frequently that ill be calling server side. Any ideas on what you guys did with react cache + next unstable_cache to get it working? @Kenzo which other discord did Julius mention that other method? Thanks
Zefty
Zefty4w ago
A bit more context for my problem. I have a webhook api route and it fetches this from the db
export hander() {
...
const jobApplicationSenderEmails =
await getJobApplicationSenderEmailsContextCached();
const jobRoles = await getJobRolesContextCached();
const jobApplicationKeywordIdentifiers =
await getJobApplicationKeywordIdentifiersContextCached();
...
}


// Cached queries
export const getJobApplicationSenderEmailsContextCached = unstable_cache(
async () => {
return await api.jobApplication.getAllSenderEmails();
},
["job-application-sender-emails-context"],
{ revalidate: 60 * 60 },
);

export const getJobRolesContextCached = unstable_cache(
async () => {
return await api.jobApplication.getAllJobRoles();
},
["job-application-roles-context"],
{
revalidate: 60 * 60,
},
);

export const getJobApplicationKeywordIdentifiersContextCached = unstable_cache(
async () => {
return await api.jobApplication.getAllKeywordIdentifiers();
},
["job-application-keyword-identifiers-context"],
{
revalidate: 60 * 60,
},
);
export hander() {
...
const jobApplicationSenderEmails =
await getJobApplicationSenderEmailsContextCached();
const jobRoles = await getJobRolesContextCached();
const jobApplicationKeywordIdentifiers =
await getJobApplicationKeywordIdentifiersContextCached();
...
}


// Cached queries
export const getJobApplicationSenderEmailsContextCached = unstable_cache(
async () => {
return await api.jobApplication.getAllSenderEmails();
},
["job-application-sender-emails-context"],
{ revalidate: 60 * 60 },
);

export const getJobRolesContextCached = unstable_cache(
async () => {
return await api.jobApplication.getAllJobRoles();
},
["job-application-roles-context"],
{
revalidate: 60 * 60,
},
);

export const getJobApplicationKeywordIdentifiersContextCached = unstable_cache(
async () => {
return await api.jobApplication.getAllKeywordIdentifiers();
},
["job-application-keyword-identifiers-context"],
{
revalidate: 60 * 60,
},
);
This doesn't quite work because Error: Route /api/webhook/email/gmail used "headers" inside a function cached with "unstable_cache(...)". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use "headers" outside of the cached function and pass the required dynamic data in as an argument. See more info here: https://nextjs.org/docs/app/api-reference/functions/unstable_cache
Functions: unstable_cache | Next.js
API Reference for the unstable_cache function.

Did you find this page helpful?