Skip to content

Latest commit

 

History

History
431 lines (346 loc) · 22.7 KB

api-reference.md

File metadata and controls

431 lines (346 loc) · 22.7 KB

API Reference

Everything the package exports. Start with withCache; the rest is for custom handler files, operations and the legacy data cache API. Behaviour (outages, tag coordination) is described in How it works, and the key layout in Redis Configuration.

Exports

Export Kind Purpose
withCache(nextConfig, options?) function Configures the whole cache. Below.
createPageCacheHandler(options?) function → class The page handler with code-level options, for a custom cacheHandler file.
RedisCacheHandler class The page handler. Same constructor as 16.4.2, so subclasses still work.
createRemoteCacheHandler(options?) function → DataCacheHandler Redis data handler plus memory fallback, for "use cache: remote".
createLocalCacheHandler(options?) function → DataCacheHandler Memory data handler with shared tag invalidation, for "use cache".
flushCache(options?) async function Deletes this app's keys. Flushing.
closeCache() async function Closes the package's Redis connections so a test run or script can exit. Closing.
setCacheLogger(logger) function Sends the package's log lines to your logger. Logging.
attachElastiCacheIamAuth(client, options?) function Keeps your own Redis or Cluster authenticated to ElastiCache with refreshed IAM tokens. Below.
createCacheHandler({ type }) function → DataCacheHandler Deprecated: the original data handler factory (legacy data cache API).
createRedisDataCacheHandler, createMemoryDataCacheHandler, createIoredisAdapter functions Deprecated: the original building blocks behind createCacheHandler.
CacheOptions, CacheLogger, ElastiCacheIamOptions, WithCacheOptions, FlushCacheOptions, FlushCacheResult, PageCacheHandlerOptions, RemoteCacheHandlerOptions, LocalCacheHandlerOptions, DataCacheHandler, DataCacheEntry, PageCache* types

The deprecated exports are still exported.

withCache(nextConfig, options?)

function withCache<T extends object>(
  nextConfig: T,
  options?: WithCacheOptions,
): T & WithCacheConfig;
function withCache<T extends object>(
  nextConfig: (phase: string, context: { defaultConfig: object }) => T | Promise<T>,
  options?: WithCacheOptions,
): (phase: string, context: { defaultConfig: object }) => Promise<T & WithCacheConfig>;

interface WithCacheOptions {
  /** Set cacheHandler and cacheMaxMemorySize: 0. @default true */
  pages?: boolean;
  /** Storage for plain "use cache": "redis" shares values across instances. @default "memory" */
  useCache?: "memory" | "redis";
  /** Your own handler files. Relative paths resolve against process.cwd(). */
  handlers?: { page?: string; default?: string; remote?: string };
  /**
   * In-process cache budget: heapPercent of the V8 heap limit, or maxMiB (wins).
   * Wins over CACHE_MEMORY_HEAP_PERCENT / CACHE_MEMORY_MAX_MIB. @default { heapPercent: 10 }
   */
  memory?: { heapPercent?: number; maxMiB?: number };
}

"use cache" always goes to cacheHandlers.default and "use cache: remote" to cacheHandlers.remote, as Next routes them; "use cache: private" never reaches a handler. withCache does not check which flag turned "use cache" on, so cacheComponents: true and Next's deprecated experimental.useCache: true both work.

Returns nextConfig plus:

Key Value
cacheHandler handlers.page, or the built-in page handler. Omitted with pages: false.
cacheHandlers.remote handlers.remote, or the built-in remote handler.
cacheHandlers.default handlers.default, or the built-in local handler (memory), or with useCache: "redis" the built-in remote handler, which falls back to memory without Redis.
cacheMaxMemorySize 0, unless nextConfig sets it: then that value is kept, with a warning if it is not 0 (it has no effect once withCache sets the handlers). Omitted with pages: false.

Throws if nextConfig already sets cacheHandlers, or cacheHandler unless pages: false (then yours is kept), and if memory has a heapPercent outside (0, 100), a maxMiB that is not a finite number above 0, a useCache other than "memory" or "redis", or any other key. memory adds no key to the config: it is handed to the handlers through the environment of the process that loaded next.config (next dev, next start), so a standalone server.js does not see it; withCache warns when output: "standalone" is set with it. See Memory. A config function is wrapped in an async one that applies the same settings to its result.

