mirror of
https://github.com/arthurfiorette/axios-cache-interceptor.git
synced 2025-12-08 17:36:16 +00:00
Add enabled flag for opt-in cache control (#1141)
* Initial plan * Add enabled flag to CacheProperties with full implementation and tests Co-authored-by: arthurfiorette <47537704+arthurfiorette@users.noreply.github.com> * Fix deprecated annotation to only apply to cache: false, add documentation for enabled flag Co-authored-by: arthurfiorette <47537704+arthurfiorette@users.noreply.github.com> * Improve JSDoc comment for DeprecatedFalse type Co-authored-by: arthurfiorette <47537704+arthurfiorette@users.noreply.github.com> * Remove deprecation warnings from docs, keep cache: false for backward compatibility Co-authored-by: arthurfiorette <47537704+arthurfiorette@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: arthurfiorette <47537704+arthurfiorette@users.noreply.github.com>
This commit is contained in:
parent
47bb5b0c6a
commit
6bda1baae0
@ -30,7 +30,7 @@ or a custom one provided by [`config.id`](./request-specifics.md#id)
|
||||
|
||||
<Badge text="optional" type="warning"/>
|
||||
|
||||
- Type: `false` or `Partial<CacheProperties<R, D>>`.
|
||||
- Type: `Partial<CacheProperties<R, D>>`.
|
||||
- Default: `{}` _(Inherits from global configuration)_
|
||||
|
||||
::: tip
|
||||
@ -43,19 +43,77 @@ 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.
|
||||
You can pass an object with cache properties to customize cache behavior.
|
||||
|
||||
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:
|
||||
To disable caching for a specific request, use `cache: { enabled: false }`:
|
||||
|
||||
```ts
|
||||
// Make a request with cache disabled.
|
||||
const { id: requestId } = await axios.get('url', { cache: false });
|
||||
// Make a request with cache disabled
|
||||
const { id: requestId } = await axios.get('url', { cache: { enabled: false } });
|
||||
|
||||
// Delete the cache entry for this request.
|
||||
// Delete the cache entry for this request if needed
|
||||
await axios.storage.remove(requestId);
|
||||
```
|
||||
|
||||
## cache.enabled
|
||||
|
||||
<Badge text="optional" type="warning"/>
|
||||
|
||||
- Type: `boolean`
|
||||
- Default: `true`
|
||||
|
||||
Whether the cache is enabled for this request.
|
||||
|
||||
When set to `false`, the cache will be completely disabled for this request.
|
||||
|
||||
This is useful for **opt-in cache** scenarios where you want to disable cache globally
|
||||
but enable it for specific requests.
|
||||
|
||||
### Example: Opt-in Cache Pattern
|
||||
|
||||
You can disable cache by default and enable it only for specific endpoints:
|
||||
|
||||
```ts
|
||||
import { setupCache } from 'axios-cache-interceptor';
|
||||
|
||||
// Setup axios with cache disabled by default
|
||||
const axios = setupCache(axiosInstance, {
|
||||
enabled: false // Disable cache globally
|
||||
});
|
||||
|
||||
// Most requests won't use cache
|
||||
await axios.get('/api/realtime-data'); // Not cached
|
||||
|
||||
// Enable cache for specific heavy/expensive requests
|
||||
await axios.get('/api/heavy-computation', {
|
||||
cache: {
|
||||
enabled: true,
|
||||
ttl: 1000 * 60 * 10 // Cache for 10 minutes
|
||||
}
|
||||
}); // Cached
|
||||
```
|
||||
|
||||
### Example: Traditional Pattern (Opt-out)
|
||||
|
||||
The traditional pattern where cache is enabled by default:
|
||||
|
||||
```ts
|
||||
import { setupCache } from 'axios-cache-interceptor';
|
||||
|
||||
// Setup axios with cache enabled by default (this is the default behavior)
|
||||
const axios = setupCache(axiosInstance, {
|
||||
enabled: true // or omit this as true is the default
|
||||
});
|
||||
|
||||
// Most requests will use cache
|
||||
await axios.get('/api/user-profile'); // Cached
|
||||
|
||||
// Disable cache for specific real-time endpoints
|
||||
await axios.get('/api/live-stock-prices', {
|
||||
cache: { enabled: false }
|
||||
}); // Not cached
|
||||
```
|
||||
|
||||
## cache.ttl
|
||||
|
||||
<Badge text="optional" type="warning"/>
|
||||
|
||||
7
src/cache/axios.ts
vendored
7
src/cache/axios.ts
vendored
@ -79,14 +79,17 @@ export interface CacheRequestConfig<R = any, D = any> extends AxiosRequestConfig
|
||||
* 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.
|
||||
* You can pass an object with cache properties to customize cache behavior.
|
||||
*
|
||||
* **Note:** Setting `cache: false` is still supported for backward compatibility, but
|
||||
* will be removed in the next major release. Use `cache: { enabled: false }` instead.
|
||||
*
|
||||
* This does not mean that the current cache will be excluded from the storage.
|
||||
*
|
||||
* @default 'inherits from global configuration'
|
||||
* @see https://axios-cache-interceptor.js.org/config/response-object#cache
|
||||
*/
|
||||
cache?: false | Partial<CacheProperties<R, D>>;
|
||||
cache?: Partial<CacheProperties<R, D>> | false;
|
||||
}
|
||||
|
||||
/** Cached version of type {@link InternalAxiosRequestConfig} */
|
||||
|
||||
14
src/cache/cache.ts
vendored
14
src/cache/cache.ts
vendored
@ -22,6 +22,20 @@ import type { CacheAxiosResponse, InternalCacheRequestConfig } from './axios.js'
|
||||
* @template D The type for the request body
|
||||
*/
|
||||
export interface CacheProperties<R = unknown, D = unknown> {
|
||||
/**
|
||||
* Whether the cache is enabled for this request.
|
||||
*
|
||||
* When set to `false`, the cache will be completely disabled for this request,
|
||||
* similar to setting `cache: false` in the request config.
|
||||
*
|
||||
* This is useful for opt-in cache scenarios where you want to disable cache globally
|
||||
* but enable it for specific requests by setting `cache: { enabled: true }`.
|
||||
*
|
||||
* @default true
|
||||
* @see https://axios-cache-interceptor.js.org/config/request-specifics#cache-enabled
|
||||
*/
|
||||
enabled: boolean;
|
||||
|
||||
/**
|
||||
* The time until the cached value is expired in milliseconds.
|
||||
*
|
||||
|
||||
2
src/cache/create.ts
vendored
2
src/cache/create.ts
vendored
@ -57,6 +57,8 @@ export function setupCache(axios: AxiosInstance, options: CacheOptions = {}): Ax
|
||||
|
||||
// CacheRequestConfig values
|
||||
axiosCache.defaults.cache = {
|
||||
enabled: options.enabled ?? true,
|
||||
|
||||
update: options.update || {},
|
||||
|
||||
ttl: options.ttl ?? 1000 * 60 * 5,
|
||||
|
||||
@ -30,6 +30,19 @@ export function defaultRequestInterceptor(axios: AxiosCacheInstance): RequestInt
|
||||
// merge defaults with per request configuration
|
||||
config.cache = { ...axios.defaults.cache, ...config.cache };
|
||||
|
||||
// Check if cache is disabled via enabled flag
|
||||
if (config.cache.enabled === false) {
|
||||
if (__ACI_DEV__) {
|
||||
axios.debug({
|
||||
id: config.id,
|
||||
msg: 'Ignoring cache because config.cache.enabled === false',
|
||||
data: config
|
||||
});
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// ignoreUrls (blacklist)
|
||||
if (
|
||||
typeof config.cache.cachePredicate === 'object' &&
|
||||
|
||||
191
test/interceptors/enabled.test.ts
Normal file
191
test/interceptors/enabled.test.ts
Normal file
@ -0,0 +1,191 @@
|
||||
import assert from 'node:assert';
|
||||
import { describe, it } from 'node:test';
|
||||
import { mockAxios } from '../mocks/axios.js';
|
||||
|
||||
describe('Cache Enabled Flag', () => {
|
||||
it('Cache enabled by default (enabled: true)', async () => {
|
||||
const axios = mockAxios();
|
||||
|
||||
const response1 = await axios.get('http://test.com');
|
||||
assert.equal(response1.cached, false);
|
||||
assert.equal(response1.stale, undefined);
|
||||
|
||||
const response2 = await axios.get('http://test.com');
|
||||
assert.ok(response2.cached);
|
||||
assert.equal(response2.stale, false);
|
||||
});
|
||||
|
||||
it('Global cache disabled (enabled: false)', async () => {
|
||||
const axios = mockAxios({ enabled: false });
|
||||
|
||||
const response1 = await axios.get('http://test.com');
|
||||
const response2 = await axios.get('http://test.com');
|
||||
|
||||
assert.equal(response1.cached, false);
|
||||
assert.equal(response1.stale, undefined);
|
||||
assert.equal(response2.cached, false);
|
||||
assert.equal(response2.stale, undefined);
|
||||
|
||||
// Verify cache is empty
|
||||
const cacheKey = axios.generateKey(response1.config);
|
||||
const cache = await axios.storage.get(cacheKey);
|
||||
assert.equal(cache.state, 'empty');
|
||||
});
|
||||
|
||||
it('Global cache disabled, per-request enabled', async () => {
|
||||
const axios = mockAxios({ enabled: false });
|
||||
|
||||
// First request with cache enabled
|
||||
const response1 = await axios.get('http://test.com', {
|
||||
cache: { enabled: true }
|
||||
});
|
||||
assert.equal(response1.cached, false);
|
||||
assert.equal(response1.stale, undefined);
|
||||
|
||||
// Second request with cache enabled - should be cached
|
||||
const response2 = await axios.get('http://test.com', {
|
||||
cache: { enabled: true }
|
||||
});
|
||||
assert.ok(response2.cached);
|
||||
assert.equal(response2.stale, false);
|
||||
|
||||
// Third request without cache config - should not be cached (global default)
|
||||
const response3 = await axios.get('http://test.com/other');
|
||||
const response4 = await axios.get('http://test.com/other');
|
||||
|
||||
assert.equal(response3.cached, false);
|
||||
assert.equal(response3.stale, undefined);
|
||||
assert.equal(response4.cached, false);
|
||||
assert.equal(response4.stale, undefined);
|
||||
});
|
||||
|
||||
it('Global cache enabled, per-request disabled', async () => {
|
||||
const axios = mockAxios({ enabled: true });
|
||||
|
||||
// First request with cache disabled
|
||||
const response1 = await axios.get('http://test.com', {
|
||||
cache: { enabled: false }
|
||||
});
|
||||
assert.equal(response1.cached, false);
|
||||
assert.equal(response1.stale, undefined);
|
||||
|
||||
// Second request with cache disabled - should not be cached
|
||||
const response2 = await axios.get('http://test.com', {
|
||||
cache: { enabled: false }
|
||||
});
|
||||
assert.equal(response2.cached, false);
|
||||
assert.equal(response2.stale, undefined);
|
||||
|
||||
// Third request without cache config - should be cached (global default)
|
||||
const response3 = await axios.get('http://test.com/other');
|
||||
const response4 = await axios.get('http://test.com/other');
|
||||
|
||||
assert.equal(response3.cached, false);
|
||||
assert.equal(response3.stale, undefined);
|
||||
assert.ok(response4.cached);
|
||||
assert.equal(response4.stale, false);
|
||||
});
|
||||
|
||||
it('Backward compatibility: cache: false still works', async () => {
|
||||
const axios = mockAxios();
|
||||
|
||||
const response1 = await axios.get('http://test.com', { cache: false });
|
||||
const response2 = await axios.get('http://test.com', { cache: false });
|
||||
|
||||
assert.equal(response1.cached, false);
|
||||
assert.equal(response1.stale, undefined);
|
||||
assert.equal(response2.cached, false);
|
||||
assert.equal(response2.stale, undefined);
|
||||
|
||||
// Verify cache is empty
|
||||
const cacheKey = axios.generateKey(response1.config);
|
||||
const cache = await axios.storage.get(cacheKey);
|
||||
assert.equal(cache.state, 'empty');
|
||||
});
|
||||
|
||||
it('Enabled flag works with other cache options', async () => {
|
||||
const axios = mockAxios({ enabled: false });
|
||||
|
||||
const response1 = await axios.get('http://test.com', {
|
||||
cache: {
|
||||
enabled: true,
|
||||
ttl: 1000 * 60 * 10 // 10 minutes
|
||||
}
|
||||
});
|
||||
assert.equal(response1.cached, false);
|
||||
assert.equal(response1.stale, undefined);
|
||||
|
||||
const response2 = await axios.get('http://test.com', {
|
||||
cache: {
|
||||
enabled: true,
|
||||
ttl: 1000 * 60 * 10
|
||||
}
|
||||
});
|
||||
assert.ok(response2.cached);
|
||||
assert.equal(response2.stale, false);
|
||||
|
||||
// Verify cache config is applied
|
||||
assert.equal(response2.config.cache?.ttl, 1000 * 60 * 10);
|
||||
});
|
||||
|
||||
it('Enabled flag overrides in request after global enabled', async () => {
|
||||
const axios = mockAxios({ enabled: true });
|
||||
|
||||
// Request 1 with enabled: true (explicit)
|
||||
const response1 = await axios.get('http://test.com', {
|
||||
cache: { enabled: true }
|
||||
});
|
||||
assert.equal(response1.cached, false);
|
||||
assert.equal(response1.stale, undefined);
|
||||
|
||||
// Request 2 - should be cached
|
||||
const response2 = await axios.get('http://test.com');
|
||||
assert.ok(response2.cached);
|
||||
assert.equal(response2.stale, false);
|
||||
|
||||
// Request 3 with enabled: false - should not be cached even though cache exists
|
||||
const response3 = await axios.get('http://test.com', {
|
||||
cache: { enabled: false }
|
||||
});
|
||||
assert.equal(response3.cached, false);
|
||||
assert.equal(response3.stale, undefined);
|
||||
});
|
||||
|
||||
it('Concurrent requests respect enabled flag', async () => {
|
||||
const axios = mockAxios({ enabled: false });
|
||||
|
||||
const [resp1, resp2] = await Promise.all([
|
||||
axios.get('http://test.com', { cache: { enabled: true } }),
|
||||
axios.get('http://test.com', { cache: { enabled: true } })
|
||||
]);
|
||||
|
||||
assert.equal(resp1.cached, false);
|
||||
assert.equal(resp1.stale, undefined);
|
||||
assert.ok(resp2.cached);
|
||||
assert.equal(resp2.stale, false);
|
||||
});
|
||||
|
||||
it('Mixed enabled and disabled requests do not interfere', async () => {
|
||||
const axios = mockAxios({ enabled: false });
|
||||
|
||||
// Disabled request
|
||||
const resp1 = await axios.get('http://test.com');
|
||||
assert.equal(resp1.cached, false);
|
||||
|
||||
// Enabled request - creates cache
|
||||
const resp2 = await axios.get('http://test.com', {
|
||||
cache: { enabled: true }
|
||||
});
|
||||
assert.equal(resp2.cached, false);
|
||||
|
||||
// Disabled request - does not use cache
|
||||
const resp3 = await axios.get('http://test.com');
|
||||
assert.equal(resp3.cached, false);
|
||||
|
||||
// Enabled request - uses cache
|
||||
const resp4 = await axios.get('http://test.com', {
|
||||
cache: { enabled: true }
|
||||
});
|
||||
assert.ok(resp4.cached);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user