Getting Started
ocache is a set of composable caching primitives with zero runtime dependencies. It runs anywhere the standard Request and Response APIs exist: Node.js, Bun, Deno, Cloudflare Workers, and other edge runtimes.
It exposes two building blocks:
defineCachedFunction— wraps any async function with TTL caching, stale-while-revalidate, integrity checks, and deduplication of concurrent calls.defineCachedHandler— wraps an HTTP handler with full response caching:etag,304 Not Modified,Cache-Control,Vary, and safe request narrowing.
#Installation
npm i ocache#Cache a function
import { defineCachedFunction } from "ocache";
const cachedFetch = defineCachedFunction(
async (url: string) => {
const res = await fetch(url);
return res.json();
},
{
name: "api-fetch",
maxAge: 60, // fresh for 60 seconds
},
);
// First call runs the function; calls within the next 60s return the cached result.
const data = await cachedFetch("https://api.example.com/data");#Cache an HTTP handler
import { defineCachedHandler } from "ocache";
const handler = defineCachedHandler(
async (event) => {
const url = event.url ?? new URL(event.req.url);
return Response.json(await loadData(url.pathname));
},
{
maxAge: 300, // fresh for 5 minutes
swr: true, // then serve stale while refreshing in the background
},
);
// The event shape is { req: Request, url?: URL } — no framework required.
const res = await handler({ req: new Request("https://example.com/data") });#Three things to know up front
All durations are in seconds.
maxAge, staleMaxAge, maxResolveTime, and storage TTLs all use seconds, never milliseconds.maxAge defaults to 1 second. Set it explicitly for anything you actually want cached.Storage is per instance. Each cached function and handler gets its own in-memory store by default. Nothing is global, so unrelated caches can never collide. Pass the same
storage to share a backend — Redis, filesystem, or anything with get/set.#Benchmarks
Seven realistic workloads under load, each run twice — once with ocache in the path and once without, from the same seed, so the only difference between the two marks on a row is the cache. Storage latency is simulated from real backend figures rather than set to zero, and the cost of a hit is measured rather than assumed. The benchmarks page has the method, every storage backend, and every number.
#Where to go next
| Guide | What it covers |
|---|---|
| Caching Functions | Options, cache keys, dynamic TTL, deduplication, custom serialization |
| Storage | The StorageInterface, custom backends, memory limits, multi-tier caches |
| Invalidation & Expiration | Purging entries, SWR-friendly expiry, standalone helpers |
| Caching HTTP Handlers | Response caching, conditional requests, request narrowing |
| Query Parameters | Which query params vary the cache |
| Cookies | The secure cookie default and how to opt back in |
| Cache-Control & Eligibility | What is stored, what is advertised, response-side opt-outs |
| Incremental Static Regeneration | Serve stale instantly, regenerate in the background |
| API Reference | Generated reference for every export |