Skip to content

Request specifics

Each request can have its own cache customization, by using the cache property. This way, you can have requests behaving differently from each other without much effort.

The inline documentation is self explanatory, but here is a shortly brief of what each property does:

TIP

You can override every request specific property when creating the cached axios client, the same way you do with the global options.

id

optional
  • Type: string
  • default: (auto generated by the current key generator)

The Request ID used in this request.

It may have been generated by the Key Generator or a custom one provided by config.id

cache

optional
  • Type: false or Partial<CacheProperties<R, D>>.
  • Default: {} (Inherits from global configuration)

TIP

As this property is optional, when not provided, all properties will inherit from global configuration

The cache option available through the request config is where all the cache customization happens.

Setting the cache property to false will disable the cache for this request.

This does not mean that the cache will be excluded from the storage, in which case, you can do that by deleting the storage entry:

ts
// Make a request with cache disabled.
const { id: requestId } = await axios.get('url', { cache: false });

// Delete the cache entry for this request.
await axios.storage.remove(requestId);

cache.ttl

optional
  • Type: number
  • Default: 1000 * 60 * 5 (5 Minutes)

WARNING

When using interpretHeader, this value will only be used if the interpreter can’t determine their TTL value to override this one.

The time until the cached value is expired in milliseconds.

If a function is used, it will receive the complete response and waits to return a TTL value

cache.interpretHeader

optional
  • Type: boolean
  • Default: true

TIP

You can override the default behavior by changing the headerInterpreter option.

If activated, when the response is received, the ttl property will be inferred from the requests headers. As described in the MDN docs and HTML specification.

See the actual implementation of the interpretHeader method for more information.

cache.cacheTakeover

optional
  • Type: boolean
  • Default: true

As most of our cache strategies depends on well known defined HTTP headers, most browsers also use those headers to define their own cache strategies and storages.

This can be seen when opening network tab in your browser’s dev tools.

Network tab in Chrome Dev Tools

When your requested routes includes Cache-Control in their responses, you may end up with we and your browser caching the response, resulting in a double layer of cache.

This option solves this by including some predefined headers in the request, that should tell any client / adapter to not cache the response, thus only we will cache it.

These are headers used in our specific request, it won’t affect any other request or response that the server may handle.

Headers included:

  • Cache-Control: no-cache
  • Pragma: no-cache
  • Expires: 0

WARNING

This option will not work on most CORS requests, as the browser will throw Request header field pragma is not allowed by Access-Control-Allow-Headers in preflight response..

When you encounter this error, you need to make sure Cache-Control, Pragma and Expires headers are included into your server’s Access-Control-Allow-Headers CORS configuration.

If you cannot do such thing, you can fallback to disabling this option. Learn more on why it should be enabled at #437 and in this StackOverflow answer.

cache.methods

optional
  • Type: Method[]
  • Default: ["get", "head"]

Specifies which methods we should handle and cache. This is where you can enable caching to POST, PUT, DELETE and other methods, as the default is only GET.

If you want to enable cache for POST requests, you can do:

ts
// Globally enables caching for POST requests
const axios = setupCache(instance, {
  methods: ['get', 'post']
});

// Just for this request
axios.post('url', data, {
  cache: {
    methods: ['post']
  }
});

We use methods in a per-request configuration setup because sometimes you have exceptions to the method rule.

cache.cachePredicate

optional
  • Type: CachePredicate<R, D>
  • Default: { statusCheck: (status) => [200, 203, 300, 301, 302, 404, 405, 410, 414, 501].includes(status) }(These default status codes follows RFC 7231)

An object or function that will be tested against the response to indicate if it can be cached. You can use statusCheck, containsHeader, ignoreUrls and responseMatch to test against the response.

ts
axios.get<{ auth: { status: string } }>('url', {
  cache: {
    cachePredicate: {
      // Only cache if the response comes with a "good" status code
      statusCheck: (status) =>  true, // some calculation

      // Tests against any header present in the response.
      containsHeaders: {
        'x-custom-header-3': (value) => true // some calculation
      },

      // Check custom response body
      responseMatch: ({ data }) => {
        // Sample that only caches if the response is authenticated
        return data.auth.status === 'authenticated';
      },

      // Ensures no request is cached if its url starts with "/api"
      ignoreUrls: [/^\/api/]
    }
  }
});

