Skip to content

Commit

Permalink
Update cache-handler-redis example dependencies (#59458)
Browse files Browse the repository at this point in the history
This pull request updates the dependencies in the cache-handler-redis
example. It updates the versions of the `@neshca/cache-handler`, which
introduces new features.

---------

Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com>
  • Loading branch information
better-salmon and delbaoliveira authored Jan 10, 2024
1 parent 21dbfec commit 8694ed6
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 155 deletions.
57 changes: 16 additions & 41 deletions examples/cache-handler-redis/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Next.js Redis Cache Integration Example

This repository provides a production-ready example of how to enhance the caching capabilities of Next.js and use Redis to share the cache for multiple instances of your app. It's made possible by the [`@neshca/cache-handler`](https://github.com/caching-tools/next-shared-cache/tree/canary/packages/cache-handler) package, which replaces the default Next.js cache handler while preserving the original functionality of reading pre-rendered pages from the file system.

This particular example is designed to be self-hosted.
This example is tailored for self-hosted setups and demonstrates how to use Redis as a shared cache. It is built on the principles of the `@neshca/cache-handler` package, which replaces the default Next.js cache handler and adds advanced caching features.

## How to use

Expand All @@ -29,51 +27,28 @@ docker-compose up -d

Then, build and start the Next.js app as usual.

## Notes

- **Handlers:** The `@neshca/cache-handler` package now includes new [Handlers](https://caching-tools.github.io/next-shared-cache/redis-stack) for Redis, making it almost zero-config.
## Documentation

- **Think different:** Ensure that your Redis server is operational and accessible before starting your Next.js application to prevent any connection errors. Remember to flush the cache or use namespacing if you preserve the Redis instance between builds.
For detailed information on configuration and usage, please refer to our comprehensive [Documentation ↗](https://caching-tools.github.io/next-shared-cache).

- **Configure:** Add your Redis credentials to the provided `cache-handler-redis*` files. Learn more about connecting to Redis with Node.js [here](https://redis.io/docs/connect/clients/nodejs/).
## Key Features and Considerations

- **Opt out of Redis during build if needed:**
To build your Next.js app without connecting to Redis, wrap the `onCreation` callback with a condition as shown below:
- **Handlers:** The `@neshca/cache-handler` package includes [Handlers](https://caching-tools.github.io/next-shared-cache/redis-stack) for seamless integration with Redis.

```js
if (process.env.SERVER_STARTED) {
IncrementalCache.onCreation(() => {
// Your code here
})
}
```
- **Redis Server Setup:** Ensure your Redis server is running and properly configured before starting your Next.js application.

This condition helps avoid potential issues if your Redis server is deployed concurrently with the app build.
- **Configure Redis Credentials:** Update the `cache-handler-redis*` files with your Redis credentials. Connection details can be found [here](https://redis.io/docs/connect/clients/nodejs/).

- **Opt out file system reads, writes or both:**
By default, the `@neshca/cache-handler` uses the file system to preserve the original behavior of Next.js, for instance, reading pre-rendered pages from the Pages dir. To opt out of this functionality, add the `diskAccessMode` option:

```js
IncrementalCache.onCreation(() => {
return {
diskAccessMode: 'read-no/write-no', // Default is 'read-yes/write-yes'
cache: {
// The same cache configuration as in the example
},
}
})
```
- **Building Without Redis:** To build the app without connecting to Redis, conditionally create the Handler. Check the [documentation](https://caching-tools.github.io/next-shared-cache/configuration/opt-out-cache-on-build) for more details.

This may be useful if you use only App dir and don't mind if Redis instance fails.
## Development and Production Considerations

Provided `docker-compose.yml` is for local development only. It is not suitable for production use. Read more about [Redis installation](https://redis.io/docs/install/) and [management](https://redis.io/docs/management/) before deploying your application to production.
- The provided `docker-compose.yml` is intended for local development. For production deployment, refer to the official [Redis installation](https://redis.io/docs/install/) and [management](https://redis.io/docs/management/) guidelines.

### How to clear the Redis cache
- **Clearing Redis Cache:** To clear the Redis cache, use RedisInsight Workbench or the following CLI command:

If you need to clear the Redis cache, use RedisInsight Workbench or run the following command:

```bash
docker exec -it redis-stack redis-cli
127.0.0.1:6379> flushall
OK
```
```bash
docker exec -it redis-stack redis-cli
127.0.0.1:6379> flushall
OK
```
145 changes: 66 additions & 79 deletions examples/cache-handler-redis/cache-handler-redis-custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,100 +3,87 @@ const {
replaceJsonWithBase64,
} = require('@neshca/json-replacer-reviver')
const { IncrementalCache } = require('@neshca/cache-handler')
const createLruCache = require('@neshca/cache-handler/local-lru').default
const { createClient } = require('redis')

/** @type {import('@neshca/cache-handler').TagsManifest} */
const localTagsManifest = {
version: 1,
items: {},
}
const REVALIDATED_TAGS_KEY = 'sharedRevalidatedTags'

const TAGS_MANIFEST_KEY = 'sharedTagsManifest'

function createRedisClient(url) {
const client = createClient({
url,
})

client.on('error', (error) => {
console.error('Redis error:', error.message)
})

return client
}

async function connect(client) {
try {
await client.connect()
} catch (error) {
console.error('Redis connection error:', error.message)
}
}
const client = createClient({
url: process.env.REDIS_URL ?? 'redis://localhost:6379',
})

IncrementalCache.onCreation(() => {
const client = createRedisClient(
process.env.REDIS_URL ?? 'redis://localhost:6379'
)
client.on('error', (error) => {
console.error('Redis error:', error.message)
})

connect(client).then(() => {
console.log('Redis connected')
})
IncrementalCache.onCreation(async () => {
// read more about TTL limitations https://caching-tools.github.io/next-shared-cache/configuration/ttl
const useTtl = false

return {
cache: {
async get(key) {
try {
const result = (await client.get(key)) ?? null
await client.connect()

if (!result) {
return null
}
const redisCache = {
async get(key) {
try {
const result = (await client.get(key)) ?? null

// use reviveFromBase64Representation to restore binary data from Base64
return JSON.parse(result, reviveFromBase64Representation)
} catch (error) {
if (!result) {
return null
}
},
async set(key, value) {
try {

// use reviveFromBase64Representation to restore binary data from Base64
return JSON.parse(result, reviveFromBase64Representation)
} catch (error) {
console.error('cache.get', error)

return null
}
},
async set(key, value, ttl) {
try {
await client.set(
key,
// use replaceJsonWithBase64 to store binary data in Base64 and save space
await client.set(key, JSON.stringify(value, replaceJsonWithBase64))
} catch (error) {
// ignore because value will be written to disk
}
},
async getTagsManifest() {
try {
const remoteTagsManifest = await client.hGetAll(TAGS_MANIFEST_KEY)
JSON.stringify(value, replaceJsonWithBase64),
useTtl && typeof ttl === 'number' ? { EX: ttl } : undefined
)
} catch (error) {
console.error('cache.set', error)
}
},
async getRevalidatedTags() {
try {
const sharedRevalidatedTags = await client.hGetAll(REVALIDATED_TAGS_KEY)

if (!remoteTagsManifest) {
return localTagsManifest
}
const entries = Object.entries(sharedRevalidatedTags)

Object.entries(remoteTagsManifest).reduce(
(acc, [tag, revalidatedAt]) => {
acc[tag] = { revalidatedAt: parseInt(revalidatedAt ?? '0', 10) }
return acc
},
localTagsManifest.items
)
const revalidatedTags = Object.fromEntries(
entries.map(([tag, revalidatedAt]) => [tag, Number(revalidatedAt)])
)

return localTagsManifest
} catch (error) {
return localTagsManifest
}
},
async revalidateTag(tag, revalidatedAt) {
try {
await client.hSet(TAGS_MANIFEST_KEY, {
[tag]: revalidatedAt,
})
} catch (error) {
localTagsManifest.items[tag] = { revalidatedAt }
}
},
return revalidatedTags
} catch (error) {
console.error('cache.getRevalidatedTags', error)
}
},
async revalidateTag(tag, revalidatedAt) {
try {
await client.hSet(REVALIDATED_TAGS_KEY, {
[tag]: revalidatedAt,
})
} catch (error) {
console.error('cache.revalidateTag', error)
}
},
}

const localCache = createLruCache({
useTtl,
})

return {
cache: [redisCache, localCache],
useFileSystem: !useTtl,
}
})

Expand Down
52 changes: 24 additions & 28 deletions examples/cache-handler-redis/cache-handler-redis.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
const { IncrementalCache } = require('@neshca/cache-handler')
const { createHandler } = require('@neshca/cache-handler/redis-stack') // @neshca/cache-handler/redis-strings also available
const createRedisCache = require('@neshca/cache-handler/redis-stack').default
const createLruCache = require('@neshca/cache-handler/local-lru').default
const { createClient } = require('redis')

function createRedisClient(url) {
const client = createClient({
url,
})

client.on('error', (error) => {
console.error('Redis error:', error.message)
})
const client = createClient({
url: process.env.REDIS_URL ?? 'redis://localhost:6379',
})

return client
}
client.on('error', (error) => {
console.error('Redis error:', error.message)
})

async function connect(client) {
try {
await client.connect()
} catch (error) {
console.error('Redis connection error:', error.message)
}
}
IncrementalCache.onCreation(async () => {
// read more about TTL limitations https://caching-tools.github.io/next-shared-cache/configuration/ttl
const useTtl = true

const client = createRedisClient(
process.env.REDIS_URL ?? 'redis://localhost:6379'
)
await client.connect()

connect(client).then(() => {
console.log('Redis connected')
})

IncrementalCache.onCreation(
createHandler({
const redisCache = await createRedisCache({
client,
useTtl,
})

const localCache = createLruCache({
useTtl,
})
)

return {
cache: [redisCache, localCache],
useFileSystem: !useTtl,
}
})

module.exports = IncrementalCache
14 changes: 7 additions & 7 deletions examples/cache-handler-redis/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
"start": "next start"
},
"dependencies": {
"next": "latest",
"next": "^14",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@neshca/cache-handler": "latest",
"@neshca/json-replacer-reviver": "latest",
"@types/node": "^20.9.1",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@neshca/cache-handler": "^0.6",
"@neshca/json-replacer-reviver": "^1",
"@types/node": "^20.10.4",
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"redis": "latest",
"typescript": "^5.2.2"
"typescript": "^5.3.3"
}
}

0 comments on commit 8694ed6

Please sign in to comment.