### Install and Run Example Project Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Commands to install dependencies, navigate to the example project, build, and start the application. Caching is only enabled in production mode. ```bash pnpm install cd examples/redis-minimal npm run build npm run start ``` -------------------------------- ### Production Cache Handler Setup Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md This example demonstrates a complete production-ready configuration for the cache handler. It includes setting up Redis, handling development fallbacks to an LRU cache, and using a composite handler to manage caching strategies based on tags. Ensure Redis connection details are provided via environment variables. ```javascript import { createClient } from "redis"; import { PHASE_PRODUCTION_BUILD } from "next/constants.js"; import { CacheHandler } from "@fortedigital/nextjs-cache-handler"; import createLruHandler from "@fortedigital/nextjs-cache-handler/local-lru"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import createCompositeHandler from "@fortedigital/nextjs-cache-handler/composite"; CacheHandler.onCreation(() => { // Important - It's recommended to use global scope to ensure only one Redis connection is made // This ensures only one instance get created if (global.cacheHandlerConfig) { return global.cacheHandlerConfig; } // Important - It's recommended to use global scope to ensure only one Redis connection is made // This ensures new instances are not created in a race condition if (global.cacheHandlerConfigPromise) { return global.cacheHandlerConfigPromise; } // You may need to ignore Redis locally, remove this block otherwise if (process.env.NODE_ENV === "development") { const lruCache = createLruHandler(); return { handlers: [lruCache] }; } // Main promise initializing the handler global.cacheHandlerConfigPromise = (async () => { let redisClient = null; if (PHASE_PRODUCTION_BUILD !== process.env.NEXT_PHASE) { const settings = { url: process.env.REDIS_URL, pingInterval: 10000, }; // This is optional and needed only if you use access keys if (process.env.REDIS_ACCESS_KEY) { settings.password = process.env.REDIS_ACCESS_KEY; } try { redisClient = createClient(settings); redisClient.on("error", (e) => { if (typeof process.env.NEXT_PRIVATE_DEBUG_CACHE !== "undefined") { console.warn("Redis error", e); } global.cacheHandlerConfig = null; global.cacheHandlerConfigPromise = null; }); } catch (error) { console.warn("Failed to create Redis client:", error); } } if (redisClient) { try { console.info("Connecting Redis client..."); await redisClient.connect(); console.info("Redis client connected."); } catch (error) { console.warn("Failed to connect Redis client:", error); await redisClient .disconnect() .catch(() => console.warn( "Failed to quit the Redis client after failing to connect.", ), ); } } const lruCache = createLruHandler(); if (!redisClient?.isReady) { console.error("Failed to initialize caching layer."); global.cacheHandlerConfigPromise = null; global.cacheHandlerConfig = { handlers: [lruCache] }; return global.cacheHandlerConfig; } const redisCacheHandler = createRedisHandler({ client: redisClient, keyPrefix: "nextjs:", }); global.cacheHandlerConfigPromise = null; // This example uses composite handler to switch from Redis to LRU cache if tags contains `memory-cache` tag. // You can skip composite and use Redis or LRU only. global.cacheHandlerConfig = { handlers: [ createCompositeHandler({ handlers: [lruCache, redisCacheHandler], setStrategy: (ctx) => (ctx?.tags.includes("memory-cache") ? 0 : 1), // You can adjust strategy for deciding which cache should the composite use }), ], }; return global.cacheHandlerConfig; })(); return global.cacheHandlerConfigPromise; }); export default CacheHandler; ``` -------------------------------- ### Build and Start Production Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Build the Next.js application and start it in production mode to enable caching. ```bash npm run build npm run start ``` -------------------------------- ### Install @fortedigital/nextjs-cache-handler Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Install the main package and optional peer dependencies for Redis clients. ```bash npm i @fortedigital/nextjs-cache-handler # optional peer deps npm i @redis/client # official node-redis client npm i ioredis # ioredis client (optional) ``` -------------------------------- ### Install Dependencies Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Install project dependencies using npm. ```bash npm i ``` -------------------------------- ### Install Next.js Cache Handler Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Standard installation command for the @fortedigital/nextjs-cache-handler package using npm. ```bash npm i @fortedigital/nextjs-cache-handler ``` -------------------------------- ### Using ioredis with Cache Handler Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Integrate the cache handler with `ioredis` using the `ioredisAdapter` helper. Install `ioredis` via npm. This setup allows using an existing `ioredis` client instance with the handler. ```javascript import Redis from "ioredis"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { ioredisAdapter } from "@fortedigital/nextjs-cache-handler/helpers/ioredisAdapter"; const client = new Redis(process.env.REDIS_URL); const redisClient = ioredisAdapter(client); const redisHandler = createRedisHandler({ client: redisClient, keyPrefix: "my-app:", }); ``` -------------------------------- ### Migrate Cache Handler Setup Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Shows how to update cache handler setup when migrating from @neshca/cache-handler to @fortedigital/nextjs-cache-handler. Ensure the CacheHandler.onCreation callback is preserved. ```javascript // cache-handler.mjs import { CacheHandler } from "@neshca/cache-handler"; CacheHandler.onCreation(() => { // setup }); export default CacheHandler; ``` ```javascript // cache-handler.mjs import { CacheHandler } from "@fortedigital/nextjs-cache-handler"; CacheHandler.onCreation(() => { // setup }); export default CacheHandler; ``` -------------------------------- ### Start Development Server Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Run all development servers in parallel using pnpm. This command is used for the development workflow within the monorepo. ```bash pnpm dev ``` -------------------------------- ### Migrate Instrumentation Setup Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Demonstrates updating the instrumentation registration when switching from @neshca/cache-handler to @fortedigital/nextjs-cache-handler. This is crucial for Next.js runtime integration. ```typescript // instrumentation.ts export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { const { registerInitialCache } = await import("@neshca/cache-handler/instrumentation"); const CacheHandler = (await import("../cache-handler.mjs")).default; await registerInitialCache(CacheHandler); } } ``` ```typescript // instrumentation.ts export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { const { registerInitialCache } = await import("@fortedigital/nextjs-cache-handler/instrumentation"); const CacheHandler = (await import("../cache-handler.mjs")).default; await registerInitialCache(CacheHandler); } } ``` -------------------------------- ### Default Cache Example Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Demonstrates Next.js's default fetch caching behavior using 'force-cache'. This is suitable for static or rarely changing data. ```javascript // Example usage in a Next.js page or component // fetch('/api/data', { cache: 'force-cache' }); ``` -------------------------------- ### No Store Example Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Shows how to use the 'no-store' fetch option to ensure responses are never cached and always fetch fresh data. Ideal for real-time or user-specific data. ```javascript // Example usage in a Next.js page or component // fetch('/api/realtime-data', { cache: 'no-store' }); ``` -------------------------------- ### Get Versioned Client Info Tag Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Use `getClientInfoTag` to obtain a versioned identification string for the Redis `clientInfoTag` option. This function reads the package version at runtime, ensuring the tag accurately reflects the installed version. It's useful for identifying the cache handler in `CLIENT LIST` output. ```javascript import { createClient } from "redis"; import { getClientInfoTag } from "@fortedigital/nextjs-cache-handler/helpers/getClientInfoTag"; // Returns e.g. "nextjs-cache-handler_v3.2.0" console.log(getClientInfoTag()); const client = createClient({ url: process.env.REDIS_URL, clientInfoTag: getClientInfoTag(), // visible in Redis CLIENT LIST output }); await client.connect(); ``` -------------------------------- ### Fetch with Tags Example Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Illustrates fetch caching with both time-based revalidation (24 hours) and cache tags for selective invalidation. Includes a button to manually clear the cache. ```javascript // Example usage in a Next.js page or component // fetch('/api/data-with-tags', { // next: { // revalidate: 86400, // 24 hours // tags: ['collection'] // } // }); ``` -------------------------------- ### unstable_cache Example Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Demonstrates persistent caching of function results using `unstable_cache`, which supports tags and revalidation. Useful for caching database queries or computations. ```javascript // Example usage of unstable_cache // import { unstable_cache } from 'next/cache'; // // const cachedFunction = unstable_cache( // async () => { // // Fetch data or perform computation // return await db.query('SELECT * FROM items'); // }, // ['items-cache-key'], // { revalidate: 3600, tags: ['items'] } // ); ``` -------------------------------- ### Configure CacheHandler.onCreation() Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Configure the cache handler once during application startup. This example shows conditional Redis usage based on the environment (dev vs. production) and sets default TTL values. ```javascript // cache-handler.mjs import { CacheHandler } from "@fortedigital/nextjs-cache-handler"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import createLruHandler from "@fortedigital/nextjs-cache-handler/local-lru"; import { createClient } from "redis"; CacheHandler.onCreation(async ({ buildId, serverDistDir, dev }) => { // Skip Redis during the build phase or in dev mode if (dev) { return { handlers: [createLruHandler()] }; } const client = createClient({ url: process.env.REDIS_URL, pingInterval: 10_000, }); client.on("error", (err) => console.warn("Redis error", err)); await client.connect(); const redisHandler = createRedisHandler({ client, keyPrefix: `${buildId ?? "app"}:`, // isolate deploys by buildId }); return { handlers: [redisHandler, createLruHandler()], ttl: { defaultStaleAge: 60 * 60 * 24, // 24 h default stale age estimateExpireAge: (staleAge) => staleAge * 2, }, }; }); export default CacheHandler; ``` -------------------------------- ### Configure Cache Handler with Redis Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Sets up the CacheHandler to use Redis for caching. Ensure the REDIS_URL environment variable is set. This setup is minimal and not intended for production. ```javascript // cache-handler.mjs import { CacheHandler } from "@fortedigital/nextjs-cache-handler"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { createClient } from "redis"; const client = createClient({ url: process.env.REDIS_URL }); await client.connect(); CacheHandler.onCreation(() => ({ handlers: [createRedisHandler({ client })], })); export default CacheHandler; ``` -------------------------------- ### ISR with Static Params Example Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Demonstrates Incremental Static Regeneration (ISR) with `generateStaticParams` for dynamic routes, allowing static generation at build time and on-demand regeneration with time-based revalidation. ```javascript // Example in a dynamic route file like app/isr/blog/[id]/page.js // export async function generateStaticParams() { // const posts = await fetch('...').then((res) => res.json()); // return posts.map((post) => ({ id: post.id.toString() })); // } // // export default async function PostPage({ params }) { // const post = await fetch(`.../posts/${params.id}`, { // next: { revalidate: 3600 } // Revalidate every hour // }).then((res) => res.json()); // // ... render post // } ``` -------------------------------- ### Time-based Revalidation Example Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Demonstrates time-based revalidation for fetch requests, automatically revalidating the cache after a specified duration (e.g., 30 seconds). ```javascript // Example usage in a Next.js page or component // fetch('/api/data-to-revalidate', { next: { revalidate: 30 } }); ``` -------------------------------- ### Create Redis Cache Handler Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Create a cache handler backed by Redis strings. This example shows minimal usage with default JSON serialization and advanced usage with gzip compression and custom Redis key configurations. ```javascript import createRedisHandler, { jsonCacheValueSerializer, } from "@fortedigital/nextjs-cache-handler/redis-strings"; import { createClient } from "redis"; import {gzipSync, gunzipSync} from "zlib"; const client = createClient({ url: process.env.REDIS_URL }); await client.connect(); // Minimal usage — JSON serialization (default) const basicHandler = createRedisHandler({ client }); // Advanced usage — gzip compression + custom key options const compressedHandler = createRedisHandler({ client, keyPrefix: "myApp:", // namespace all keys sharedTagsKey: "myTags", // override tag hash key name sharedTagsTtlKey: "myTagTtls", // override TTL hash key name timeoutMs: 3_000, // 3 s timeout per Redis op keyExpirationStrategy: "EXAT", // use SET … EXAT instead of EXPIREAT revalidateTagQuerySize: 5_000, // tags per HSCAN page valueSerializer: { serialize: (value) => gzipSync(JSON.stringify(value)).toString("base64"), deserialize: (stored) => JSON.parse(gunzipSync(Buffer.from(stored, "base64")).toString("utf-8")), }, }); ``` -------------------------------- ### Implement a Custom Handler Interface Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Create a custom cache backend by implementing the `Handler` interface. Register your custom handler similarly to built-in ones. This example uses an in-memory Map. ```typescript import type { Handler, CacheHandlerValue } from "@fortedigital/nextjs-cache-handler"; import { CacheHandler } from "@fortedigital/nextjs-cache-handler"; const memoryStore = new Map(); const customHandler: Handler = { name: "my-custom-handler", async get(key, { implicitTags }) { const entry = memoryStore.get(key); if (!entry) return null; // Honour implicit tag invalidation for (const tag of [...entry.tags, ...implicitTags]) { // custom staleness check omitted for brevity } return entry; }, async set(key, value, ctx) { if (ctx?.setOnlyIfNotExists && memoryStore.has(key)) return; memoryStore.set(key, value); }, async revalidateTag(tag) { for (const [key, entry] of memoryStore) { if (entry.tags.includes(tag)) memoryStore.delete(key); } }, async delete(key) { memoryStore.delete(key); }, async prepare() { // Called once after handlers are wired up; good for cleanup / warm-up tasks const now = Date.now() / 1000; for (const [key, entry] of memoryStore) { if (entry.lifespan && entry.lifespan.expireAt < now) memoryStore.delete(key); } }, }; CacheHandler.onCreation(() => ({ handlers: [customHandler] })); ``` -------------------------------- ### createCompositeHandler() Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Routes cache operations across two or more handlers. The `get` operation uses a first-match strategy, while `set` delegates to a single handler chosen by `setStrategy`. `revalidateTag` and `delete` operations fan out to all handlers in parallel. ```APIDOC ## createCompositeHandler() ### Description Routes cache operations across two or more handlers. `get` uses a first-match strategy; `set` delegates to a single handler chosen by `setStrategy`; `revalidateTag` and `delete` fan out to all handlers in parallel. ### Usage ```js import createCompositeHandler from "@fortedigital/nextjs-cache-handler/composite"; import createLruHandler from "@fortedigital/nextjs-cache-handler/local-lru"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; const lru = createLruHandler(); const redis = createRedisHandler({ client }); // Route writes: entries tagged "memory-cache" go to LRU (index 0), // everything else to Redis (index 1). const composite = createCompositeHandler({ handlers: [lru, redis], setStrategy: (cacheValue) => cacheValue?.tags.includes("memory-cache") ? 0 : 1, }); CacheHandler.onCreation(() => ({ handlers: [composite], })); ``` ### Options - **handlers** (Array): An array of cache handlers to route operations through. - **setStrategy** (function): A function that determines which handler to use for `set` operations. It receives the `cacheValue` and should return the index of the handler in the `handlers` array. ``` -------------------------------- ### Revalidate Cache by Tag (GET) Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Use this GET request to revalidate cache entries associated with a specific tag. You can optionally specify a cache profile like 'hours' or 'days'. ```bash curl http://localhost:3000/api/revalidate?tag=futurama ``` ```bash curl http://localhost:3000/api/revalidate?tag=futurama&cacheLife=hours ``` ```bash curl http://localhost:3000/api/revalidate?tag=futurama&cacheLife=days ``` -------------------------------- ### getClientInfoTag() Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Returns a versioned identification string for the Redis clientInfoTag option. This function reads the package version at runtime to ensure the tag always reflects the installed version, which is useful for identifying the cache handler in Redis CLIENT LIST output. ```APIDOC ## getClientInfoTag() — `@fortedigital/nextjs-cache-handler/helpers/getClientInfoTag` Returns a versioned identification string for the Redis `clientInfoTag` option. Reads the package version at runtime so the tag always reflects the installed version. Useful for identifying the cache handler in `CLIENT LIST` output. ```js import { createClient } from "redis"; import { getClientInfoTag } from "@fortedigital/nextjs-cache-handler/helpers/getClientInfoTag"; // Returns e.g. "nextjs-cache-handler_v3.2.0" console.log(getClientInfoTag()); const client = createClient({ url: process.env.REDIS_URL, clientInfoTag: getClientInfoTag(), // visible in Redis CLIENT LIST output }); await client.connect(); ``` ``` -------------------------------- ### Enable Debug Cache Logging Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md To debug cache behavior, enable detailed cache operation logs by setting the `NEXT_PRIVATE_DEBUG_CACHE` environment variable to 1 before starting your Next.js application in production mode. ```bash NEXT_PRIVATE_DEBUG_CACHE=1 npm run start ``` -------------------------------- ### Create Composite Cache Handler Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Combine multiple cache handlers using `createCompositeHandler`. `get` operations use a first-match strategy, while `set` operations are routed based on a provided strategy. `revalidateTag` and `delete` operations are fanned out to all handlers. ```javascript import createCompositeHandler from "@fortedigital/nextjs-cache-handler/composite"; import createLruHandler from "@fortedigital/nextjs-cache-handler/local-lru"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; const lru = createLruHandler(); const redis = createRedisHandler({ client }); // Route writes: entries tagged "memory-cache" go to LRU (index 0), // everything else to Redis (index 1). const composite = createCompositeHandler({ handlers: [lru, redis], setStrategy: (cacheValue) => cacheValue?.tags.includes("memory-cache") ? 0 : 1, }); CacheHandler.onCreation(() => ({ handlers: [composite], })); ``` -------------------------------- ### Cache API Data with neshClassicCache Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Example of caching API data using neshClassicCache within a Next.js API route. Configure revalidation interval and tags for the cache. Ensure the fetchData function returns a Promise. ```jsx import { neshClassicCache } from "@fortedigital/nextjs-cache-handler/functions"; import axios from "axios"; const cachedAxios = neshClassicCache(async (url) => { return (await axios.get(url.href)).data; }); export default async function handler(request, response) { const data = await cachedAxios( { revalidate: 5, tags: ["api-data"], responseContext: response }, new URL("https://api.example.com/data.json"), ); response.json(data); } ``` -------------------------------- ### Importing CacheHandler in 1.x.x (JavaScript) Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/docs/migration/1_x_x__2_x_x.md Demonstrates the import statement for Next15CacheHandler in version 1.x.x. ```javascript const { Next15CacheHandler, } = require("@fortedigital/nextjs-cache-handler/next-15-cache-handler"); module.exports = new Next15CacheHandler(); ``` -------------------------------- ### Register Initial Cache with Write Strategy Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Populates the cache with build-time artifacts. Use `setOnlyIfNotExists` to preserve existing runtime entries, allowing explicit control over cache population. ```typescript await registerInitialCache(CacheHandler, { setOnlyIfNotExists: true, }); ``` -------------------------------- ### Cache Revalidation API Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Unified endpoint for revalidating cache by tag or path. Supports GET and POST methods for tag-based revalidation, and POST for path-based revalidation. ```APIDOC ## GET /api/revalidate ### Description Revalidates cache based on a specified tag. Supports different cache profiles. ### Method GET ### Endpoint `/api/revalidate?tag={tag}&cacheLife={profile}` ### Parameters #### Query Parameters - **tag** (string) - Required - The tag to revalidate. - **cacheLife** (string) - Optional - The cache profile to use (e.g., 'max', 'hours', 'days'). Defaults to 'max'. ### Response #### Success Response (200) - **revalidated** (boolean) - Indicates if the cache was revalidated. ### Response Example ```json { "revalidated": true } ``` ## POST /api/revalidate ### Description Revalidates cache by tag or path. For tag-based revalidation, a JSON body with a 'tag' field is expected. For path-based revalidation, a JSON body with a 'path' field is expected. ### Method POST ### Endpoint `/api/revalidate` ### Parameters #### Request Body - **tag** (string) - Required (if revalidating by tag) - The tag to revalidate. - **cacheLife** (string) - Optional (if revalidating by tag) - The cache profile to use (e.g., 'max', 'hours', 'days'). Defaults to 'max'. - **path** (string) - Required (if revalidating by path) - The specific path to revalidate. ### Request Example ```json { "tag": "futurama", "cacheLife": "hours" } ``` ### Request Example ```json { "path": "/examples/default-cache" } ``` ### Response #### Success Response (200) - **revalidated** (boolean) - Indicates if the cache was revalidated. ### Response Example ```json { "revalidated": true } ``` ``` -------------------------------- ### Adapt Redis Cluster for Cache Handler Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Use `withAdapter` to wrap a `@redis/client` cluster instance. This ensures the `isReady` property correctly reflects the readiness of all replica clients. Pass the result directly to `createRedisHandler`. ```javascript import { createCluster } from "@redis/client"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { withAdapter } from "@fortedigital/nextjs-cache-handler/cluster/adapter"; const cluster = withAdapter( createCluster({ rootNodes: [{ url: process.env.REDIS_URL }], // Resolve shard IPs to the correct TLS hostname when using a cluster behind a LB nodeAddressMap(address) { const { hostname } = new URL(process.env.REDIS_URL); const [, port] = address.split(":"); return { host: hostname, port: Number(port) }; }, }), ); await cluster.connect(); const handler = createRedisHandler({ client: cluster, keyPrefix: "app:" }); ``` -------------------------------- ### Run in Development Mode Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Run the Next.js application in development mode, where caching is intentionally disabled. ```bash npm run dev ``` -------------------------------- ### registerInitialCache() Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Pre-warms the remote cache from the `.next` build artefacts at server startup. This function should be called from Next.js's `instrumentation.ts` within the `nodejs` runtime guard. It reads the prerender manifest and uploads pages, app routes, and fetch-cache entries in parallel. ```APIDOC ## registerInitialCache() ### Description Pre-warms the remote cache from the `.next` build artefacts at server startup. Call it from Next.js's `instrumentation.ts` inside the `nodejs` runtime guard. Reads the prerender manifest and uploads pages, app routes, and fetch-cache entries in parallel. ### Usage ```ts // src/instrumentation.ts export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { const { registerInitialCache } = await import("@fortedigital/nextjs-cache-handler/instrumentation"); const CacheHandler = (await import("../cache-handler.mjs")).default; await registerInitialCache(CacheHandler, { fetch: true, // populate fetch-cache entries pages: true, // populate pre-rendered pages/routes routes: true, // populate App Router route handlers buildDir: ".next",// override if build output is elsewhere setOnlyIfNotExists: true, // preserve runtime-written entries on redeploy parallelism: 8, // concurrent file-read + cache-set operations }); } } ``` ### Options - **fetch** (boolean): Pre-warm fetch-cache entries. Defaults to `true`. - **pages** (boolean): Pre-warm pre-rendered pages. Defaults to `true`. - **routes** (boolean): Pre-warm App Router routes. Defaults to `true`. - **buildDir** (string): Path to the Next.js build directory. Defaults to `'.next'`. - **setOnlyIfNotExists** (boolean): NX-style write; skip if key exists. Defaults to `false`. - **parallelism** (number): Max concurrent cache-population operations. Defaults to `max(4, cpus)`. ``` -------------------------------- ### Required Dependencies for Full Redis Package (JSON) Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/docs/migration/1_x_x__2_x_x.md Specifies the minimum required versions for Next.js and the full Redis package when needed. ```json "next": ">=15.2.4", "redis": ">=5.5.6" ``` -------------------------------- ### withAdapter() Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Wraps a RedisClusterType from @redis/client to correctly derive the isReady property from replica clients. This is useful when creating a Redis handler for a cluster. ```APIDOC ## withAdapter() — `@fortedigital/nextjs-cache-handler/cluster/adapter` Wraps a `RedisClusterType` from `@redis/client` so that the `isReady` property is correctly derived from the readiness of all replica clients. Pass the result directly to `createRedisHandler`. ```js import { createCluster } from "@redis/client"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { withAdapter } from "@fortedigital/nextjs-cache-handler/cluster/adapter"; const cluster = withAdapter( createCluster({ rootNodes: [{ url: process.env.REDIS_URL }], // Resolve shard IPs to the correct TLS hostname when using a cluster behind a LB nodeAddressMap(address) { const { hostname } = new URL(process.env.REDIS_URL); const [, port] = address.split(":"); return { host: hostname, port: Number(port) }; }, }), ); await cluster.connect(); const handler = createRedisHandler({ client: cluster, keyPrefix: "app:" }); ``` ``` -------------------------------- ### Required Dependencies for Redis Client (JSON) Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/docs/migration/1_x_x__2_x_x.md Specifies the minimum required versions for Next.js and the Redis client when using the Redis client only. ```json "next": ">=15.2.4", "@redis/client": ">=5.5.6" ``` -------------------------------- ### Create Redis Cache Handler Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Initializes the redis-strings cache handler. Requires the official `redis` package and provides configuration options for client, key prefix, and shared tag keys. Ensure `REDIS_URL` is set in your environment variables. ```javascript import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; const redisHandler = await createRedisHandler({ client: createClient({ url: process.env.REDIS_URL, }), keyPrefix: "myApp:", sharedTagsKey: "myTags", sharedTagsTtlKey: "myTagTtls", }); ``` -------------------------------- ### Run All Tests Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Execute all tests within the project using pnpm. This command is part of the development workflow for ensuring code quality. ```bash pnpm test ``` -------------------------------- ### Create Local LRU Cache Handler Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Use `createLruHandler` for an in-process LRU cache. It's suitable for local fallbacks or development but not for multi-instance production environments. Defaults to 1,000 items and 100 MB. ```javascript import createLruHandler from "@fortedigital/nextjs-cache-handler/local-lru"; // Defaults: 1 000 items, 100 MB total const defaultHandler = createLruHandler(); // Custom limits const largeHandler = createLruHandler({ maxItemsNumber: 10_000, // max number of entries maxItemSizeBytes: 1024 * 1024 * 500, // 500 MB total size limit }); // Typical use: fallback inside CacheHandler.onCreation CacheHandler.onCreation(async () => { if (process.env.NODE_ENV === "development") { return { handlers: [createLruHandler()] }; } // ... connect Redis, return composite }); ``` -------------------------------- ### Redis Cluster Configuration Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Configure the cache handler to use a Redis Cluster with the `@redis/client` library. The `withAdapter` helper is used to adapt the cluster instance for the handler. Ensure your `REDIS_URL` environment variable is set. ```javascript import { createCluster } from "@redis/client"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { withAdapter } from "@fortedigital/nextjs-cache-handler/cluster/adapter"; const { hostname: redisHostName } = new URL(process.env.REDIS_URL); redis = withAdapter( createCluster({ rootNodes: [{ url: process.env.REDIS_URL }], // optional if you use TLS and need to resolve shards' ip to proper hostname nodeAddressMap(address) { const [_, port] = address.split(":"); return { host: redisHostName, port: Number(port), }; }, }), ); // after using withAdapter you can use redis cluster instance as parameter for createRedisHandler const redisCacheHandler = createRedisHandler({ client: redis, keyPrefix: CACHE_PREFIX, }); ``` -------------------------------- ### ioredisAdapter() Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Adapts an ioredis Redis instance to the RedisClientType interface expected by createRedisHandler. This allows ioredis to be used as a drop-in replacement for @redis/client. ```APIDOC ## ioredisAdapter() — `@fortedigital/nextjs-cache-handler/helpers/ioredisAdapter` Adapts an `ioredis` `Redis` instance to the `RedisClientType` interface expected by `createRedisHandler`. Translates method names, argument shapes, and return values so `ioredis` works as a drop-in replacement for `@redis/client`. ```js import Redis from "ioredis"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { ioredisAdapter } from "@fortedigital/nextjs-cache-handler/helpers/ioredisAdapter"; const ioredisClient = new Redis(process.env.REDIS_URL, { lazyConnect: false, }); // Wait for connection before passing to the adapter await new Promise((resolve, reject) => { ioredisClient.once("ready", resolve); ioredisClient.once("error", reject); }); const adaptedClient = ioredisAdapter(ioredisClient); const handler = createRedisHandler({ client: adaptedClient, keyPrefix: "nextjs:", }); ``` ``` -------------------------------- ### Register Initial Cache for Pre-warming Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Use `registerInitialCache` in `instrumentation.ts` to pre-warm the remote cache from build artifacts. This function reads the prerender manifest and uploads pages, app routes, and fetch-cache entries concurrently. Configure which cache types to populate and the parallelism level. ```typescript // src/instrumentation.ts export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { const { registerInitialCache } = await import("@fortedigital/nextjs-cache-handler/instrumentation"); const CacheHandler = (await import("../cache-handler.mjs")).default; await registerInitialCache(CacheHandler, { fetch: true, // populate fetch-cache entries pages: true, // populate pre-rendered pages/routes routes: true, // populate App Router route handlers buildDir: ".next",// override if build output is elsewhere setOnlyIfNotExists: true, // preserve runtime-written entries on redeploy parallelism: 8, // concurrent file-read + cache-set operations }); } } ``` -------------------------------- ### Flush All Redis Databases Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md When migrating from Next.js 14 to 15/16, cache formats are incompatible. Flush your entire Redis cache before running the new version to avoid errors. ```bash redis-cli FLUSHALL ``` -------------------------------- ### Adapt ioredis Client for Cache Handler Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Use `ioredisAdapter` to adapt an `ioredis` `Redis` instance to the `RedisClientType` interface. This allows `ioredis` to be used as a drop-in replacement for `@redis/client` with `createRedisHandler`. Ensure the `ioredis` client is connected before adapting. ```javascript import Redis from "ioredis"; import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { ioredisAdapter } from "@fortedigital/nextjs-cache-handler/helpers/ioredisAdapter"; const ioredisClient = new Redis(process.env.REDIS_URL, { lazyConnect: false, }); // Wait for connection before passing to the adapter await new Promise((resolve, reject) => { ioredisClient.once("ready", resolve); ioredisClient.once("error", reject); }); const adaptedClient = ioredisAdapter(ioredisClient); const handler = createRedisHandler({ client: adaptedClient, keyPrefix: "nextjs:", }); ``` -------------------------------- ### Create Composite Cache Handler Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Sets up a composite cache handler to route operations across multiple underlying handlers. Supports custom routing strategies and a first-available read strategy. ```javascript import createCompositeHandler from "@fortedigital/nextjs-cache-handler/composite"; const compositeHandler = createCompositeHandler({ handlers: [handler1, handler2], setStrategy: (data) => (data?.tags.includes("handler1") ? 0 : 1), }); ``` -------------------------------- ### Create Local LRU Cache Handler Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Initializes an in-memory LRU cache handler. Suitable for development and testing, not production. Configure max items and size. ```javascript import createLruHandler from "@fortedigital/nextjs-cache-handler/local-lru"; const localHandler = createLruHandler({ maxItemsNumber: 10000, maxItemSizeBytes: 1024 * 1024 * 500, }); ``` -------------------------------- ### Importing CacheHandler in 2.x.x+ (JavaScript) Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/docs/migration/1_x_x__2_x_x.md Shows the updated import statement for CacheHandler in version 2.x.x and later. The Next15CacheHandler is no longer exported separately. ```javascript const { CacheHandler } = require("@fortedigital/nextjs-cache-handler"); module.exports = CacheHandler; ``` -------------------------------- ### Update Cache Handler Import Path Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/docs/migration/1_2_x__1_3_x.md Before 1.3.x, Next15CacheHandler was imported from a specific next-15-cache-handler subpath. After 1.3.x, it is directly available from the main package. ```javascript const { Next15CacheHandler, } = require("@fortedigital/nextjs-cache-handler/next-15-cache-handler"); module.exports = new Next15CacheHandler(); ``` ```javascript const { Next15CacheHandler } = require("@fortedigital/nextjs-cache-handler"); module.exports = Next15CacheHandler; ``` -------------------------------- ### Configure TTL options for CacheHandler Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Control cache entry freshness and expiration using `defaultStaleAge` and `estimateExpireAge`. These options are passed within the object returned from `onCreation`. ```javascript CacheHandler.onCreation(async () => { const handlers = [createRedisHandler({ client })]; return { handlers, ttl: { // Entries with no explicit `revalidate` are stale after 12 h (default: 1 year) defaultStaleAge: 60 * 60 * 12, // Hard-expire at 2× the stale age; Next.js will serve stale during revalidation // until expireAt, then treat the entry as a full cache miss estimateExpireAge: (staleAge) => staleAge * 2, }, }; }); ``` -------------------------------- ### Custom Value Serializer: Gzip Compression Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Implement a custom value serializer using Node's zlib for gzip compression. This is useful for large text cache entries. Ensure to profile if your traffic is very hot, as compression runs on the server during cache reads and writes. ```javascript import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { gzipSync, gunzipSync } from "zlib"; const redisCacheHandler = createRedisHandler({ client: redisClient, keyPrefix: "nextjs:", valueSerializer: { serialize: (value) => gzipSync(JSON.stringify(value)).toString("base64"), deserialize: (stored) => { const parsed = JSON.parse( gunzipSync(Buffer.from(stored, "base64")).toString("utf-8"), ); return parsed; }, }, }); ``` -------------------------------- ### Revalidate Cache by Path (POST) Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Use this POST request to revalidate cache entries for a specific path. Ensure the path is correctly formatted. ```bash curl -X POST http://localhost:3000/api/revalidate \ -H "Content-Type: application/json" \ -d '{"path": "/examples/default-cache"}' ``` -------------------------------- ### Import Default Serializer Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Import the built-in JSON cache value serializer for wrapping or comparison. ```javascript import createRedisHandler, { jsonCacheValueSerializer, } from "@fortedigital/nextjs-cache-handler/redis-strings"; ``` -------------------------------- ### Custom Value Serializer: Brotli Compression Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Implement a custom value serializer using Node's zlib for brotli compression. This is beneficial for large text cache entries. Brotli compression runs on the server during cache reads and writes; profile if your traffic is very hot. ```javascript import createRedisHandler from "@fortedigital/nextjs-cache-handler/redis-strings"; import { brotliCompress, brotliDecompress } from "zlib"; import { promisify } from "util"; const brotliCompressAsync = promisify(brotliCompress); const brotliDecompressAsync = promisify(brotliDecompress); const redisCacheHandler = createRedisHandler({ client: redisClient, keyPrefix: "nextjs:", valueSerializer: { async serialize(value) { const compressed = await brotliCompressAsync( Buffer.from(JSON.stringify(value), "utf8"), ); return compressed.toString("base64"); }, async deserialize(stored) { const decompressed = await brotliDecompressAsync( Buffer.from(stored, "base64"), ); return JSON.parse(decompressed.toString("utf8")); }, }, }); ``` -------------------------------- ### Update Instrumentation Import Path Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/docs/migration/1_2_x__1_3_x.md The instrumentation module's import path has changed in version 1.3.x. Ensure you are importing from the correct location to enable proper cache registration. ```javascript if (process.env.NEXT_RUNTIME === "nodejs") { const { registerInitialCache } = await import( "@neshca/cache-handler/instrumentation" ); const CacheHandler = (await import("./cache-handler.js")).default; await registerInitialCache(CacheHandler); } ``` ```javascript if (process.env.NEXT_RUNTIME === "nodejs") { const { registerInitialCache } = await import( "@fortedigital/nextjs-cache-handler/instrumentation" ); const CacheHandler = (await import("./cache-handler.js")).default; await registerInitialCache(CacheHandler); } ``` -------------------------------- ### Ping Redis Server Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Verify that your Redis server is running and accessible by sending a PING command. A PONG response indicates a successful connection. ```bash redis-cli ping ``` -------------------------------- ### createLruHandler() Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Creates an in-process LRU cache Handler using `lru-cache`. This is intended as a local fallback when Redis is unavailable or for development environments. It is not suitable for multi-instance production use. ```APIDOC ## createLruHandler() ### Description Creates an in-process LRU cache Handler using `lru-cache`. Intended as a local fallback when Redis is unavailable or for development environments. Not suitable for multi-instance production use. ### Usage ```js import createLruHandler from "@fortedigital/nextjs-cache-handler/local-lru"; // Defaults: 1 000 items, 100 MB total const defaultHandler = createLruHandler(); // Custom limits const largeHandler = createLruHandler({ maxItemsNumber: 10_000, // max number of entries maxItemSizeBytes: 1024 * 1024 * 500, // 500 MB total size limit }); // Typical use: fallback inside CacheHandler.onCreation CacheHandler.onCreation(async () => { if (process.env.NODE_ENV === "development") { return { handlers: [createLruHandler()] }; } // ... connect Redis, return composite }); ``` ### Options Refer to `CreateLruHandlerOptions` for available options. ``` -------------------------------- ### Configure next.config.ts/js Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Register the custom cache handler with Next.js and disable the default in-memory cache. ```typescript // next.config.ts import type { NextConfig } from "next"; const nextConfig: NextConfig = { cacheHandler: require.resolve("./cache-handler.mjs"), cacheMaxMemorySize: 0, // disable Next.js built-in in-memory layer }; export default nextConfig; ``` -------------------------------- ### Configure Next.js to Use Cache Handler Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md Configures your Next.js project to use the custom cache handler. This file should be placed in your project root. ```javascript // next.config.js module.exports = { cacheHandler: require.resolve("./cache-handler.mjs"), }; ``` -------------------------------- ### revalidateTag() with cacheLife Profiles Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/examples/redis-minimal/README.md Shows the updated `revalidateTag()` API with required `cacheLife` profiles ('max', 'hours', 'days') for stale-while-revalidate behavior, demonstrating migration from Next.js 15. ```javascript // Example for 'max' cacheLife profile // import { revalidateTag } from 'next/cache'; // revalidateTag('profile-max', { cacheLife: 'max' }); // // // Example for 'hours' cacheLife profile // revalidateTag('profile-hours', { cacheLife: 'hours' }); // // // Example for 'days' cacheLife profile // revalidateTag('profile-days', { cacheLife: 'days' }); ``` -------------------------------- ### Flush Specific Redis Database Source: https://github.com/fortedigital/nextjs-cache-handler/blob/master/README.md If you are using a specific Redis database, you can flush only that database when migrating from Next.js 14 to 15/16. Replace `` with the actual database number. ```bash redis-cli -n FLUSHDB ``` -------------------------------- ### withAbortSignal() Source: https://context7.com/fortedigital/nextjs-cache-handler/llms.txt Wraps any promise-returning function with AbortSignal cancellation support. If the signal fires before the promise settles, the returned promise rejects with 'Operation aborted'. Already-aborted signals reject immediately. ```APIDOC ## withAbortSignal() — `@fortedigital/nextjs-cache-handler/helpers/withAbortSignal` Wraps any promise-returning function with `AbortSignal` cancellation support. If the signal fires before the promise settles, the returned promise rejects with `"Operation aborted"`. Already-aborted signals reject immediately. ```js import { withAbortSignal } from "@fortedigital/nextjs-cache-handler/helpers/withAbortSignal"; const controller = new AbortController(); // Cancel the operation after 2 seconds setTimeout(() => controller.abort(), 2_000); try { const result = await withAbortSignal( () => fetch("https://api.example.com/data").then((r) => r.json()), controller.signal, ); console.log("Got:", result); } catch (err) { console.error("Failed or aborted:", err.message); // => "Operation aborted" if the signal fired first } ``` ```