Skip to Content
✨ New: Plugin Support. Extend the core functionality with ease. Read more... 🎉
Plugins NewCache Plugin

Cache Plugin

The official caching plugin for endpoint-fetcher. It provides intelligent, type-safe caching with support for TTL, LRU (Least Recently Used) eviction, and custom storage adapters.

Check out the code on GitHub 

Check out the package on NPM 

Installation

npm install @endpoint-fetcher/cache

Note: Requires endpoint-fetcher v3.0.0 or higher.

Quick Start

Add the cache plugin to your client and wrap your endpoint return types with CachingWrapper<T> to enable metadata and helper methods.

import { createApiClient, get } from 'endpoint-fetcher'; import { cache, CachingWrapper } from '@endpoint-fetcher/cache'; const api = createApiClient({ users: { endpoints: { // 1. Wrap the return type with CachingWrapper<T> getAll: get<void, CachingWrapper<User[]>>('/users'), } } }, { baseUrl: 'https://api.example.com', plugins: [ cache({ ttl: 300 }) // Global TTL: 5 minutes ] }); // Usage const result = await api.users.getAll(); console.log(result.data); // User[] console.log(result.isStale); // false console.log(result.expiresAt); // Date object

CachingWrapper<T>

When an endpoint is cached, the response object includes the following properties and methods to help you manage the data lifecycle.

Properties

PropertyTypeDescription
dataTThe actual API response data.
cachedAtDateTimestamp of the original network fetch.
expiresAtDateWhen the entry will be considered stale.
isStalebooleanHelper to check if the current time has passed the TTL.

Methods

  • refresh(): Forces a network re-fetch, updates the cache, and returns the new data.
  • invalidate(): Immediately removes this specific entry from the cache.
const result = await api.users.getAll(); // Force a network refresh const fresh = await result.refresh(); // Remove this specific entry from cache result.invalidate();

Configuration

The cache(config) function accepts the following options:

OptionTypeDefaultDescription
ttlnumber300Global time-to-live in seconds.
maxSizenumberInfinityMax number of entries before LRU eviction.
methodsstring[]['GET']HTTP methods to cache.
storageCacheStorageMemoryCustom storage implementation (e.g., localStorage).
keyGeneratorFunctionDefaultLogic to generate unique cache keys.

Custom Storage

You can persist cache across sessions by providing a custom storage adapter.

const api = createApiClient({...}, { plugins: [ cache({ ttl: 3600, storage: { get: (key) => JSON.parse(localStorage.getItem(key) || 'null'), set: (key, val) => localStorage.setItem(key, JSON.stringify(val)), delete: (key) => localStorage.removeItem(key), keys: () => Object.keys(localStorage), clear: () => localStorage.clear() } }) ] });

Cache Management

Global Cache Methods

The cache plugin exposes methods on client.plugins.cache for managing the entire cache:

import { createApiClient, get } from 'endpoint-fetcher'; import { cache, CachingWrapper } from '@endpoint-fetcher/cache'; const api = createApiClient({ users: { endpoints: { getAll: get<void, CachingWrapper<User[]>>('/users'), getById: get<{ id: string }, CachingWrapper<User>>((input) => `/users/${input.id}`), } } }, { baseUrl: 'https://api.example.com', plugins: [cache({ ttl: 300 })] }); // Clear the entire cache api.plugins.cache.clear(); // Invalidate a specific endpoint's cache api.plugins.cache.invalidate('GET', '/users', undefined); // Invalidate using a specific cache key api.plugins.cache.invalidateKey('GET:/users/123:{"id":"123"}');

Available Methods

MethodSignatureDescription
clear()() => voidClears all cache entries.
invalidate(method, path, input)(method: string, path: string, input: any) => voidInvalidates cache for a specific endpoint with given parameters.
invalidateKey(key)(key: string) => voidInvalidates cache using a specific cache key string.

Per-Entry Cache Management

Each cached response also includes methods for managing its own cache entry:

const result = await api.users.getAll(); // Force a network refresh for this specific endpoint const fresh = await result.refresh(); // Remove only this entry from cache result.invalidate();

Stale-While-Revalidate Pattern

Use the isStale flag to show cached data immediately while updating in the background.

const result = await api.users.getAll(); // Render cached data immediately render(result.data); // If stale, refresh in the background if (result.isStale) { result.refresh().then(fresh => render(fresh.data)); }

Common Cache Management Scenarios

Clear cache on logout

function logout() { // Clear user data api.plugins.cache.clear(); // Redirect to login window.location.href = '/login'; }

Invalidate specific user data after update

async function updateUser(userId: string, data: Partial<User>) { // Update the user await api.users.update({ id: userId, ...data }); // Invalidate the cached user data api.plugins.cache.invalidate('GET', `/users/${userId}`, { id: userId }); // Or invalidate the entire user list api.plugins.cache.invalidate('GET', '/users', undefined); }

Selective cache invalidation

// Clear only specific endpoints while keeping others api.plugins.cache.invalidate('GET', '/users', undefined); // Clear users list api.plugins.cache.invalidate('GET', '/posts', undefined); // Clear posts list // But keep other cached data intact // (e.g., /settings, /profile, etc.)

Troubleshooting

Data is never cached

  • Check the Type: Ensure you are using CachingWrapper<T>. If the return type is just T, the plugin will skip it.
  • Check the Method: By default, only GET requests are cached. Check your methods config if you need to cache POST requests.

Cache persists after refresh

  • The default storage is in-memory. If you are using a custom localStorage adapter, the cache will persist across page reloads unless ttl is reached or invalidate() is called.

How to invalidate cache programmatically

  • Use api.plugins.cache.clear() to clear all cache entries
  • Use api.plugins.cache.invalidate(method, path, input) to clear specific endpoints
  • Use result.invalidate() on individual responses to clear just that entry

Cache not being cleared

  • Make sure you’re calling the methods on the correct plugin instance: api.plugins.cache.clear()
  • If using custom storage, ensure your storage adapter’s clear() and delete() methods are working correctly