Search code examples
reactjsreduxreact-reduxredux-toolkit

Is it possible to apply a tag to all endpoints within an API slice in Redux Toolkit Query?


I have an API with a parameter called 'region' which is applied in the request header. The region is dynamic, changing as the user does things in the site, and because it's not part of the request URL/parameters I had to add it into the cache key using serializeQueryArgs:

serializeQueryArgs: args => {
    const region = localStorage.getItem('region');

    return `${region}_${defaultSerializeQueryArgs(args)}`;
}

This prevents the API from getting confused between cache entries for different regions, which is good, but it doesn't cause requests to re-fetch with the new region like I want.

I thought I could accomplish the automatic re-fetching using cache tags, by adding a tag type to my API in its definition and then invalidating that tag when the region changes:

export const apiSlice = createApi({
    tagTypes: ['region'],

    // Other stuff.
});
const changeRegion = region => {
    dispatch(setRegion(region));
    dispatch(apiSlice.util.invalidateTags([cloudTag]));
}

This works great! The problem is that, for every endpoint I want to re-fetch, I have to add the tag:

providesTags: ['region']

I have hundreds of endpoints, so this isn't really practical, and enforcing that everyone adding new endpoints remembers to add the tag without ever forgetting it seems... quite difficult. I'd really like to be able to add the tag to every endpoint in one go.

I thought maybe enhanceEndpoints could do this, but I'm not sure how to use it. Ideally, I'd like a solution as concise as this:

export const apiSlice = createApi({
    tagTypes: ['region']
})
.providesTags(['region']);

I also tried to use apiSlice.util.resetApiSlice for this, but rather than trigger a re-fetch of everything that replaced the whole site with a white screen and I had to refresh to get it to load again.


Solution

  • Based on the Code Splitting: enhanceEndpoints documentation I could come up with this that appears to correctly inject/enhance each endpoint with the "region" tag and serialized query args.

    const apiSlice = createApi({
      tagTypes: ['region'],
      // Other stuff.
    });
    
    const enhancedApi = apiSlice.enhanceEndpoints({
      endpoints: Object.fromEntries(
        Object.entries(apiSlice.endpoints).map(([key, endpoint]) => {
          const enhancedEndpoint = {
            ...endpoint,
            providesTags: ["region"],
            serializeQueryArgs: (args) => {
              const region = JSON.parse(localStorage.getItem("region"));
    
              return `${region}_${defaultSerializeQueryArgs(args)}`;
            }
          };
    
          return [key, enhancedEndpoint];
        })
      )
    });
    
    export default enhancedApi;
    

    I think this is what you were looking for, but since I don't know what any of your queries are or what the rest of your code is doing around your queries, this wasn't something I could easily test/verify.