The built-in handlers read the environment at runtime.

CacheOptions

Shared by every factory below and by flushCache. Each defaults to the environment, so a factory called with no options behaves like the built-in handler. The table form, with notes, is in Custom handlers.

interface CacheOptions {
  /** @default REDIS_URL ?? VALKEY_URL ?? the ELASTICACHE_* env; unset → memory only */
  url?: string;
  /** Your own ioredis Redis or Cluster. Overrides url. Never closed or reconfigured. */
  client?: Redis | Cluster;
  /** @default CACHE_KEY_PREFIX ?? "nextjs:" */
  keyPrefix?: string;
  /** Serve from memory (each entry at most 60 s) while Redis is down. @default true */
  memoryFallback?: boolean;
  /**
   * Per-command timeout in ms. On a client you pass in, it bounds the package's own commands
   * through a proxy; 3 timeouts in a row → memory for 2 s. @default 1000
   */
  commandTimeout?: number;
  /**
   * Ms to wait for a connection the package opens. Not used with client.
   * @default 2000 (10000 with the ELASTICACHE_* host form)
   */
  connectTimeout?: number;
  /** Ms a pending command may wait for a reply before an owned connection is reopened. @default 2000 */
  socketTimeout?: number;
  /** Timeouts in a row that reopen an owned connection, or send a passed client to memory for 2 s. @default 3 */
  maxConsecutiveTimeouts?: number;
  /**
   * ElastiCache IAM authentication for the connection the package opens: { userId, cacheName,
   * region, serverless, credentials }, each defaulting to the ELASTICACHE_* / AWS_REGION env.
   * Throws with client. @default IAM when ELASTICACHE_IAM_USER is set
   */
  iam?: ElastiCacheIamOptions;
  /** @default CACHE_DEBUG is `1` or `true` */
  debug?: boolean;
  /** Where this handler's warnings, errors and debug lines go. @default setCacheLogger's, else the console */
  logger?: CacheLogger;
}

Handlers in one process with the same connection and keyPrefix share one connection, one tag store and one set of memory stores. CACHE_REDIS=off and next build force memory regardless of these options. Different timeouts for the same server open a second connection, and the package logs a warning when that happens, so give every handler the same settings.

Page handler

createPageCacheHandler(options?)

function createPageCacheHandler(options?: PageCacheHandlerOptions): typeof RedisCacheHandler;

Returns a RedisCacheHandler subclass with options applied. Export it as the default of a custom cacheHandler file:

// cache/page.mjs
import { createPageCacheHandler } from "@mrjasonroy/cache-components-cache-handler";
import { client } from "./redis.mjs";

export default createPageCacheHandler({ client });

RedisCacheHandler

The class Next constructs for the singular cacheHandler. It implements get, set, revalidateTag, resetRequestCache, delete and close. get reads the entry and the tag records that apply to it in one round trip (1 GET + 1 HMGET of the <prefix>tags hash), so a revalidation on any instance is seen by the next read. The first read of an entry by a process that did not write it takes one more HMGET, for the tags it learns from the entry. See Sizing.

