Drop-in Next.js CacheHandler that stores ISR cache and tag-based revalidation in DynamoDB.
Built for Next.js deployments on AWS Amplify, Lambda, App Runner, ECS, or anywhere your filesystem is ephemeral — so your ISR cache and revalidateTag() calls actually survive deploys and work across instances.
npm install nextjs-dynamodb-cache
The default Next.js cache handler writes to disk. On Amplify/Lambda/Fargate that means:
- ISR pages re-render from scratch after every deploy
revalidateTag()only invalidates the instance that received the call- Multiple instances serve inconsistent stale content
This package replaces the filesystem cache with DynamoDB. Cache entries are shared across all instances and survive deploys. Tag revalidation uses a GSI so it's a single query, not a full-table scan.
npx nextjs-dynamodb-cache-create-table --table my-app-cache --region eu-central-1This is idempotent. It creates the table, the tag-index GSI, and enables TTL on expiresAt. PAY_PER_REQUEST billing by default.
Or programmatically:
import { createCacheTable } from "nextjs-dynamodb-cache/create-table";
await createCacheTable({ tableName: "my-app-cache", region: "eu-central-1" });// next.config.js
module.exports = {
cacheHandler: require.resolve("./cache-handler.mjs"),
cacheMaxMemorySize: 0, // disable in-memory cache so DDB is authoritative
};// cache-handler.mjs
import { DynamoDBCacheHandler } from "nextjs-dynamodb-cache";
export default DynamoDBCacheHandler.configure({
tableName: "my-app-cache",
region: "eu-central-1",
// optional: defaultTtlSeconds, keyPrefix, log, credentials
});That's it. revalidateTag() and revalidatePath() now work cluster-wide.
The Lambda/Amplify role needs:
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem",
"dynamodb:Query",
"dynamodb:BatchWriteItem"
],
"Resource": [
"arn:aws:dynamodb:eu-central-1:*:table/my-app-cache",
"arn:aws:dynamodb:eu-central-1:*:table/my-app-cache/index/*"
]
}| Option | Default | Description |
|---|---|---|
tableName |
env NEXT_CACHE_DYNAMODB_TABLE or nextjs-cache |
DynamoDB table name |
region |
env NEXT_CACHE_AWS_REGION / AWS_REGION / eu-central-1 |
AWS region |
credentials |
default chain | Explicit AWS credentials |
tagIndexName |
tag-index |
GSI name used for revalidateTag |
defaultTtlSeconds |
604800 (7 days) |
TTL when Next.js omits revalidate |
keyPrefix |
"" |
Prefix for keys (multi-app table sharing) |
log |
true |
Verbose logs on errors and revalidations |
- Schema: single table, partition key
key. Each entry hasvalue,expiresAt(TTL), and optionaltags. - Tags: for each tag a sentinel row
__tag__<tag>__<key>is written.revalidateTag(t)queries thetag-indexGSI ontag = t, collects the original cache keys, and batch-deletes them. - TTL: DynamoDB native TTL on
expiresAtcleans up stale entries automatically — no cron required. - Resilience: every operation is wrapped in try/catch; a DynamoDB outage degrades to "cache miss," never a 500.
Battle-tested in production on Next.js 16 + Amplify hosting on the HonestDog marketplace. Compatible with Next.js 14, 15, and 16 cache-handler APIs.
MIT