Install @fastify/caching
mainInstall the plugin using npm:
npm i @fastify/cachingrepository·main·Indexed 42 days ago
https://github.com/fastify/fastify-cachingA Fastify plugin for managing HTTP cache headers (ETag, Expires, and Cache-Control) according to RFC 2616. It provides server-side caching via abstract-cache and decorates the reply object with etag() and expires() methods to handle conditional requests and 304 Not Modified responses.
Install the plugin using npm:
npm i @fastify/cachingWhen registering the plugin, you can provide an options object to control cache-control headers and the cache storage engine.
Important Note on Cache Storage: The default cache is an in-memory LRU cache capped at 100,000 items. It is not recommended for production. It is highly recommended to provide a full-featured cache object (e.g., using abstract-cache-redis).
{
privacy: 'value',
expiresIn: 300,
cache: {get, set},
cacheSegment: 'segment-name'
}To add caching capabilities to your Fastify instance, register the @fastify/caching plugin. This plugin decorates the Fastify instance with a cache object and decorates the reply object with etag() and expires() methods. It also automatically handles ETag validation via the onRequest hook and stores successful responses in the cache via the onSend hook.
const fastify = require('fastify')
const caching = require('@fastify/caching')
const app = fastify()
app.register(caching, {
// configuration options
})
app.listen({ port: 3000 })This example demonstrates how to register the plugin with a NOCACHE privacy setting to prevent clients from caching responses.
const http = require('node:http')
const fastify = require('fastify')()
const fastifyCaching = require('@fastify/caching')
fastify.register(
fastifyCaching,
{privacy: fastifyCaching.privacy.NOCACHE},
(err) => { if (err) throw err }
)
fastify.get('/', (req, reply) => {
reply.send({hello: 'world'})
})
fastify.listen({ port: 3000 }, (err) => {
if (err) throw err
http.get('http://127.0.0.1:3000/', (res) => {
console.log(res.headers['cache-control'])
})
})This example shows how to register the plugin with a custom abstract-cache instance (using Redis) to provide server-side caching without setting any cache-control headers on the responses. It also demonstrates how to access the cache via fastify.cache.
const IORedis = require('ioredis')
const redis = new IORedis({host: '127.0.0.1'})
const abcache = require('abstract-cache')({
useAwait: false,
driver: {
name: 'abstract-cache-redis', // must be installed via `npm i`
options: {client: redis}
}
})
const fastify = require('fastify')()
fastify
.register(require('@fastify/redis'), {client: redis})
.register(require('@fastify/caching'), {cache: abcache})
fastify.get('/', (req, reply) => {
fastify.cache.set('hello', {hello: 'world'}, 10000, (err) => {
if (err) return reply.send(err)
reply.send({hello: 'world'})
})
})
fastify.listen({ port: 3000 }, (err) => {
if (err) throw err
})The reply.expires() method allows you to set the expires header. It accepts either a regular JavaScript Date object or a string that is a valid date string according to RFC 2616 §14.21.
// Example usage with a Date object
reply.expires(new Date('2025-01-01'))
// Example usage with a valid date string
reply.expires('Wed, 21 Oct 2015 07:28:00 GMT')The reply.etag() method allows you to set the etag header.
string: An arbitrary string for the tag. If not supplied, a tag is generated using uid-safe synchronously. It is recommended to always supply a value to avoid the synchronous overhead.number (optional): An integer representing the time in milliseconds to cache the ETag for generating 304 responses. The default is 3600000 (1 hour).Behavior:
All incoming requests to paths affected by this plugin are inspected for the if-none-match header. If the header value matches a tag found in the plugin's cache, the response will automatically end with a 304 Not Modified status code.
// Example usage
reply.etag('my-custom-etag', 60000)The following options are available for @fastify/caching:
privacy (Default: undefined): A string valid for a cache-response-directive per RFC 2616.expiresIn (Default: undefined): A value in seconds for the max-age directive. If set (and privacy is not no-cache), ', max-age=<value>' is appended to the cache-control header.cache (Default: abstract-cache.memclient): An abstract-cache protocol-compliant cache object. A cache instance is required for the ETag mechanism to work properly.cacheSegment (Default: '@fastify/caching'): Segment identifier used when communicating with the cache.serverExpiresIn (Default: undefined): A value in seconds for the s-maxage directive (shared caches/CDNs). Should be used alongside expiresIn. When privacy is set to public, ', s-maxage=<value>' is appended to the cache-control header.Ensure your Fastify version is compatible with the version of @fastify/caching you are using:
| Plugin version | Fastify version |
|---|---|
>=9.x | ^5.x |
^8.x | ^4.x |
>=6.x <8.x | ^3.x |
>=4.x <6.x | ^2.x |
>=1.x <4.x | ^1.x |
The reply.etag() method allows you to set an ETag header on the response. This enables conditional requests (e.g., If-None-Match).
value (string): The ETag value. If not provided, a random 18-character string is generated using uid-safe.lifetime (number): The lifetime of the ETag in milliseconds. If not provided or not an integer, it defaults to 3600000 (1 hour). This lifetime is used by the plugin to determine how long to store the ETag in the cache.If the ETag is stored in the cache and a subsequent request arrives with a matching If-None-Match header, the plugin will automatically return a 304 Not Modified response.
app.get('/data', async (request, reply) => {
const data = { hello: 'world' }
// Set an ETag with a 5-minute lifetime (300,000 ms)
reply.etag('my-custom-etag', 300000)
return data
})The reply.expires() method sets the Expires header on the response.
date (Date | string): A Date object or a string representing the expiration date. If a Date object is provided, it is converted to a UTC string.This method is used to define when the response should be considered stale by the client.
app.get('/stale', async (request, reply) => {
const expiry = new Date(Date.now() + 3600000) // 1 hour from now
reply.expires(expiry)
return { status: 'expiring-soon' }
})The plugin exports a privacy object containing standard Cache-control privacy directives to ensure string accuracy.
{
NOCACHE: 'no-cache',
PUBLIC: 'public',
PRIVATE: 'private'
}