Skip to content

Instantly share code, notes, and snippets.

@omavi
Created April 17, 2025 02:42
Show Gist options
  • Save omavi/5527110cc6b664eefccd33fbb3f4aa20 to your computer and use it in GitHub Desktop.
Save omavi/5527110cc6b664eefccd33fbb3f4aa20 to your computer and use it in GitHub Desktop.
NextJS 15 cache handler shim for transforming buffers appropriately
/**
* This cache handler shim ensures compatibility with Redis, by converting
* buffers to strings on save and back to buffers on read, since the data
* is stored as JSON.
*
* This shim is a temporary workaround until Next 15 support is added to
* @neshca/cache-handler:
* https://github.com/caching-tools/next-shared-cache/pull/969
*
* @param {import("@neshca/cache-handler").Handler} handler - The original cache handler to wrap.
* @returns {import("@neshca/cache-handler").Handler} - The transformed cache handler.
*/
function bufferTransformCacheHandlerShim(handler) {
return {
name: handler.name,
async get(key, data) {
const result = await handler.get(key, data)
const value = result?.value
if (value?.kind === 'APP_PAGE') {
if (value.rscData) {
// Convert rscData string to Buffer
// See: https://github.com/vercel/next.js/blob/f5444a16ec2ef7b82d30048890b613aa3865c1f1/packages/next/src/server/response-cache/types.ts#L76
value.rscData = Buffer.from(value.rscData, 'utf-8')
}
if (value.segmentData) {
// Convert segmentData Record<string, string> to Map<string, Buffer>
// See: https://github.com/vercel/next.js/blob/f5444a16ec2ef7b82d30048890b613aa3865c1f1/packages/next/src/server/response-cache/types.ts#L80
value.segmentData = new Map(
Object.entries(value.segmentData).map(([key, value]) => [
key,
Buffer.from(value, 'utf-8'),
]),
)
}
}
if (value?.kind === 'APP_ROUTE' && value?.body) {
// Convert body string to Buffer
// See: https://github.com/vercel/next.js/blob/f5444a16ec2ef7b82d30048890b613aa3865c1f1/packages/next/src/server/response-cache/types.ts#L97
value.body = Buffer.from(value.body, 'utf-8')
}
return result
},
async set(key, data) {
const value = data?.value
if (value?.kind === 'APP_PAGE') {
if (value.rscData) {
// Convert rscData Buffer to string
// See: https://github.com/vercel/next.js/blob/f5444a16ec2ef7b82d30048890b613aa3865c1f1/packages/next/src/server/response-cache/types.ts#L76
value.rscData = value.rscData.toString()
}
if (value.segmentData) {
// Convert segmentData Map<string, Buffer> to Record<string, string>
// See: https://github.com/vercel/next.js/blob/f5444a16ec2ef7b82d30048890b613aa3865c1f1/packages/next/src/server/response-cache/types.ts#L80
value.segmentData = Object.fromEntries(
Array.from(value.segmentData.entries()).map(([key, value]) => [key, value.toString()]),
)
}
}
if (value?.kind === 'APP_ROUTE' && value?.body) {
// Convert body Buffer to string
// See: https://github.com/vercel/next.js/blob/f5444a16ec2ef7b82d30048890b613aa3865c1f1/packages/next/src/server/response-cache/types.ts#L97
value.body = value.body.toString()
}
await handler.set(key, data)
},
async revalidateTag(tag) {
await handler.revalidateTag(tag)
},
async delete(key) {
await handler.delete?.(key)
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment