
Product
Introducing Repository Access Permissions and Custom Roles
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.
Wrap any function with defineCachedFunction to add caching with TTL, stale-while-revalidate, and request deduplication:
import { defineCachedFunction } from "ocache";
const cachedFetch = defineCachedFunction(
async (url: string) => {
const res = await fetch(url);
return res.json();
},
{
maxAge: 60, // Cache for 60 seconds
name: "api-fetch",
},
);
// First call hits the function, subsequent calls return cached result
const data = await cachedFetch("https://api.example.com/data");
const cached = defineCachedFunction(fn, {
name: "my-fn", // Cache key name (defaults to function name)
maxAge: 10, // TTL in seconds (default: 1)
swr: true, // Stale-while-revalidate (default: true)
staleMaxAge: 60, // Max seconds to serve stale content
base: "/cache", // Base prefix for cache keys (string or string[] for multi-tier)
group: "my-group", // Cache key group (default: "functions")
getKey: (...args) => "custom-key", // Custom cache key generator
shouldBypassCache: (...args) => false, // Skip cache entirely when true
shouldInvalidateCache: (...args) => false, // Force refresh when true
validate: (entry) => entry.value !== undefined, // Custom validation
transform: (entry) => entry.value, // Transform before returning
onError: (error) => console.error(error), // Error handler
});
Wrap HTTP handlers with defineCachedHandler for automatic response caching with etag, last-modified, and 304 Not Modified support:
import { defineCachedHandler } from "ocache";
const handler = defineCachedHandler(
async (event) => {
// event.req is a standard Request object
const url = event.url ?? new URL(event.req.url);
const data = await getExpensiveData(url.pathname);
return new Response(JSON.stringify(data), {
headers: { "content-type": "application/json" },
});
},
{
maxAge: 300, // Cache for 5 minutes
swr: true,
staleMaxAge: 600,
varies: ["accept-language"], // Vary cache by these headers
},
);
Use headersOnly to handle conditional requests without caching the full response:
const handler = defineCachedHandler(myHandler, {
headersOnly: true,
maxAge: 60,
});
Cached functions have an .invalidate() method that removes cached entries across all base prefixes:
import { defineCachedFunction } from "ocache";
const getUser = defineCachedFunction(async (id: string) => db.users.find(id), {
name: "getUser",
maxAge: 60,
getKey: (id: string) => id,
});
const user = await getUser("user-123");
// Invalidate a specific entry
await getUser.invalidate("user-123");
// Next call will re-invoke the function
const freshUser = await getUser("user-123");
You can also use the standalone invalidateCache() when you don't have a reference to the cached function — just pass the same options:
import { invalidateCache } from "ocache";
await invalidateCache({
options: { name: "getUser", getKey: (id: string) => id },
args: ["user-123"],
});
For advanced use cases, .resolveKeys() returns the raw storage keys:
const keys = await getUser.resolveKeys("user-123");
// ["/cache:functions:getUser:user-123.json"]
While .invalidate() removes an entry entirely (the next call must wait for a fresh value), .expire() only marks it as stale. With SWR enabled, stale values keep being served — still bounded by the originally configured staleMaxAge window — and the next access triggers a background refresh:
// Mark the entry stale: next call serves the stale value and refetches in the background
await getUser.expire("user-123");
The standalone expireCache() works like invalidateCache() — pass the same maxAge / swr / staleMaxAge options you cache with so the remaining storage TTL is preserved:
import { expireCache } from "ocache";
await expireCache({
options: { name: "getUser", getKey: (id: string) => id, maxAge: 60, staleMaxAge: 300 },
args: ["user-123"],
});
Use an array of base prefixes to enable multi-tier caching. On read, each prefix is tried in order and the first hit is used. On write, the entry is written to all prefixes:
const cachedFetch = defineCachedFunction(
async (url: string) => {
const res = await fetch(url);
return res.json();
},
{
maxAge: 60,
base: ["/tmp", "/cache"],
},
);
This is useful for layered cache setups (e.g., fast local cache + shared remote cache) where you want reads to prefer the nearest tier while keeping all tiers populated on writes.
By default, ocache uses an in-memory Map-based storage. You can provide a custom storage implementation:
import { setStorage } from "ocache";
import type { StorageInterface } from "ocache";
const redisStorage: StorageInterface = {
get: async (key) => {
return JSON.parse(await redis.get(key));
},
set: async (key, value, opts) => {
// Setting null/undefined deletes the entry (used for cache invalidation)
if (value === null || value === undefined) {
await redis.del(key);
return;
}
await redis.set(key, JSON.stringify(value), opts?.ttl ? { EX: opts.ttl } : undefined);
},
};
setStorage(redisStorage);
cachedFunctionconst cachedFunction = defineCachedFunction;
Alias for defineCachedFunction.
createMemoryStoragefunction createMemoryStorage(): StorageInterface;
Creates an in-memory storage backed by a Map with optional TTL support (in seconds).
defineCachedFunctionfunction defineCachedFunction<T, ArgsT extends unknown[] = any[]>(
fn: (...args: ArgsT) => T | Promise<T>,
opts: CacheOptions<T, ArgsT> =
Wraps a function with caching support including TTL, SWR, integrity checks, and request deduplication.
Parameters:
fn — The function to cache.opts — Cache configuration options.Returns: — A cached function with a .resolveKey(...args) method for cache key resolution.
defineCachedHandlerfunction defineCachedHandler<E extends HTTPEvent = HTTPEvent>(
handler: EventHandler<E>,
opts: CachedEventHandlerOptions<E> =
Wraps an HTTP event handler with response caching.
Automatically generates cache keys from the URL path and variable headers,
sets cache-control, etag, and last-modified headers, and handles
304 Not Modified responses via conditional request headers.
Parameters:
handler — The event handler to cache.opts — Cache and HTTP-specific configuration options.Returns: — A new event handler that serves cached responses when available.
EventHandlertype EventHandler<E extends HTTPEvent = HTTPEvent> = (
Handler function that receives an HTTPEvent and returns a response value.
expireCacheasync function expireCache<ArgsT extends unknown[] = any[]>(
input:
Expires cached entries for given arguments and cache options across all base prefixes, without removing them.
Unlike invalidateCache (which removes entries entirely), expired entries keep
serving the stale value with SWR — still bounded by the originally configured
staleMaxAge window — while the next access triggers a background refresh.
Without SWR, the next call re-resolves before returning.
Uses the same key derivation as defineCachedFunction / resolveCacheKeys.
Pass the same maxAge / swr / staleMaxAge options you cache with so the
remaining storage TTL is preserved.
Parameters:
input — Object with options (cache options) and optional args (function arguments).Example:
// Mark a cached entry for background refresh on next access
await expireCache({
options: { name: "fetchUser", getKey: (id: string) => id, maxAge: 60, staleMaxAge: 300 },
args: ["user-123"],
});
invalidateCacheasync function invalidateCache<ArgsT extends unknown[] = any[]>(
input:
Invalidates (removes) cached entries for given arguments and cache options across all base prefixes.
Uses the same key derivation as defineCachedFunction / resolveCacheKeys.
Parameters:
input — Object with options (cache options) and optional args (function arguments).Example:
// Invalidate a specific cached entry
await invalidateCache({
options: { name: "fetchUser", getKey: (id: string) => id },
args: ["user-123"],
});
resolveCacheKeysasync function resolveCacheKeys<ArgsT extends unknown[] = any[]>(
input:
Resolves all cache storage keys (one per base prefix) for given arguments and cache options.
Uses the same key derivation as defineCachedFunction internally:
opts.getKey is provided, it is called with args to produce the key segment.args are hashed with ohash (same default as defineCachedFunction).Pass the same getKey, name, group, and base options you use in
defineCachedFunction / defineCachedHandler to get the exact storage keys.
Parameters:
input — Object with options (cache options) and optional args (function arguments).Returns: — An array of storage key strings (one per base prefix).
Example:
const keys = await resolveCacheKeys({
options: { name: "fetchUser", getKey: (id: string) => id },
args: ["user-123"],
});
for (const key of keys) {
await useStorage().set(key, null); // invalidate all tiers
}
setStoragefunction setStorage(storage: StorageInterface): void;
Sets a custom storage implementation to be used by all cached functions.
useStoragefunction useStorage(): StorageInterface;
Returns the current storage instance. If none has been set via setStorage, lazily initializes an in-memory storage.
Published under the MIT license 💛.
FAQs
Standalone caching utilities with TTL, SWR, and HTTP response caching
The npm package ocache receives a total of 8,706,295 weekly downloads. As such, ocache popularity was classified as popular.
We found that ocache demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.

Product
Socket MCP now lets AI assistants review org alerts, investigate threats using the Socket threat feed, and inspect package files in addition to dependency scoring.

Product
Socket Firewall blocks malicious VS Code and Open VSX extensions before install, protecting developers from compromised editor marketplaces.