| name | gorgon-cache |
| description | How to use the @gorgonjs/gorgon caching library for async functions in TypeScript/JavaScript. Use this skill whenever the user wants to cache API calls, database queries, or any async operation; when they mention Gorgon, @gorgonjs, or ask about caching promises/async functions; when they need help with cache invalidation, expiry policies, custom storage providers, or React cache hooks; or when they're building a Node.js or browser app that would benefit from request deduplication or caching. Also trigger when the user is working in a project that already imports @gorgonjs/gorgon and needs guidance. |
Gorgon.js Caching Library
Gorgon is a lightweight (~4kb, ~1.3kb gzipped) TypeScript caching library for async functions. It works in both Node.js and browsers. Its key differentiator is automatic concurrency protection — multiple simultaneous requests for the same cache key are deduplicated, with all callers receiving the same result.
Installation
npm install @gorgonjs/gorgon
Core Concepts
- Cache key naming: Use the format
cachetype/{id}/{sub-id} — this enables wildcard clearing (e.g. Gorgon.clear('user/*'))
- Concurrency protection: If 10 requests hit
Gorgon.get('key', fn) simultaneously, fn only executes once. The other 9 wait and share the result.
- Errors are never cached: If the async function throws, all waiting callers get the error, but the next request retries fresh.
- Expiry options: milliseconds (
60000), a Date object, false (cache forever), or omit for no expiry.
API Reference
Gorgon.get<R>(key, asyncFunc, policy?): Promise<R>
The primary method — fetches from cache or executes the function and caches the result.
import Gorgon from '@gorgonjs/gorgon';
const user = await Gorgon.get(`user/${id}`, async () => {
const res = await fetch(`/api/users/${id}`);
return res.json();
}, 60 * 1000);
Gorgon.put<R>(key, value, policy?): Promise<R>
Directly insert a value into cache.
await Gorgon.put(`user/${id}`, userData, 60 * 1000);
Gorgon.clear(key, provider?): Promise<boolean | boolean[]>
Clear a cache key. Supports wildcard patterns with *.
Gorgon.clear(`user/${id}`);
Gorgon.clear(`user/*`);
Gorgon.clearAll(provider?): Promise<boolean>
Clear all cached data.
Gorgon.overwrite<R>(key, asyncFunc, policy?): Promise<R>
Force-refresh a cache key. Unlike get, this always executes the function. Does NOT have concurrency protection.
const updated = await Gorgon.overwrite(`user/${id}`, async () => {
return fetch(`/api/users/${id}`).then(r => r.json());
}, 60 * 1000);
Gorgon.settings(newSettings?): GorgonSettings
Configure global defaults.
Gorgon.settings({
debug: true,
defaultProvider: 'memory',
retry: 5000
});
Gorgon.addProvider(name, provider): void
Register a custom storage backend.
import Gorgon from '@gorgonjs/gorgon';
import { FileProvider } from '@gorgonjs/file-provider';
const fileCache = FileProvider('./cache', { createSubfolder: false });
Gorgon.addProvider('file', fileCache);
Gorgon.settings({ defaultProvider: 'file' });
Gorgon.addHook(event, callback): void
Listen to cache lifecycle events. Events: settings, addProvider, put, clear, clearAll, overwrite, get, valueError.
Gorgon.addHook('clear', (key, input, output) => {
console.log('Cache cleared:', input);
});
Policies
Policies control expiry and which provider to use:
Gorgon.get('key', fn, 60000);
Gorgon.get('key', fn, new Date('2026-12-31'));
Gorgon.get('key', fn, false);
Gorgon.get('key', fn, { expiry: 60000, provider: 'file' });
Common Patterns
Query collocation — keep fetch logic next to where it's used
import Gorgon from '@gorgonjs/gorgon';
export const getTodo = (id: number) =>
Gorgon.get(`todo/${id}`, async () => {
const res = await fetch(`https://api.example.com/todos/${id}`);
return res.json();
}, 60 * 1000);
Cache with file persistence (server-side)
import Gorgon from '@gorgonjs/gorgon';
import { FileProvider } from '@gorgonjs/file-provider';
const fileCache = FileProvider('./cache', { createSubfolder: false });
Gorgon.addProvider('perm', fileCache);
const movie = await Gorgon.get(`movie/${id}`, async () => {
return fetch(`https://api.themoviedb.org/3/movie/${id}`).then(r => r.json());
}, { provider: 'perm', expiry: false });
Error handling
try {
const data = await Gorgon.get('flaky-api', async () => {
const res = await fetch('/api/flaky');
if (!res.ok) throw new Error(`API error: ${res.status}`);
return res.json();
}, 30000);
} catch (e) {
console.error(e);
}
Common Mistakes
CRITICAL: Not clearing cache after mutating underlying data
const user = await Gorgon.get(`user/${id}`, () => fetchUser(id), 60000);
await updateUser(id, newData);
const user = await Gorgon.get(`user/${id}`, () => fetchUser(id), 60000);
await updateUser(id, newData);
Gorgon.clear(`user/${id}`);
Gorgon does not know when the underlying data changes. Always clear the relevant cache key after any mutation that affects cached data.
CRITICAL: Cache keys not specific enough to input parameters
const results = await Gorgon.get('search-results', () =>
searchAPI(query, page, filters), 60000);
const results = await Gorgon.get(
`search/${query}/${page}/${JSON.stringify(filters)}`,
() => searchAPI(query, page, filters), 60000);
If the cache key does not include all varying parameters, different requests share the same cached result, returning wrong data silently.
HIGH: Caching forever without expiry in production
await Gorgon.get('config', fetchConfig, false);
await Gorgon.get('config', fetchConfig, 24 * 60 * 60 * 1000);
Even rarely-changing data should have an expiry. Permanent caches become stale silently and are hard to debug.
MEDIUM: Aligned cache expiry causing thundering herd
for (const id of popularIds) {
await Gorgon.get(`item/${id}`, () => fetchItem(id), 3600000);
}
for (const id of popularIds) {
const fuzz = Math.random() * 600000;
await Gorgon.get(`item/${id}`, () => fetchItem(id), 3600000 + fuzz);
}
When warming caches for many popular items, fuzz the expiry times so they don't all expire at once causing a burst of requests.
HIGH: Using a plain Map instead of Gorgon
A hand-rolled Map-based cache misses concurrency deduplication (10 simultaneous calls hit the API 10 times), has no expiry management, no wildcard clearing, and no type safety on cached returns. Use Gorgon.get instead.
MEDIUM: Wrapping Gorgon.get in custom deduplication
Gorgon already deduplicates concurrent requests for the same key. Adding external dedup logic is redundant and can introduce bugs.
Reference Files
references/react.md — React hooks (useGorgon) and integration patterns
references/providers-and-plugins.md — Custom provider interface, file provider, ClearLink plugin
Read the appropriate reference file when the user's question involves those topics.