[▲ Vercel Community](/) · [Categories](/categories) · [Latest](/latest) · [Top](/top) · [Live](/live) [Feedback](/c/feedback/8) # Allow adding cache tags that depend on the response of `fetch` or `unstable_cache` 135 views · 1 like · 3 posts Romeovs (@romeovs) · 2024-10-17 Cross-posting this from [GitHub discussion](https://github.com/vercel/next.js/discussions/65521), in the hopes this gets more traction here. ### Usecase In some cases it might be useful to provide cache tags to a cache entry, _after_ the result has been received. For instance, I might fetch a product from the api: ```js await unstable_cache( () => api.products.get({ id }), ["product", id], { tags: [`product:${id}`], }, )() // { type: "product", id: 42, ... } ``` I pass the `product:$42` tag so I can invalidate the cached item when the product with that `id` changes. It is possible, because we know the `id` we're trying to fetch ahead of time and so we can add the tag when calling `unstable_cache`. Additionally, I might want to include related products in the response: ```js await unstable_cache( () => api.products.get({ id }, { related: true }), ["product", id], { tags: [`product:${id}`], }, ) // { type: "product", id: 42, related: [{ type: "product", id: 85 }, ...], ... } ``` Here it is incorrect to only add `product:42` as a tag, since the result also contains data from other products. We should also pass `product:85` and other product tags from the response so we can correctly invalidate the response when related products change. The way `unstable_cache` works now, it is not possible to do this atomically. ### Proposal It would be useful if we could calculate the tags from the response in `unstable_cache` so the tags can be set atomically: In next something similar could look like: ```js await unstable_cache( () => api.products.get({ id }, { related: true }), ["product", id], { tags(product) { // return the related product's tags too return [`product:${id}`, ...product.related.map(p => `product:${p.id}`)] } }, ) // { type: "product", id: 42, related: [{ type: "product", id: 85 }, ...], ... } ``` This will allow sites to cache responses _way_ more agressively, since they can tag the cache entries with info about their _content_. Being able to this is a major blocker for me to do more fine-grained caching. ### Background One example where a similar pattern is used is [RTK Query](https://redux-toolkit.js.org/rtk-query/usage/automated-refetching), where [`providesTags`](https://redux-toolkit.js.org/rtk-query/api/createApi#providestags) is allowed to be a function that takes the response as an argument and returns a list of tags. ### Workaround It _does_ seem to be possible to add cache tags to an existing cache key (without invoking the cached function again), but that was only clear to me when I read the `unstable_cache` code and I think it's an implementation detail, not an API guarantee. Additionally, this is not atomic, so it might happen that a cache tag gets revalidated using `revalidateTag` between the first and second call to `unstable_cache` in the above function will cause the cache entry to not be cleared properly. ```ts function async workaround_cache(fn, key, options) { const res = await unstable_cache(fn, key, { tags: typeof options.tags === "function" ? [] : options.tags, }) const tags = typeof options.tags === "function" ? await options.tags(res) : options.tags // A second call does not call fn again, but because the key is the same, but it // will add the tags to the existing cache entry. // (!) DANGER: this is relying on an implementation detail // (!) DANGER: this introduces a race condition return await unstable_cache(fn, key, { tags, }) } ``` It would be good to add this to the `unstable_cache` API. I'm happy to help and set up a PR if this seems like a good idea. Romeovs (@romeovs) · 2024-10-22 · ♥ 1 It seems like the new `"use cache"` system would allow this to work, as long as we can call `cacheTag` *after* `await`! ``` function cached() { const res = await fn() const tags = options.tags(res) cacheTags(tags) return res } Pauline P. Narvas (@pawlean) · 2024-11-05 Let us know if you have any more questions, @romeovs :slight_smile: Happy to pass onto the Next.js team!