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

ocache under load: p99 latency with and without cache, and origin-call reduction, for seven workloads

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

GuideWhat it covers
Caching FunctionsOptions, cache keys, dynamic TTL, deduplication, custom serialization
StorageThe StorageInterface, custom backends, memory limits, multi-tier caches
Invalidation & ExpirationPurging entries, SWR-friendly expiry, standalone helpers
Caching HTTP HandlersResponse caching, conditional requests, request narrowing
Query ParametersWhich query params vary the cache
CookiesThe secure cookie default and how to opt back in
Cache-Control & EligibilityWhat is stored, what is advertised, response-side opt-outs
Incremental Static RegenerationServe stale instantly, regenerate in the background
API ReferenceGenerated reference for every export