Tag‑aware caching for Axios: stable cache IDs, simple tag invalidation, and a drop‑in Orval mutator on top of axios‑cache‑interceptor.
Tag-aware caching helpers for Axios on top of axios-cache-interceptor.
This library makes it easy to:
- Assign stable cache ids to GET-like requests.
- Group ids under tags and invalidate them after write-like requests.
- Share a cache-aware Axios instance across a project.
- Generate consistent id/tag keys from a declarative config shape.
- Plug into code generators such as Orval via a ready-to-use mutator.
npm i @karmaniverous/cached-axios axios axios-cache-interceptor zodRequires Node 20+.
import {
cachedAxios,
withQuery,
withMutation,
} from '@karmaniverous/cached-axios';
// GET with a stable id; associate id with tags for later invalidation.
await withQuery<User>(
cachedAxios.request,
'user:42', // your stable id
['user:list'], // tags to register
{ url: '/users/42', method: 'get', baseURL: 'https://api.example.com' },
);
// Write and invalidate any ids under these tags.
await withMutation(
cachedAxios.request,
['user:list'], // tags to invalidate
{
url: '/users/42',
method: 'patch',
data: { name: 'Alice' },
baseURL: 'https://api.example.com',
},
);The helpers forward any object-valued cache options from your base config and
set only what they need (custom id for queries; update map for mutations).
Pass cache: false on a call to disable caching for that request.
Declaratively define your keyspace, then use strongly-typed builders everywhere.
import { buildConfig } from '@karmaniverous/cached-axios';
const cfg = buildConfig({
user: {
byId: undefined,
list: undefined,
},
});
// Generate keys
const id = cfg.user.byId.id(42); // "user:byId:42"
const tag = cfg.user.list.tag(); // "user:list"Segments can be string | number | (string|number)[] | undefined, and are joined
with : to form keys.
import { makeCacheHelpers } from '@karmaniverous/cached-axios';
const { query, mutation } = makeCacheHelpers(() => ({
baseURL: 'https://api.example.com',
headers: { Authorization: 'Bearer <token>' },
}));
// Query
await query<User>(
cachedAxios.request,
cfg.user.byId.id(42),
[cfg.user.list.tag()],
{
url: '/users/42',
method: 'get',
},
);
// Mutation + invalidation
await mutation(cachedAxios.request, [cfg.user.list.tag()], {
url: '/users/42',
method: 'patch',
data: { name: 'Bob' },
});Use the provided mutator to keep generated clients cache-aware.
// orval.config.ts (or .js)
import { orvalMutator } from '@karmaniverous/cached-axios/mutators/orval';
export default {
petstore: {
input: './openapi.yaml',
output: {
target: './src/generated/client.ts',
client: 'axios',
mutator: {
path: '@karmaniverous/cached-axios/mutators/orval',
name: 'orvalMutator',
},
},
},
};Recommended path: '@karmaniverous/cached-axios/mutators/orval' for generators.
For manual imports, a convenience barrel is available at
'@karmaniverous/cached-axios/mutators'. The root export remains available if
you prefer: import { orvalMutator } from '@karmaniverous/cached-axios'.
The orvalMutator<T, R>(config, options?) shallow-merges options over config
and always resolves to AxiosResponse<T>. It executes via the same shared
cache-aware Axios instance exported as cachedAxios.
import { cachedAxios } from '@karmaniverous/cached-axios';Defaults:
interpretHeader: true— honor HTTP caching headers.staleIfError: true— serve cached data when revalidation fails.ttl: 5 minutes— fallback TTL if headers don’t specify caching.
No baseURL is set; pass baseURL/headers per request to keep parallel calls
safe against multiple backends.
withQuerysets a custom cacheidand registers it under provided tags in an in-memory index.withMutationbuilds an ACIupdatemap ({[id]: 'delete'}) for all ids currently associated with the given tags and clears those tag buckets.- The tag index is process-local and not persisted or shared between processes. For distributed invalidation, prefer server-driven cache headers or a shared cache layer (e.g., Redis) integrated with your backend.
cachedAxios: ACI-wrapped Axios instance.withQuery(call, id, tags, base?): GET-like helper with stable id + tag registration.withMutation(call, invalidate, base?): write-like helper with tag-based invalidation.makeCacheHelpers(base?)→{ query, mutation }bound to a base config.buildConfig(shape)→ nested object withid(seg?)andtag(seg?)at every node.orvalMutator<T, R>(config, options?)→ AxiosResponse viacachedAxios.- Types:
- Branded
IdandTag, BuiltNode,ConfigInput,- Augmented Axios/ACI types re-exported from the package.
- Branded
await withQuery(cachedAxios.request, 'user:42', ['user:list'], {
url: '/users/42',
method: 'get',
cache: false,
});AxiosRequestConfig['cache']is augmented to acceptfalse | Partial<CacheProperties>.AxiosResponseexposescached?: booleanandprevious?: AxiosResponse.CacheProperties(from ACI) is extended withid?: string,etag?: string, andupdate?: Record<string, 'delete'>.
- API docs are generated by TypeDoc (
npm run docs). - Tests:
npm test(Vitest with coverage). - Linting/formatting:
npm run lint.
Built for you with ❤️ on Bali! Find more great tools & templates on my GitHub Profile.