### Quick Start Installation for Next.js Cache Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/installation/page.mdx Initializes a new Next.js application with the `cache-handler-redis` example using various package managers for a quick setup. ```sh npx create-next-app --example cache-handler-redis cache-handler-redis-app ``` ```sh yarn create next-app --example cache-handler-redis cache-handler-redis-app ``` ```sh pnpm create next-app --example cache-handler-redis cache-handler-redis-app ``` -------------------------------- ### Install Redis Package (Optional) Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/installation/page.mdx Installs the `redis` package, which is required if you plan to use Redis as your cache store with `@neshca/cache-handler`. ```sh npm install redis ``` -------------------------------- ### Install `@neshca/cache-handler` Package Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/installation/page.mdx Installs the core `@neshca/cache-handler` package into your Next.js project's root directory for manual setup. ```sh npm install @neshca/cache-handler ``` -------------------------------- ### Install Project Dependencies with pnpm Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/monorepo.md Installs all required packages and their specified versions from `package-lock.json` using pnpm. ```bash pnpm i ``` -------------------------------- ### Install Redis package Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/creating-a-custom-handler/page.mdx Installs the `redis` package using npm, which is a prerequisite for connecting to a Redis instance and implementing the custom cache handler. ```sh npm install redis ``` -------------------------------- ### Install Local Dependencies for Command Line Usage Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/monorepo.md Installs local dependencies to enable their usage from the command line. Updates to `package-lock.json` are expected and safe to commit. ```bash pnpm i ``` -------------------------------- ### Build and Start @repo/cache-testing Application Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/cache-handler.md These commands compile the `@repo/cache-testing` application and then launch it. This step is crucial for testing changes made to `@neshca/cache-handler` and requires rebuilding after any handler changes. ```bash pnpm build:test-app pnpm start:test-app ``` -------------------------------- ### Build and Start Next.js Application in Production Mode Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/installation/page.mdx These shell commands are used to build and start a Next.js application in production mode. Running these commands allows you to observe the custom cache handler in action, as it's typically configured to be active only in production environments. ```sh npm run build npm run start ``` -------------------------------- ### Install Playwright and Run E2E UI Tests Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/cache-handler.md These commands install Playwright dependencies for the `@repo/cache-testing` project and then execute the end-to-end UI tests. This is used to verify the functionality of the cache handler. ```bash pnpm -F @repo/cache-testing playwright:install pnpm -F @repo/cache-testing e2e:ui ``` -------------------------------- ### Build Internal Monorepo Packages with pnpm Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/monorepo.md Compiles internal dependencies of the monorepo after initial dependency installation. ```bash pnpm build:packages ``` -------------------------------- ### Start Development Servers for Packages Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/cache-handler.md This command initiates the local backend development servers for packages within the monorepo, specifically for `@repo/cache-testing` to support SSG and ISR functionalities. ```bash pnpm dev:packages ``` -------------------------------- ### Install @neshca/json-replacer-reviver Source: https://github.com/caching-tools/next-shared-cache/blob/canary/packages/json-replacer-reviver/README.md Commands to install the `@neshca/json-replacer-reviver` utility library using various Node.js package managers. ```bash npm add @neshca/json-replacer-reviver ``` ```bash pnpm add @neshca/json-replacer-reviver ``` ```bash yarn add @neshca/json-replacer-reviver ``` ```bash bun add @neshca/json-replacer-reviver ``` -------------------------------- ### JavaScript Example: Configuring CacheHandler with onCreation Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/on-creation/page.mdx Demonstrates how to use the `CacheHandler.onCreation` static method to define and configure multiple custom cache handlers and a global TTL policy for a Next.js application. ```js CacheHandler.onCreation(async (context) => { const mainHandler = { // Define custom get, set, and other Handler methods }; const fallbackHandler = { // Define custom get, set, and other Handler methods }; return { handlers: [mainHandler, fallbackHandler], ttl: { defaultStaleAge: 3600, estimateExpireAge: (staleAge) => staleAge * 2, }, }; }); ``` -------------------------------- ### Populate Next.js Cache with Initial Prerendered Data Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/installation/page.mdx This TypeScript code for `instrumentation.ts` demonstrates how to use `registerInitialCache` from `@neshca/cache-handler/instrumentation`. It ensures that pre-rendered pages and routes are added to the custom cache handler's store when the Next.js application starts, specifically for Node.js runtime environments. ```ts export async function register() { if (process.env.NEXT_RUNTIME === 'nodejs') { const { registerInitialCache } = await import( '@neshca/cache-handler/instrumentation' ); // Assuming that your CacheHandler configuration is in the root of the project and the instrumentation is in the src directory. // Please adjust the path accordingly. // CommonJS CacheHandler configuration is also supported. const CacheHandler = (await import('../cache-handler.mjs')).default; await registerInitialCache(CacheHandler); } } ``` -------------------------------- ### Example: Conditional Cache Handler Initialization Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/cache-handler-config/page.mdx Illustrates how to dynamically initialize cache handlers using `CacheHandler.onCreation`, demonstrating a setup where a Redis handler is used if available, otherwise a local handler is employed. ```javascript CacheHandler.onCreation(async () => { let handler; if (process.env.REDIS_AVAILABLE) { await client.connect(); handler = await createRedisHandler({ client, }); } else { handler = { // ... }; } return { handlers: [handler], }; }); ``` -------------------------------- ### Build and Start Next.js Application Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/troubleshooting/page.mdx Instructions to build and start a Next.js application using npm or yarn, typically after modifying the cache handler or environment variables. ```sh npm run build npm run start ``` -------------------------------- ### Fix Code Style and Adhere to Guidelines with pnpm Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/monorepo.md Runs required checks and automatically fixes code style errors and warnings to ensure consistent and high-quality code before committing. ```bash pnpm codestyle:fix ``` -------------------------------- ### Launch Redis Instance using Docker Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/cache-handler.md This command launches a Redis Stack instance as a Docker container, mapping standard Redis and Redis Insight ports to the host machine. It's essential for local cache development and inspection. ```bash docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest ``` -------------------------------- ### Copying `fetch-cache` in Monorepo for Next.js Standalone Output Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/populating-cache-on-start/page.mdx Shows an adjusted shell command for copying the `fetch-cache` directory in a monorepo setup when using `output: 'standalone'`. This command accounts for the specific path of the Next.js application within the monorepo structure, ensuring correct cache pre-population. ```sh mkdir -p .next/standalone//.next/cache/fetch-cache cp .next/cache/fetch-cache/* .next/standalone//.next/cache/fetch-cache/ ``` -------------------------------- ### Implement Custom Cache Handler Logic for Next.js Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/installation/page.mdx This JavaScript code defines a custom cache handler (`cache-handler.mjs`) using `@neshca/cache-handler`. It demonstrates implementing `get`, `set`, `revalidateTag`, and an optional `delete` method for a `MagicMap`-like cache store, allowing shared cache values across Next.js app instances. ```js import { CacheHandler } from '@neshca/cache-handler'; CacheHandler.onCreation(async () => { // Let's imagine we're using a map // in which values are shared via the network // between all your Next.js app instances. const cacheStore = new MagicMap(); const handler = { async get(key) { return await cacheStore.get(key); }, async set(key, value) { await cacheStore.set(key, value); }, async revalidateTag(tag) { // Iterate over all entries in the cache for (const [key, { tags }] of cacheStore) { // If the value's tags include the specified tag, delete this entry if (tags.includes(tag)) { await cacheStore.delete(key); } } }, // Optional: Implement the delete method // if your cache store doesn't support automatic time-based key expiration. // It will be called when the get method returns expired data. async delete(key) { await cacheStore.delete(key); } }; return { handlers: [handler] }; }); export default CacheHandler; ``` -------------------------------- ### Implement Custom Redis Cache Handler for Next.js Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/creating-a-custom-handler/page.mdx This JavaScript code demonstrates how to create a custom Redis cache handler for Next.js applications using `@neshca/cache-handler`. It initializes a Redis client, defines key prefixes, handles errors, and implements `get` and `set` methods for caching data, including tag revalidation logic. ```js import { CacheHandler } from '@neshca/cache-handler'; import { isImplicitTag } from '@neshca/cache-handler/helpers'; import { createClient, commandOptions } from 'redis'; CacheHandler.onCreation(async () => { // Always create a Redis client inside the `onCreation` callback. const client = createClient({ url: 'redis://localhost:6379', }); // Redis won't work without error handling. https://github.com/redis/node-redis?tab=readme-ov-file#events client.on('error', (error) => { if (typeof process.env.NEXT_PRIVATE_DEBUG_CACHE !== 'undefined') { // Use logging with caution in production. Redis will flood your logs. Hide it behind a flag. console.error('Redis client error:', error); } }); await client.connect(); // Define a timeout for Redis operations. const timeoutMs = 1000; // Define a key prefix for the cache. // It is useful to avoid key collisions with other data in Redis, // or to delete all cache keys at once by using a pattern. const keyPrefix = 'my-app-cache:'; // Define a key for shared tags. // You'll see how to use it later in the `revalidateTag` method const sharedTagsKey = '_sharedTags_'; // Create an assert function to ensure that the client is ready before using it. // When you throw an error in any Handler method, // the CacheHandler will use the next available Handler listed in the `handlers` array. function assertClientIsReady() { if (!client.isReady) { throw new Error('Redis client is not ready yet or connection is lost.'); } } const revalidatedTagsKey = `${keyPrefix}__revalidated_tags__`; // Create a custom Redis Handler const customRedisHandler = { // Give the handler a name. // It is useful for logging in debug mode. name: 'redis-strings-custom', // We do not use try/catch blocks in the Handler methods. // CacheHandler will handle errors and use the next available Handler. async get(key, { implicitTags }) { // Ensure that the client is ready before using it. // If the client is not ready, the CacheHandler will use the next available Handler. assertClientIsReady(); // Create a new AbortSignal with a timeout for the Redis operation. // By default, redis client operations will wait indefinitely. const options = commandOptions({ signal: AbortSignal.timeout(timeoutMs), }); // Get the value from Redis. // We use the key prefix to avoid key collisions with other data in Redis. const result = await client.get(options, keyPrefix + key); // If the key does not exist, return null. if (!result) { return null; } // Redis stores strings, so we need to parse the JSON. const cacheValue = JSON.parse(result); // If the cache value has no tags, return it early. if (!cacheValue) { return null; } // Get the set of explicit and implicit tags. // implicitTags are available only on the `get` method. const combinedTags = new Set([...cacheValue.tags, ...implicitTags]); // If there are no tags, return the cache value early. if (combinedTags.size === 0) { return cacheValue; } // Get the revalidation times for the tags. const revalidationTimes = await client.hmGet( commandOptions({ signal: AbortSignal.timeout(timeoutMs) }), revalidatedTagsKey, Array.from(combinedTags), ); // Iterate over all revalidation times. for (const timeString of revalidationTimes) { // If the revalidation time is greater than the last modified time of the cache value, if ( timeString && Number.parseInt(timeString, 10) > cacheValue.lastModified ) { // Delete the key from Redis. await client.unlink( commandOptions({ signal: AbortSignal.timeout(timeoutMs) }), keyPrefix + key, ); // Return null to indicate cache miss. return null; } } // Return the cache value. return cacheValue; }, async set(key, cacheHandlerValue) { // Ensure that the client is ready before using it. assertClientIsReady(); // Create a new AbortSignal with a timeout for the Redis operation. const options = commandOptions({ signal: AbortSignal.timeout(timeoutMs), }); // Redis stores strings, so we need to stringify the JSON. const setOperation = client.set( options, keyPrefix + key, JSON.stringify(cacheHandlerValue), ); // If the cacheHandlerValue has a lifespan, set the automatic expiration. // cacheHandlerValue.lifespan can be null if the value is the page from the Pages Router without getStaticPaths or with `fallback: false` ``` -------------------------------- ### Configure Redis-stack Cache Handler in Next.js Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/redis/page.mdx This JavaScript code demonstrates how to set up a `cache-handler.mjs` file for Next.js, utilizing `@neshca/cache-handler` with a `redis-stack` backend. It includes logic for connecting to Redis using `REDIS_URL`, handling connection errors, and falling back to a local LRU cache if Redis is unavailable or fails to connect. The file should be created next to `next.config.js`. ```js import { CacheHandler } from '@neshca/cache-handler'; import createLruHandler from '@neshca/cache-handler/local-lru'; import createRedisHandler from '@neshca/cache-handler/redis-stack'; import { createClient } from 'redis'; CacheHandler.onCreation(async () => { let client; try { // Create a Redis client. client = createClient({ url: process.env.REDIS_URL ?? 'redis://localhost:6379', }); // Redis won't work without error handling. https://github.com/redis/node-redis?tab=readme-ov-file#events client.on('error', (error) => { if (typeof process.env.NEXT_PRIVATE_DEBUG_CACHE !== 'undefined') { // Use logging with caution in production. Redis will flood your logs. Hide it behind a flag. console.error('Redis client error:', error); } }); } catch (error) { console.warn('Failed to create Redis client:', error); } if (client) { try { console.info('Connecting Redis client...'); // Wait for the client to connect. // Caveat: This will block the server from starting until the client is connected. // And there is no timeout. Make your own timeout if needed. await client.connect(); console.info('Redis client connected.'); } catch (error) { console.warn('Failed to connect Redis client:', error); console.warn('Disconnecting the Redis client...'); // Try to disconnect the client to stop it from reconnecting. client .disconnect() .then(() => { console.info('Redis client disconnected.'); }) .catch(() => { console.warn( 'Failed to quit the Redis client after failing to connect.', ); }); } } /** @type {import("@neshca/cache-handler").Handler | null} */ let handler; if (client?.isReady) { // Create the `redis-stack` Handler if the client is available and connected. handler = await createRedisHandler({ client, keyPrefix: 'prefix:', timeoutMs: 1000, }); } else { // Fallback to LRU handler if Redis client is not available. // The application will still work, but the cache will be in memory only and not shared. handler = createLruHandler(); console.warn( 'Falling back to LRU handler because Redis client is not available.', ); } return { handlers: [handler], }; }); export default CacheHandler; ``` -------------------------------- ### Exposing Custom Redis Cache Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/creating-a-custom-handler/page.mdx This snippet shows how the `customRedisHandler` is structured and returned as part of the `CacheHandler` object, specifically within the `handlers` array. It highlights that `get` methods run sequentially, while other methods run in parallel, influencing the order of cache operations. ```javascript }; return { // The order of the handlers is important. // The CacheHandler will run get methods in the order of the handlers array. // Other methods will be run in parallel. handlers: [customRedisHandler], }; }); export default CacheHandler; ``` -------------------------------- ### Configure Local Redis URL Environment Variable Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/cache-handler.md This snippet shows how to define the `REDIS_URL` environment variable in a `.env.local` file. This variable points the application to the locally running Redis instance for cache operations. ```bash REDIS_URL=redis://localhost:6379 ``` -------------------------------- ### Configure Next.js to Use Custom Cache Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/installation/page.mdx This JavaScript configuration for `next.config.js` integrates the custom cache handler. It conditionally enables the `cacheHandler` (or `incrementalCacheHandlerPath` for older versions) in production, and sets `instrumentationHook` to true for experimental features like initial cache population. ```js const nextConfig = { cacheHandler: process.env.NODE_ENV === 'production' ? require.resolve('./cache-handler.mjs') : undefined, // Use `experimental` option instead of the `cacheHandler` property when using Next.js versions from 13.5.1 to 14.0.4 /* experimental: { incrementalCacheHandlerPath: process.env.NODE_ENV === 'production' ? require.resolve('./cache-handler.mjs') : undefined, }, */ experimental: { // This is required for the experimental feature of pre-populating the cache with the initial data instrumentationHook: true } }; module.exports = nextConfig; ``` -------------------------------- ### Conditionally Connect to Redis in cache-handler.mjs Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/opt-out-cache-on-build/page.mdx Adjust the `onCreation` method in your `cache-handler.mjs` file to conditionally connect to the Redis server. The `process.env.REDIS_AVAILABLE` environment variable is used to determine if the server has already started, ensuring the Redis client attempts a connection only when the server is up and running. ```js CacheHandler.onCreation(async () => { let handler; if (process.env.REDIS_AVAILABLE) { await client.connect(); handler = await createRedisHandler({ client, }); } return { handlers: [handler], }; }); ``` -------------------------------- ### Clear Redis Database Before Tests Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/contributing/cache-handler.md This sequence of commands connects to the Redis instance running in Docker and executes the `flushall` command. It's important to clear the Redis database before running tests to ensure a clean state. ```bash docker exec -it cache-handler-redis redis-cli 127.0.0.1:6379> flushall OK ``` -------------------------------- ### Expected Console Output for Active Cache Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/troubleshooting/page.mdx Shows the expected console output when the cache handler's `get` and `set` methods are invoked, indicating successful operation. ```plaintext handler.get ... handler.set ... ``` -------------------------------- ### APIDOC: Handler.get Method Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/handler/page.mdx Details the `get` method of the `Handler` interface, responsible for retrieving a value from the cache. It handles cache misses and expiration, potentially passing control to subsequent handlers. ```APIDOC Handler.get: Parameters: key: string - The unique string identifier for the cache entry. implicitTags: string[] - An array of tags that are implicitly associated with the cache entry. Return value: Promise - Resolves to the cached value (if found), null or undefined if the entry is not found. Overview: Retrieves the value associated with the given key from the cache. Handles cache misses by returning null/undefined or throwing an error to pass to the next handler. Automatically checks for expiration and calls delete if implemented. ``` -------------------------------- ### Caching Data with nashCache and Axios in Next.js Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/functions/nesh-cache/page.mdx This example demonstrates how to integrate `nashCache` into a Next.js application to cache data fetched using Axios. It shows how to define an asynchronous data fetching function, wrap it with `neshCache`, and then use the cached function within a React component to display data, handling cases where data might not be found. ```jsx import { neshCache } from '@neshca/cache-handler/functions'; import axios from 'axios'; async function getViaAxios(url) { try { return (await axios.get(url.href)).data; } catch (_error) { return null; } } const cachedAxios = neshCache(getViaAxios); export default async function Index() { const url = new URL('https://api.example.com/data.json'); const data = await cachedAxios(url); if (!data) { notFound(); } const { id } = data; return
id is {id}
; } ``` -------------------------------- ### Add Console Logging to Next.js Cache Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/troubleshooting/page.mdx Demonstrates how to add `console.log` statements within the `get` and `set` methods of a custom Next.js cache handler to verify its activity. This helps in tracing cache operations. ```js /* ... */ const handler = { name: 'my-cache-handler', async get(key) { console.log('handler.get', key); return cacheStore.get(key); }, async set(key, value) { console.log('handler.set', key, value); cacheStore.set(key, value); } }; ``` -------------------------------- ### TypeScript Handler Type Definition Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/handler/page.mdx Defines the `Handler` type, an interface for cache handlers specifying methods for getting, setting, revalidating, and optionally deleting cache entries. ```typescript type Handler = { name: string; get: (key: string) => Promise; set: (key: string, value: CacheHandlerValue) => Promise; revalidateTag: (tag: string) => Promise; delete?: (key: string) => Promise; }; ``` -------------------------------- ### Setting Cache Expiration and Tags in Redis Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/creating-a-custom-handler/page.mdx This code snippet demonstrates how to store a cache entry's expiration time using `client.expireAt` and its associated tags using `client.hSet` in Redis. Tags are stored separately to optimize retrieval during revalidation. All operations are executed concurrently using `Promise.all`. ```javascript // so, we need to check if it exists before using it const expireOperation = cacheHandlerValue.lifespan ? client.expireAt( options, keyPrefix + key, cacheHandlerValue.lifespan.expireAt, ) : undefined; // If the cache handler value has tags, set the tags. // We store them separately to save time to retrieve them in the `revalidateTag` method. const setTagsOperation = cacheHandlerValue.tags.length ? client.hSet( options, keyPrefix + sharedTagsKey, key, JSON.stringify(cacheHandlerValue.tags), ) : undefined; // Wait for all operations to complete. await Promise.all([setOperation, expireOperation, setTagsOperation]); }, ``` -------------------------------- ### Configuring CacheHandler with Custom TTL Parameters Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/ttl-parameters/page.mdx Demonstrates how to configure the `CacheHandler` class with custom `TTLParameters`, setting a `defaultStaleAge` of 1 hour (3600 seconds) and an `estimateExpireAge` function that doubles the stale age. This setup is useful for background revalidation of cache entries. ```javascript CacheHandler.onCreation(async () => { const mainHandler = { // ... }; const fallbackHandler = { // ... }; return { handlers: [mainHandler, fallbackHandler], // will apply to all Handlers ttl: { defaultStaleAge: 3600, estimateExpireAge: (staleAge) => staleAge * 2, }, }; }); ``` -------------------------------- ### Revalidating Cache Tags in Redis Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/creating-a-custom-handler/page.mdx This function handles the revalidation of cached content based on a given tag. It first checks for implicit tags, then scans Redis for all keys associated with the specified tag using `hScan`. Finally, it deletes the invalidated cache entries using `client.unlink` and updates the tag hash map using `client.hDel`, ensuring concurrent execution of these cleanup operations. ```javascript async revalidateTag(tag) { // Ensure that the client is ready before using it. assertClientIsReady(); // Check if the tag is implicit. // Implicit tags are not stored in the cached values. if (isImplicitTag(tag)) { // Mark the tag as revalidated at the current time. await client.hSet( commandOptions({ signal: AbortSignal.timeout(timeoutMs) }), revalidatedTagsKey, tag, Date.now(), ); } // Create a map to store the tags for each key. const tagsMap = new Map(); // Cursor for the hScan operation. let cursor = 0; // Define a query size for the hScan operation. const hScanOptions = { COUNT: 100 }; // Iterate over all keys in the shared tags. do { const remoteTagsPortion = await client.hScan( commandOptions({ signal: AbortSignal.timeout(timeoutMs) }), keyPrefix + sharedTagsKey, cursor, hScanOptions, ); // Iterate over all keys in the portion. for (const { field, value } of remoteTagsPortion.tuples) { // Parse the tags from the value. tagsMap.set(field, JSON.parse(value)); } // Update the cursor for the next iteration. cursor = remoteTagsPortion.cursor; // If the cursor is 0, we have reached the end. } while (cursor !== 0); // Create an array of keys to delete. const keysToDelete = []; // Create an array of tags to delete from the hash map. const tagsToDelete = []; // Iterate over all keys and tags. for (const [key, tags] of tagsMap) { // If the tags include the specified tag, add the key to the delete list. if (tags.includes(tag)) { // Key must be prefixed because we use the key prefix in the set method. keysToDelete.push(keyPrefix + key); // Set an empty string as the value for the revalidated tag. tagsToDelete.push(key); } } // If there are no keys to delete, return early. if (keysToDelete.length === 0) { return; } // Delete the keys from Redis. const deleteKeysOperation = client.unlink( commandOptions({ signal: AbortSignal.timeout(timeoutMs) }), keysToDelete, ); // Update the tags in Redis by deleting the revalidated tags. const updateTagsOperation = client.hDel( // Use the isolated option to prevent the command from being executed on the main connection. { isolated: true, ...commandOptions({ signal: AbortSignal.timeout(timeoutMs) }), }, keyPrefix + sharedTagsKey, tagsToDelete, ); // Wait for all operations to complete. await Promise.all([deleteKeysOperation, updateTagsOperation]); }, ``` -------------------------------- ### Copying `fetch-cache` in Dockerfile for Next.js Standalone Output Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/populating-cache-on-start/page.mdx Illustrates the Dockerfile instruction to copy the `fetch-cache` directory into the `standalone` build context. This ensures that applications deployed via Docker with `output: 'standalone'` have their initial fetch calls data pre-populated. ```dockerfile COPY .next/cache/fetch-cache/ .next/standalone/.next/cache/fetch-cache/ ``` -------------------------------- ### Copying `fetch-cache` for Next.js Standalone Output Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/populating-cache-on-start/page.mdx Provides a shell command to manually copy the `fetch-cache` directory to the `standalone` build output. This step is crucial when using `output: 'standalone'` in `next.config.js` to ensure that initial fetch calls data is pre-populated in the deployed application. ```sh mkdir -p .next/standalone/.next/cache/fetch-cache cp .next/cache/fetch-cache/* .next/standalone/.next/cache/fetch-cache/ ``` -------------------------------- ### APIDOC: CacheHandler.onCreationHook Definition and Details Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/on-creation/page.mdx Comprehensive API documentation for the `onCreationHook` function, including its TypeScript type signature, the properties of its `context` argument (`serverDistDir`, `dev`, `buildId`), and its `CacheHandlerConfig` return type. ```APIDOC type OnCreationHook = ( context: CacheCreationContext, ) => Promise | CacheHandlerConfig; OnCreationHook: (context: CacheCreationContext) => Promise | CacheHandlerConfig Arguments: context: CacheCreationContext serverDistDir: string Description: The absolute path to the Next.js server directory. Purpose: Critical for locating server-side resources and files. dev: boolean (optional) Description: Indicates whether the Next.js application is running in development mode. Purpose: Can be used to alter cache behavior based on the environment. buildId: string (optional) Description: A unique identifier for the current build of the Next.js application. Purpose: May be used as a prefix for namespacing cache keys. Return Value: CacheHandlerConfig Description: Specifies the list of cache Handlers and TTL policy. ``` -------------------------------- ### Run Local Tests for @neshca/json-replacer-reviver Source: https://github.com/caching-tools/next-shared-cache/blob/canary/packages/json-replacer-reviver/README.md Command to execute local tests specifically for the `@neshca/json-replacer-reviver` package within the monorepo using pnpm. ```bash pnpm -F @repo/json-replacer-reviver test ``` -------------------------------- ### Initialize and Configure Redis Cluster Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/handlers/experimental-redis-cluster/page.mdx Demonstrates how to import and initialize the `experimental-redis-cluster` handler with a Redis cluster instance and various configuration options like key prefix, timeout, and tag revalidation settings. Note that this handler is experimental. ```js import { createCluster } from 'redis'; import createClusterHandler from '@neshca/cache-handler/experimental-redis-cluster'; const cluster = createCluster(clusterOptions); await cluster.connect(); const redisHandler = createClusterHandler({ cluster, keyPrefix: 'JSON:', timeoutMs: 1000, keyExpirationStrategy: 'EXAT', sharedTagsKey: '__sharedTags__', revalidateTagQuerySize: 100, }); // ... ``` -------------------------------- ### Registering Initial Cache with `registerInitialCache` in Next.js Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/populating-cache-on-start/page.mdx Demonstrates how to use `registerInitialCache` from `@neshca/cache-handler/instrumentation` to populate the cache with initial data during application startup. It shows conditional registration for Node.js environments and configuration options to control what data (pages, routes, fetch calls) is cached. Requires `experimental.instrumentationHook = true;`. ```js export async function register() { if (process.env.NEXT_RUNTIME === 'nodejs') { const { registerInitialCache } = await import( '@neshca/cache-handler/instrumentation' ); // Assuming that your CacheHandler configuration is in the root of the project and the instrumentation is in the src directory. // Please adjust the path accordingly. // CommonJS CacheHandler configuration is also supported. const CacheHandler = (await import('../cache-handler.mjs')).default; await registerInitialCache(CacheHandler, { // By default, it populates the cache with pre-rendered pages, routes, and fetch calls. // You can disable these features by setting the options to false. // For example, if you want to populate the cache with only pre-rendered pages, you can set the options as follows: fetch: false, routes: false, }); } } ``` -------------------------------- ### API Reference for experimental-redis-cluster Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/handlers/experimental-redis-cluster/page.mdx Details the parameters and return value for creating a new `Handler` instance using `@neshca/cache-handler/experimental-redis-cluster`. ```APIDOC createClusterHandler(options: object): Handler instance Parameters: options: object cluster: Redis Cluster instance keyPrefix?: string = '' Description: Optional. Prefix for all keys, useful for namespacing. timeoutMs?: number = 5000 Description: Optional. Timeout in milliseconds for Redis operations. Set to 0 for no timeout. keyExpirationStrategy?: 'EXAT' | 'EXPIREAT' = 'EXPRIREAT' Description: Optional. Allows choosing the expiration strategy for cache keys. Values: 'EXAT': Uses the EXAT option of the SET command (Redis server 6.2.0+). More efficient. 'EXPIREAT': Uses the EXPIREAT command (Redis server 4.0.0+). Requires an additional command call. sharedTagsKey?: string = '__sharedTags__' Description: Optional. Dedicated key for the internal revalidation process. Must not interfere with application cache keys. revalidateTagQuerySize?: number = 100 Description: Optional. The number of tags in a single query retrieved from Redis when scanning or searching for tags. Adjust to optimize commands/network data. Return value: A new Handler instance for the experimental-redis-cluster Handler. ``` -------------------------------- ### Initialize Redis Strings Cache Handler in JavaScript Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/handlers/redis-strings/page.mdx Demonstrates how to import and initialize the `redis-strings` cache handler using a Redis client. It shows setting up the client connection and configuring the handler with various options like key prefix, timeout, and key expiration strategy. ```javascript import { createClient } from 'redis'; import createRedisHandler from '@neshca/cache-handler/redis-strings'; const client = createClient(clientOptions); await client.connect(); const redisHandler = createRedisHandler({ client, keyPrefix: 'prefix:', timeoutMs: 1000, keyExpirationStrategy: 'EXAT', sharedTagsKey: '__sharedTags__', revalidateTagQuerySize: 100, }); ``` -------------------------------- ### API Reference for `@neshca/cache-handler/local-lru` Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/handlers/local-lru/page.mdx Details the API for creating a new `Handler` instance for the `local-lru` cache. It describes the `options` parameter and its properties, along with the function's return value. ```APIDOC @neshca/cache-handler/local-lru: createLocalHandler(options: object): Handler options: maxItemsNumber: number (Optional) Description: Maximum number of items the cache can hold. Default: 1000 maxItemSizeBytes: number (Optional) Description: Maximum size in bytes for each item in the cache. Default: 1024 * 1024 * 100 (100 MB) Returns: A new Handler instance for the local-lru Handler. ``` -------------------------------- ### API Reference: createRedisHandler Function Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/handlers/redis-stack/page.mdx Detailed API documentation for the `createRedisHandler` function, which creates a new `Handler` instance for the `redis-stack` cache handler. It outlines the required `options` object and its properties, including `client`, `keyPrefix`, `timeoutMs`, and `revalidateTagQuerySize`, along with their types and descriptions. It also specifies the return value of the function. ```APIDOC createRedisHandler(options: object): Handler instance options: client: Redis client instance (required) description: A Redis client instance. The client must be ready before creating the Handler. keyPrefix: string (optional) description: Prefix for all keys, useful for namespacing. Defaults to an empty string. timeoutMs: number (optional) description: Timeout in milliseconds for Redis operations. Defaults to 5000. Set to 0 for disabling timeouts. revalidateTagQuerySize: number (optional) description: The number of tags in a single query retrieved from Redis when scanning or searching for tags. Defaults to 100. Note: Adjust this value to optimize the number of commands sent to Redis when scanning or searching for tags. A higher value will reduce the number of commands sent to Redis, but it will also increase the amount of data transferred over the network. Returns: A new Handler instance for the redis-stack Handler. ``` -------------------------------- ### API Reference for createRedisHandler Function Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/handlers/redis-strings/page.mdx Detailed API documentation for the `createRedisHandler` function, outlining its parameters, their types, descriptions, and default values. It also explains specific options like `keyExpirationStrategy` and `revalidateTagQuerySize`. ```APIDOC Function: createRedisHandler Description: Creates a new Handler instance for the redis-strings Handler. Parameters: options: object client: Redis client instance (required) Description: The client must be ready before creating the Handler. keyPrefix: string (optional, default: '') Description: Prefix for all keys, useful for namespacing. timeoutMs: number (optional, default: 5000) Description: Timeout in milliseconds for Redis operations. Set to 0 to disable. keyExpirationStrategy: string (optional, default: 'EXPRIREAT') Description: Allows choosing the expiration strategy for cache keys. Possible values: 'EXAT': Uses the EXAT option of the SET command. Requires Redis server 6.2.0 or newer. 'EXPIREAT': Uses the EXPIREAT command. Requires Redis server 4.0.0 or newer. sharedTagsKey: string (optional, default: '__sharedTags__') Description: Dedicated key for the internal revalidation process. Must not interfere with cache keys from your application. revalidateTagQuerySize: number (optional, default: 100) Description: The number of tags in a single query retrieved from Redis when scanning or searching for tags. Note: A higher value reduces commands but increases data transfer (Redis TCP packet size typically 65,535 bytes). Return value: Handler instance ``` -------------------------------- ### Configure Next.js for Standalone Output Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/build-id-as-prefix-key/page.mdx This `next.config.js` configuration sets the `output` to `standalone`, which is required when deploying the app once and using the same build across all environments without `generateBuildId`. ```JavaScript const nextConfig = { output: 'standalone' }; ``` -------------------------------- ### Verbose Debug Output for Next.js Cache Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/troubleshooting/page.mdx Illustrates the detailed console output generated when the `NEXT_PRIVATE_DEBUG_CACHE` environment variable is enabled, showing cache handler initialization and operation details. ```plaintext using custom cache handler @neshca/cache-handler is not configured yet [CacheHandler] Instance created with provided context. [CacheHandler] Creating new CacheHandler configuration. Connecting Redis client... [CacheHandler] Cache configuration retrieved from onCreation hook. Redis client connected. [CacheHandler] [handlers: [redis-stack]] Successfully created CacheHandler configuration. [CacheHandler] [method: get] [key: /app/with-params/dynamic-true/200] Started retrieving value in order. [CacheHandler] [handler: redis-stack] [method: get] [key: /app/with-params/dynamic-true/200] Started retrieving value. ``` -------------------------------- ### Initialize Redis Stack Cache Handler in JavaScript Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/handlers/redis-stack/page.mdx This snippet demonstrates how to initialize the `redis-stack` cache handler using the `createRedisHandler` function from `@neshca/cache-handler/redis-stack`. It requires a connected Redis client instance and allows for configuration of key prefixes, operation timeouts, and revalidation tag query size. Ensure `RedisJSON` and `RedisSearch` modules are enabled on your Redis server for proper functionality. ```javascript import { createClient } from 'redis'; import createRedisHandler from '@neshca/cache-handler/redis-stack'; const client = createClient(clientOptions); await client.connect(); const redisHandler = createRedisHandler({ client, keyPrefix: 'prefix:', timeoutMs: 1000, revalidateTagQuerySize: 100, }); ``` -------------------------------- ### API Documentation for TTLParameters Interface Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/ttl-parameters/page.mdx Detailed documentation for the `TTLParameters` interface, outlining its properties `defaultStaleAge` and `estimateExpireAge`, including their purpose, parameters, and return values. ```APIDOC TTLParameters: defaultStaleAge: number Description: The time in seconds for when the cache entry becomes stale. Defaults to 1 year. Stale age is the time after which the cache entry is considered stale, can be served from the cache, and should be revalidated. Revalidation is handled by the `CacheHandler` class. estimateExpireAge(staleAge: number): number Description: Estimates the expiration age based on the stale age. Defaults to `(staleAge) => staleAge * 1.5`. Expire age is the time after which the cache entry is considered expired and should be removed from the cache and must not be served. The provided callback will be wrapped in a normalizing function to ensure that the result is a non-negative integer. Parameters: staleAge: number Description: The stale age in seconds. Defaults to 1 year. The stale age is the time you have provided in the `revalidate` option in your `getStaticProps` or in the `fetch` method. If you did not specify the `revalidate` option, the stale age will be the default stale age. After the stale age, the cache entry is considered stale, can be served from the cache, and should be revalidated. Revalidation is handled by the `CacheHandler` class. Return value: The expire age in seconds. ``` -------------------------------- ### nashCache Function API Reference Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/functions/nesh-cache/page.mdx Details the parameters and return value of the `nashCache` function, including options for data fetching, caching behavior, and serialization/deserialization. ```APIDOC nashCache function: Parameters: fetchData: Type: asynchronous function (Promise-returning) Description: Fetches the data to cache. commonOptions: Type: object Description: Controls cache behavior. Properties: tags: Type: string[] Description: Tags for cache revalidation (revalidateTag, revalidatePath). revalidate: Type: number | false Description: Revalidation interval in seconds (positive integer) or false to disable. Defaults to route's export const revalidate. argumentsSerializer: Type: function Description: Serializes callback arguments to create a cache key. Defaults to JSON.stringify(args). resultSerializer: Type: function Description: Serializes callback result. Defaults to Buffer.from(JSON.stringify(data)).toString('base64'). resultDeserializer: Type: function Description: Deserializes string representation of result. Defaults to JSON.parse(Buffer.from(data, 'base64').toString('utf-8')). Returns: Type: function (Promise-returning) Description: When invoked, returns a Promise resolving to cached data. If not in cache, fetchData is invoked, its result cached and returned. Arguments of returned function: options: Type: object Description: Overrides commonOptions. cacheKey: Type: string Description: Custom cache key. ``` -------------------------------- ### LifespanParameters Type Properties Reference Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/lifespan-parameters/page.mdx Detailed API documentation for the properties of the `LifespanParameters` type, outlining their purpose, data types, and how they relate to cache entry lifecycle management. ```APIDOC LifespanParameters: lastModifiedAt: number The Unix timestamp (in seconds) for when the cache entry was last modified. staleAt: number The Unix timestamp (in seconds) for when the cache entry entry becomes stale. After this time, the entry is considered staled and may be used. expireAt: number The Unix timestamp (in seconds) for when the cache entry must be removed from the cache. After this time, the entry is considered expired and must not be used. staleAge: number Time in seconds before the cache entry becomes stale. expireAge: number Time in seconds before the cache entry becomes expired. revalidate: Revalidate | undefined Value from Next.js revalidate option. May be false if the page has no revalidate option or the revalidate option is set to false. ``` -------------------------------- ### Enable Next.js Cache Debug Mode via Environment Variable Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/troubleshooting/page.mdx Explains how to activate a verbose debug mode for the Next.js cache handler by setting the `NEXT_PRIVATE_DEBUG_CACHE` environment variable in a `.env.local` file. ```plaintext NEXT_PRIVATE_DEBUG_CACHE=1 ``` -------------------------------- ### CacheHandlerConfig TTL Property Reference Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/api-reference/cache-handler-config/page.mdx Documents the `ttl` property of `CacheHandlerConfig`, which provides options for configuring the time-to-live for cache entries, referencing the `TTLParameters` API for detailed settings. ```APIDOC ttl?: Partial Description: Time-to-live (TTL) options for the cache entries. See `TTLParameters` API reference for more information. ``` -------------------------------- ### Use Git Hash as Cache Key Prefix in Cache Handler Source: https://github.com/caching-tools/next-shared-cache/blob/canary/docs/cache-handler-docs/src/app/usage/build-id-as-prefix-key/page.mdx This `cache-handler.mjs` snippet demonstrates how to use `process.env.GIT_HASH` directly as the `keyPrefix` for the Redis cache handler. This approach relies on the `GIT_HASH` being available in the environment. ```JavaScript CacheHandler.onCreation(async () => { await client.connect(); const handler = await createRedisHandler({ client, keyPrefix: process.env.GIT_HASH }); return { handlers: [handler] }; }); ```