cache.update

optional
  • Type: CacheUpdater<R, D>
  • Default: {}

Once the request is resolved, this specifies what other responses should change their cache. Can be used to update the request or delete other caches. It is a simple Record with the request id.

Here’s an example with some basic login:

Using a function instead of an object is supported but not recommended, as it’s better to just consume the response normally and write your own code after it. But it`s here in case you need it.

ts
// Some requests id's
let profileInfoId;
let userInfoId;

axios.post<{ auth: { user: User } }>(
  'login',
  { username, password },
  {
    cache: {
      update: {
        // Evicts the profile info cache, because now he is authenticated and the response needs to be re-fetched
        [profileInfoId]: 'delete',

        // An example that update the "user info response cache" when doing a login.
        // Imagine this request is a login one.
        [userInfoResponseId]: (cachedValue, response) => {
          if (cachedValue.state !== 'cached') {
            // Only needs to update if the response is cached
            return 'ignore';
          }

          cachedValue.data = data;

          // This returned value will be returned in next calls to the cache.
          return cachedValue;
        }
      }
    }
  }
);

cache.etag

optional
  • Type: boolean
  • Default: true

If the request should handle ETag and If-None-Match support. Use a string to force a custom static value or true to use the previous response ETag.

To use true (automatic ETag handling), interpretHeader option must be set to true.

cache.modifiedSince

optional
  • Type: boolean
  • Default: true

Use If-Modified-Since header in this request. Use a date to force a custom static value or true to use the last cached timestamp.

If never cached before, the header is not set.

If interpretHeader is set and a Last-Modified header is sent to us, then value from that header is used, otherwise cache creation timestamp will be sent in If-Modified-Since.

cache.staleIfError

  • Type: number or boolean or StaleIfErrorPredicate<R, D>
  • Default: true

Enables cache to be returned if the response comes with an error, either by invalid status code, network errors and etc. You can filter the type of error that should be stale by using a predicate function.

WARNING

If the response is treated as error because of invalid status code (like when using statusCheck), and this ends up true, the cache will be preserved over the “invalid” request.

So, if you want to preserve the response, you can use the below predicate:

ts
const customPredicate = (response, cache, error) => {
  // Blocks staleIfError if has a response
  return !response;

  // Note that, this still respects axios default implementation
  // and throws an error, (but it keeps the response)
};

Types:

  • number -> the max time (in seconds) that the cache can be reused.
  • boolean -> false disables and true enables with infinite time if no value is present on stale-if-error in Cache-Control.
  • function -> a predicate that can return number or boolean as described above.

cache.override

optional
  • Type: boolean
  • Default: false

This option bypasses the current cache and always make a new http request. This will not delete the current cache, it will just replace the cache when the response arrives.

Unlike as cache: false, this will not disable the cache, it will just ignore the pre-request cache checks before making the request. This way, all post-request options are still available and will work as expected.

cache.hydrate

optional
  • Type: undefined | ((cache: StorageValue) => void | Promise<void>)
  • Default: undefined

Asynchronously called when a network request is needed to resolve the data, but an older one and probably expired cache exists. Its with the current data BEFORE the network travel starts, so you can use it to temporarily update your UI with expired data before the network returns.

Hydrating your components with old data before the network resolves with the newer one is better than flickering your entire UI. This is even better when dealing with slower networks and persisted cache, like for mobile apps.

WARNING

If the axios call will return cached data, meaning no network will be involved, the hydrate IS NOT CALLED, as the axios promise will be resolved instantly.

ts
// Example of function that receives data and renders into a screen
function render() {}

const response = await axios.get( 'url', {
  // This is called instantly if axios needs to make a network request
  cache: {
    hydrate: (cache) => render(cache.data)
  }
});

// After the network lookup ends, we have fresh data and can
// re-render the UI with confidence
render(response.data);

Made with ❤️