UNPKG

k-fastify-gateway

Version:
83 lines (66 loc) 2.45 kB
const fp = require('fastify-plugin') const CacheManager = require('cache-manager') const ms = require('ms') const onEnd = require('on-http-end') const getKeys = require('./get-keys') const X_CACHE_EXPIRE = 'x-cache-expire' const X_CACHE_TIMEOUT = 'x-cache-timeout' const X_CACHE_HIT = 'x-cache-hit' const plugin = (fastify, opts, next) => { opts = Object.assign({ stores: [CacheManager.caching({ store: 'memory', max: 1000, ttl: 30 })] }, opts) // creating multi-cache instance const multiCache = CacheManager.multiCaching(opts.stores) fastify.addHook('onRequest', (request, reply, next) => { const { res } = reply const { req } = request onEnd(res, (payload) => { // avoid double caching if (req.cacheHit) return if (payload.headers[X_CACHE_EXPIRE]) { // support service level expiration const keysPattern = payload.headers[X_CACHE_EXPIRE] // delete keys on all cache tiers opts.stores.forEach(cache => getKeys(cache, keysPattern).then(keys => multiCache.del(keys))) } else if (payload.headers[X_CACHE_TIMEOUT]) { // we need to cache response multiCache.set(req.cacheKey, JSON.stringify(payload), { // @NOTE: cache-manager uses seconds as TTL unit // restrict to min value "1 second" ttl: Math.max(ms(payload.headers[X_CACHE_TIMEOUT]), 1000) / 1000 }) } }) return next() }) fastify.addHook('preHandler', async (request, reply) => { const { req } = request if (req.cacheDisabled) return let { url, cacheAppendKey = req => '' } = req cacheAppendKey = await cacheAppendKey(req) const key = req.method + url + cacheAppendKey // ref cache key on req object req.cacheKey = key const cached = await get(multiCache, key) if (!cached) return // respond from cache if there is a hit let { status, headers, data } = JSON.parse(cached) if (typeof data === 'object' && data.type === 'Buffer') { data = Buffer.from(data.data) } headers[X_CACHE_HIT] = '1' // set cached response headers Object.keys(headers).forEach(header => reply.header(header, headers[header])) // send cached payload req.cacheHit = true reply.code(status).send(data) }) return next() } const get = (cache, key) => new Promise((resolve) => { cache.getAndPassUp(key, (_, res) => { resolve(res) }) }) module.exports = fp(plugin)