PageCacheHandlerOptions (16.4.2's RedisCacheHandlerOptions, still exported as a deprecated alias until 16.5) is Next's constructor context plus CacheOptions and:

Option Default Meaning
buildId the app's deployment ID if set, else its BUILD_ID The build that scopes entry keys.
defaultTTL 31536000 (365 days) Lifetime in seconds of an entry with neither revalidate nor expire, at most 365 days.
redis — Deprecated (removed in 16.5): the 16.4.2 form of client / url, an ioredis client, a URL or an ioredis options object. Use client or url.
tagPrefix — Deprecated and ignored. Tag records live in the <keyPrefix>tags hash.

The constructor has the same shape as in 16.4.2, so subclasses keep working. close() is deprecated: it is the same as closeCache().

Data handlers

Both return a Next DataCacheHandler (get, set, refreshTags, getExpiration, updateTags). Export the result as the default of a cacheHandlers file.

createRemoteCacheHandler(options?)

For "use cache: remote". Entries are stored in Redis under <prefix><buildId>:data:<key> until their expire, with the memory fallback during an outage. Takes CacheOptions plus buildId and defaultTTL: the seconds to keep an entry whose expire is infinite (default 86400, 24 hours; anything but a finite number greater than 0 throws when the handler is created). Every entry is kept at most 365 days.

createLocalCacheHandler(options?)

For "use cache". Values live in this process's memory until their revalidate period: up to 10,000 entries and 32/98 of the memory budget (about 27 MiB at --max-old-space-size=768 on Node 22). A larger value is not kept. When Redis is configured, tag invalidations are shared across instances. Takes CacheOptions; only the connection, keyPrefix, debug and logger matter.

// cache/default.mjs
import { createLocalCacheHandler } from "@mrjasonroy/cache-components-cache-handler";
import { client } from "./redis.mjs";

export default createLocalCacheHandler({ client });

flushCache(options?)

function flushCache(options?: FlushCacheOptions): Promise<FlushCacheResult>;

interface FlushCacheOptions extends CacheOptions {
  /** "build": <prefix><buildId>:*. "all": every key under the prefix. @default "build" */
  scope?: "build" | "all";
  /** @default the app's deployment ID if set, else its BUILD_ID */
  buildId?: string;
}

interface FlushCacheResult {
  /** Redis keys deleted; without Redis, in-process entries dropped. */
  deleted: number;
  /** Set when some matching keys could not be deleted (a Cluster slot migration). Run it again. */
  partial?: true;
  /** How many matching keys could not be deleted; set with partial. */
  failed?: number;
}

Deletes with SCAN + UNLINK on every master of a Cluster, and empties this process's memory stores. deleted is the number of Redis keys deleted, or, without Redis, the number of memory entries dropped. It throws, deleting nothing, if Redis is configured but unavailable, or for scope: "all" with an empty keyPrefix. It also throws if a Redis node stops answering (commandTimeout, also on each Cluster master) or refuses part-way: some keys may already be deleted, so run it again. After the delete it writes a tag record for the flush (__cch_flush__, or __cch_flush__:<buildId> for scope: "build") that prerendered build output is checked against, so build output older than the flush is not served again. See Flushing the cache.

closeCache()

function closeCache(): Promise<void>;

For test runs, scripts and CLIs, so Node can exit; a Next server never needs it. Closes every Redis connection the package opened in this process and empties its memory stores. A client you passed in is never closed. It never rejects because Redis is down, and is safe to call more than once. Handlers created before it reconnect on their next call. The details, including what happens to revalidations in flight, are under Closing the cache.

import { afterAll } from "vitest";
import { closeCache } from "@mrjasonroy/cache-components-cache-handler";

afterAll(() => closeCache());

setCacheLogger(logger)

interface CacheLogger {
  error(message: string, meta?: Record<string, unknown>): void;
  warn(message: string, meta?: Record<string, unknown>): void;
  info?(message: string, meta?: Record<string, unknown>): void;
  debug?(message: string, meta?: Record<string, unknown>): void;
}
function setCacheLogger(logger: CacheLogger | undefined): void;

Sends the package's warnings, errors and CACHE_DEBUG lines to logger for the whole process (every module graph), or back to the console with undefined. Call it in instrumentation.ts's register() or a custom handler file. The logger option of createRemoteCacheHandler, createLocalCacheHandler and createPageCacheHandler wins over it for that handler; the shared connection and tag state use the logger of the handler that opened them. message is what the console would print; meta holds the details (error, operation). Without info or debug those lines are dropped. Errors stay rate-limited. No line carries a Redis URL or connection options; errors, their causes, objects and URLs are scrubbed of credentials, but other free text in a message is not. A logger that throws never fails a cache call: the line goes to the console.

attachElastiCacheIamAuth(client, options?)

function attachElastiCacheIamAuth(
  client: Redis | Cluster,
  options?: ElastiCacheIamOptions & { logger?: CacheLogger },
): { refresh(): Promise<boolean>; stop(): void };

For a client you build yourself, such as a Cluster for cluster-mode-enabled ElastiCache. Create the client with lazyConnect: true and TLS, and do not connect it. Then this:

  • connects it once the first IAM token is in its options;
  • makes a new token every 10 minutes, puts it in the options for reconnects (every node's options on a Cluster) and sends AUTH with it on every connected node.

Options left out come from the environment, as in IAM authentication. Throws when IAM cannot work (TLS off, no user, cache name or region) or the AWS packages are not installed. refresh() makes a new token now; stop() ends the refresh and leaves the client connected. Call it once per client: IAM on your own client has the full handler-file example.

Legacy data cache API

The data cache API of 16.4.2 and earlier. Deprecated in 16.4.3: it keeps working in 16.4.x, logs a one-time deprecation warning on first use, and is removed in 16.5. Use withCache, or createRemoteCacheHandler / createLocalCacheHandler. createCacheHandler keeps its own key layout (nextjs:data-cache:, nextjs:tags:) and covers "use cache" only, not pages.

createCacheHandler(options)

// data-cache-handler.mjs
import { createCacheHandler } from "@mrjasonroy/cache-components-cache-handler";

export default createCacheHandler({ type: process.env.REDIS_URL ? "redis" : "memory" });

type is "memory", "redis", "valkey" or "elasticache". The Redis types share the process's connection, fall back to memory during an outage, and use memory during next build and with CACHE_REDIS=off.

The 16.4.2 wiring still works: a handler file that exports createCacheHandler({ type }), wired with createCacheConfig or by hand into cacheHandlers. In production it logs a one-time reminder to use withCache; pass suppressIsrWarning: true to silence it.

createCacheHandler reads REDIS_URL / VALKEY_URL, REDIS_PASSWORD and the ELASTICACHE_* variables, and honours CACHE_REDIS=off and the build phase. It does not read CACHE_KEY_PREFIX or CACHE_DEBUG. Its options:

Option Default What it does
url REDIS_URL / VALKEY_URL, else redis://localhost:6379 Connection URL (redis, valkey)
endpoint / port ELASTICACHE_ENDPOINT / ELASTICACHE_PORT, 6379 Host and port (elasticache)
tls true for elasticache, else false Use TLS
password ELASTICACHE_AUTH_TOKEN / REDIS_PASSWORD Auth token or password
client — Your own ioredis Redis or Cluster; you own its lifecycle
keyPrefix / tagPrefix "nextjs:data-cache:" / "nextjs:tags:" Key prefixes
commandTimeout 1000 (ms) Per-command timeout
connectTimeout 2000 (ms; 10000 for elasticache) Connect timeout
socketTimeout 2000 (ms) Drop the connection when a pending command gets no reply for this long
maxConsecutiveTimeouts 3 Timeouts in a row before the connection is dropped and redialled (with client: before it is marked not ready for 2 s)
memoryFallback / fallbackTTL true / 60 (s) Serve from memory during an outage, each entry for at most fallbackTTL
defaultTTL 86400 (s) TTL for entries without a finite expire
tagTTL 31622400 (s, 366 days) TTL for tag records; entry TTLs are capped at it
errorLogIntervalMs 60000 Redis errors are logged at most once per operation per interval
suppressIsrWarning false Silence the one-time production reminder
maxSize 100 MB Memory budget (memory)
debug false Debug logging

createMemoryDataCacheHandler(options?)

A memory data handler. Options: maxSize in bytes (default 50 MB) and debug.

createRedisDataCacheHandler(options)

The Redis data handler behind createCacheHandler, without the shared connection or the fallback. Options: redis (a node-redis-style client, required), keyPrefix ("nextjs:data-cache:"), tagPrefix ("nextjs:tags:"), defaultTTL (86400), tagTTL (366 days), errorLogIntervalMs (60000) and debug.

createIoredisAdapter(client)

Wraps an ioredis Redis or Cluster so createRedisDataCacheHandler can use it. It translates node-redis SET options ({ EX: seconds }) to ioredis positional arguments. Without it, Redis rejects writes with ERR syntax error. The new handlers take ioredis directly and don't need it.

import Redis from "ioredis";
import {
  createIoredisAdapter,
  createRedisDataCacheHandler,
} from "@mrjasonroy/cache-components-cache-handler";

export default createRedisDataCacheHandler({
  redis: createIoredisAdapter(new Redis(process.env.REDIS_URL)),
});

Deprecated

Deprecated in 16.4.3, with JSDoc pointing at the replacement. They keep working in 16.4.x and log a one-time deprecation warning (once per process, per API) on first use. They are removed in 16.5.

Deprecated Use instead Why
createCacheHandler withCache, or createRemoteCacheHandler / createLocalCacheHandler in a custom handler The original data stack: "use cache" only, its own key layout, and no page handler. It does not reconnect after closeCache(): create it again.
createRedisDataCacheHandler, createIoredisAdapter createRemoteCacheHandler({ client }) It takes an ioredis client directly; no adapter.
createMemoryDataCacheHandler createLocalCacheHandler Memory with tag invalidation shared across instances.
RedisCacheHandler#close() closeCache() The same, without needing a handler.
The page handler's redis and tagPrefix options, and a defaultTTL that is not a number client or url; nothing for tagPrefix; a number of seconds 16.4.2 forms. tagPrefix is ignored: tag records live in the <keyPrefix>tags hash.
RedisCacheHandlerOptions PageCacheHandlerOptions Renamed with createPageCacheHandler; the same type.
createCacheConfig, createCacheConfigWithProfiles withCache Both make you write and wire handler files yourself.
CompositeHandler, createCompositeHandler built in The memory fallback is now inside every handler.
MemoryCacheHandler, createMemoryCacheHandler withCache without REDIS_URL Memory only is the no-URL mode.
bufferToString, stringToBuffer, jsonReplacer, jsonReviver, isImplicitTag, calculateLifespan, isExpired, IMPLICIT_TAG_PREFIX, and the Next 14 CacheHandler* / CacheValue / LifespanParameters types none Internals of the Next 14 handlers.

RedisCacheHandler is not deprecated. It is the page handler withCache uses.

createCacheConfig(options) / createCacheConfigWithProfiles(options)

Use withCache. They return { cacheHandlers, cacheMaxMemorySize } for handler files you write yourself:

  • createCacheConfig({ dataCacheHandlerPath, isrHandlerPath?, disableDefaultMemoryCache? }) points default and remote at one file.
  • createCacheConfigWithProfiles({ defaultDataCacheHandlerPath, remoteDataCacheHandlerPath, isrHandlerPath?, disableDefaultMemoryCache? }) takes one file per profile.

isrHandlerPath is passed through as cacheHandler. disableDefaultMemoryCache defaults to true (cacheMaxMemorySize: 0).

Next 14 handlers and helpers

MemoryCacheHandler, createMemoryCacheHandler, CompositeHandler, createCompositeHandler, bufferToString, stringToBuffer, jsonReplacer, jsonReviver, isImplicitTag, calculateLifespan, isExpired, IMPLICIT_TAG_PREFIX, and the types CacheHandler, CacheHandlerContext, CacheHandlerGetMeta, CacheHandlerOptions, CacheHandlerValue, CacheValue and LifespanParameters. See the Deprecated table above for replacements.

Types

WithCacheOptions, WithCacheConfig, NextConfigFunction, MemoryOptions, CacheOptions, ElastiCacheIamOptions, AttachElastiCacheIamAuthOptions, ElastiCacheIamAuth, AwsCredentials, AwsCredentialsProvider, PageCacheHandlerOptions, RedisCacheHandlerOptions (deprecated), RemoteCacheHandlerOptions, LocalCacheHandlerOptions, FlushCacheOptions, FlushCacheResult, PageCacheControl, PageCacheGetContext, PageCacheHandlerContext, PageCacheHandlerValue, PageCacheSetContext, PageCacheValue, DataCacheHandler, DataCacheEntry, Timestamp.

Types of the deprecated legacy data cache API, removed with it in 16.5: DataCacheHandlerOptions, CacheHandlerType, MemoryDataCacheHandlerOptions, RedisDataCacheHandlerOptions, RedisClient, IoredisLike, CacheHandlerConfig.

Next.js APIs

The package sits behind Next's own caching APIs: "use cache", "use cache: remote" ("use cache: private" never reaches it), cacheLife, cacheTag, revalidateTag, updateTag and revalidatePath. See Usage Examples and Next's Caching and Revalidating.