Next.js Discord

Discord Forum

How can I revalidate (on-demand) manual caching?

Answered
North Pacific hake posted this in #help-forum
Open in Discord
North Pacific hakeOP
We all know Next.js has this neat feature called [on-demand revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/revalidating#on-demand-revalidation) which I would like to use, but I have an issue.

I'm using Sanity as my CMS, and Sanity has their own client for fetching data. But how can I add a tag revalidation to Sanity, which doesn't use fetch?

I was able to cache the data by wrapping my API functions in React's cache function, as recommended by [the documentation](https://nextjs.org/docs/app/building-your-application/data-fetching/caching#react-cache).

This is an example of one of said functions:
// sanity/api.ts

export const getPosts = cache(
  async (params?: GetPostsParams): Promise<Post[]> => {
    const { labKey } = params || {};

    const query = `...`;

    const posts = await sanity.fetch(query);
    return posts;
  }
);

Logically, I should be able to do something like this, but obviously these don't work:
// sanity/api.ts

export const getPosts = cache(
  async (params?: GetPostsParams): Promise<Post[]> => {
    const { labKey } = params || {};

    const query = `...`;

    const posts = await sanity.fetch(query, { next: { tags: ['posts'] }); // this doesn't work
    return posts;
  }
, { next: { tags: ['posts'] }); // this doesn't work either

PS: I'm using the new /app structure, if that is of any importance
Answered by North Pacific hake
unstable_cache was too unstble, and I didn't want the overhead of using NextJS' API just for the sake of having the ability to add on-demand caching, so I came up with a very hacky solution myself.
// utils/nextUtils.ts

/**
 * This function adds request config to a non-next-fetch request.
 * ORM requests, or requests that are not made with fetch, can use this  function to add eg. revalidation tags or timer.
 * It's a hacky way to do it, but it's the only solution I could find until Next.js adds a proper way to do this.
 */

export const addNextRequestConfig = (options: NextFetchRequestConfig) => {
  fetch("data:text/plain;base64,", { next: options }); // fetching nothing just to add the next property to the request
};

This is how you use it:
export const getPosts = async (params?: GetPostsParams): Promise<Post[]> => {
  addNextRequestConfig({ tags: ["sanity", "posts"] }); // This adds `tags`, can also be used to just add `revalidate`

  const { labKey } = params || {};

  const query = `...`;

  const posts = await sanity.fetch(query);
  return posts;
};
View full answer

28 Replies

North Pacific hakeOP
As a last resort, I'll have to wrap this logic in a Next.js API route, and then just add revalidation to that route by using fetch. This 'solution' is an overhead proxy though that I don't like but I cannot come up with anything else. Manual caching should obviously also support manual revalidation.
or if you feel adventurous, have a look at this https://twitter.com/steventey/status/1658851469334855682
an unreleased and undocumented API, but the source code is there to dive in
North Pacific hakeOP
Oh nice, I didn't know about revalidatePath, thanks! I guess I will do that for now, although it still seems a bit 'hacky'.
North Pacific hakeOP
Wait so unstable_cache is basically a wrapper that replaces fetch cache behaviour? @joulev
Ojos Azules
Does the cache persist if you refresh the page? If for example I fetch a post from prisma like this:
export const revalidate = 3600; // revalidate every hour

const getFirstPost = async () => {
  const latestPost = await prisma.post.findFirst({
    orderBy: { createdAt: "desc" },
  });

  return latestPost;
};
it should
though there's only one way to really find out: try it yourself
Ojos Azules
I see prisma making a new call every time I refresh, so I don't really get the behavior
North Pacific hakeOP
How do you test if a function is cached? I just put a console.log and that shouldn't run anymore after the first time right?
Make sure you're not in dev mode btw, I think that has impact on the way cache is handled in NextJS
@North Pacific hake How do you test if a function is cached? I just put a `console.log` and that shouldn't run anymore after the first time right?
Ojos Azules
if it's a 3rd party call you can look out for calls in the network tab in your browser
Yup, dev mode doesnt have cache
Try prod mode
Ojos Azules
running the build now lemme see
North Pacific hakeOP
next build && next start
I dont have an app with export const revalidate, but i do have an app without it and prisma is only run once during build
Ojos Azules
wdym without it? did you setup any cache?
I don’t need to revalidate it on interval, i only call revalidatePath for it when i need it to update
Ojos Azules
even on prod everytime I refresh I see
prisma:query SELECT `t3approuter`.`Post`.`id`, `t3approuter`.`Post`.`text`, `t3approuter`.`Post`.`createdAt`, `t3approuter`.`Post`.`updatedAt`, `t3approuter`.`Post`.`createdById` FROM `t3approuter`.`Post` WHERE 1=1 ORDER BY `t3approuter`.`Post`.`createdAt` DESC LIMIT ? OFFSET ?
North Pacific hakeOP
where do you export revalidate?
@Ojos Azules
I'm pretty sure you can only export from a page, not from a separate utils file and such if that's what you're using
Ojos Azules
yeah from page.tsx
North Pacific hakeOP
unstable_cache was too unstble, and I didn't want the overhead of using NextJS' API just for the sake of having the ability to add on-demand caching, so I came up with a very hacky solution myself.
// utils/nextUtils.ts

/**
 * This function adds request config to a non-next-fetch request.
 * ORM requests, or requests that are not made with fetch, can use this  function to add eg. revalidation tags or timer.
 * It's a hacky way to do it, but it's the only solution I could find until Next.js adds a proper way to do this.
 */

export const addNextRequestConfig = (options: NextFetchRequestConfig) => {
  fetch("data:text/plain;base64,", { next: options }); // fetching nothing just to add the next property to the request
};

This is how you use it:
export const getPosts = async (params?: GetPostsParams): Promise<Post[]> => {
  addNextRequestConfig({ tags: ["sanity", "posts"] }); // This adds `tags`, can also be used to just add `revalidate`

  const { labKey } = params || {};

  const query = `...`;

  const posts = await sanity.fetch(query);
  return posts;
};
Answer
North Pacific hakeOP
I feel like NextJS needs to start paying attention to ORM support, because it's quite poorly supported atm