Standalone Caching Utilities
ocache is a JavaScript/TypeScript library providing robust caching utilities for functions and HTTP handlers. It supports common caching strategies such as time-to-live (TTL), stale-while-revalidate (SWR), and multi-tier caching. Currently at version 0.1.4, it is under active development by the unjs organization, suggesting a focus on modularity and integration within their ecosystem, although it is designed to be standalone. Key differentiators include built-in request deduplication for cached functions, automatic HTTP response caching with `etag` and `last-modified` support, and a flexible multi-tier caching mechanism that allows for layered cache storage. The project appears to follow a rapid release cadence for minor enhancements and performance improvements, typical for libraries in their early 0.x development phase, with type definitions included for TypeScript users.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'headers') when using defineCachedHandler
cause The `event` object passed to the handler does not contain a standard `req` property or `req` is not a valid `Request` object with headers.fixEnsure that the environment in which `defineCachedHandler` is used (e.g., a serverless function context) provides an event object compatible with standard `Request` objects, specifically `event.req.headers`. You might need to adapt your handler to parse headers from a different property or format. -
Cache entry not found or always re-fetching despite maxAge being set.
cause The cache key is not being generated consistently, or the `maxAge` is too low, or `shouldBypassCache` is inadvertently returning `true`.fixCheck your `getKey` function (if custom) for consistency. Increase `maxAge` for testing. Verify `shouldBypassCache` option to ensure it's not unintentionally skipping the cache. Also, ensure that the cache storage is correctly configured and accessible. -
SyntaxError: Named export 'defineCachedFunction' not found. The requested module 'ocache' does not provide an export named 'defineCachedFunction'
cause Attempting to use `require()` for an ESM-only package or an environment that doesn't correctly resolve ESM imports.fixEnsure your project is configured for ESM (e.g., `"type": "module"` in `package.json`) and use `import { defineCachedFunction } from 'ocache';`. If you must use CommonJS, consider transpilation or using a bundler that handles ESM conversion.
Warnings
- gotcha ocache is currently in `0.x` versioning, which implies that breaking changes could be introduced in minor releases without strict adherence to semantic versioning until a `1.0.0` release. Users should carefully review release notes for `0.x` updates.
- gotcha When using `defineCachedFunction`, ensure your `getKey` function (if custom) consistently returns a unique key for distinct inputs. Incorrect key generation will lead to unintended cache hits or misses.
- breaking The `staleMaxAge` option was introduced and refined in `v0.1.3`. For users upgrading from earlier `0.1.x` versions, the behavior related to serving stale content while revalidating might change or become active for the first time.
- gotcha Multi-tier caching (`base: string[]`) processes reads by trying each prefix in order and using the first hit, but writes to *all* prefixes. This can lead to increased write overhead and potential inconsistencies if tiers are not properly managed or if a write fails on one tier.
- gotcha HTTP handler caching with `defineCachedHandler` relies on `event.req` being a standard Request object or similar. In non-standard environments, the `etag` and `last-modified` headers might not behave as expected or could require polyfills/adaptations.
Install
-
npm install ocache -
yarn add ocache -
pnpm add ocache
Imports
- defineCachedFunction
const defineCachedFunction = require('ocache').defineCachedFunction;import { defineCachedFunction } from 'ocache'; - defineCachedHandler
import defineCachedHandler from 'ocache/handler';
import { defineCachedHandler } from 'ocache'; - invalidateCache
import { invalidate } from 'ocache';import { invalidateCache } from 'ocache';
Quickstart
import { defineCachedFunction } from "ocache";
const cachedFetch = defineCachedFunction(
async (url: string) => {
console.log(`Fetching data for: ${url}`);
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP error! Status: ${res.status}`);
}
return res.json();
},
{
maxAge: 10, // Cache for 10 seconds
name: "api-fetch", // Identifier for cache keys
swr: true, // Enable stale-while-revalidate
staleMaxAge: 60 // Serve stale content for up to 60 seconds while revalidating
},
);
async function runExample() {
console.log('--- First call ---');
const data1 = await cachedFetch("https://jsonplaceholder.typicode.com/todos/1");
console.log('Data 1:', data1.title);
console.log('\n--- Second call (should be cached) ---');
const data2 = await cachedFetch("https://jsonplaceholder.typicode.com/todos/1");
console.log('Data 2:', data2.title);
// Wait for cache to become stale but still valid for SWR
console.log('\n--- Waiting for cache to expire... ---');
await new Promise(resolve => setTimeout(resolve, 11 * 1000));
console.log('\n--- Third call (should trigger revalidation and serve stale first) ---');
const data3 = await cachedFetch("https://jsonplaceholder.typicode.com/todos/1");
console.log('Data 3 (potentially stale, revalidating in background):', data3.title);
// Invalidate specific cache entry
console.log('\n--- Invalidating cache for todos/1 ---');
await cachedFetch.invalidate("https://jsonplaceholder.typicode.com/todos/1");
console.log('\n--- Fourth call (after invalidation, should re-fetch) ---');
const data4 = await cachedFetch("https://jsonplaceholder.typicode.com/todos/1");
console.log('Data 4:', data4.title);
}
runExample().catch(console.error);