0.1.1 • Published 8 years ago

@bubenguru/koa-response-cache v0.1.1

Weekly downloads
14
License
MIT
Repository
github
Last release
8 years ago

koa-response-cache

Build Status Coverage Status NPM version

Storage-agnostic response cache for Koa@2.

Standard - JavaScript Style Guide

Usage

First we need to configure cache storage:

const cache = require('@bubenguru/koa-response-cache')

cache.config({
  ttl: 60000, // set default TTL
  key: (ctx, namespace) => ctx.request.href, // method thats provides cache-key
  storage: redisStorage // storage instance; structure will be described below
})

Then we can use cache as koa@2/koa-router middleware:

const Koa = require('koa')
const Router = require('koa-router')
const cache = require('@bubenguru/koa-response-cache')

const app = new Koa()
const router = new Router()

let counter = 0
router.get(
  '/counter',
  cache.use({
    ttl: '60s' // redeclare global TTL, now cache lives 60 seconds
  }),
  (ctx) => {
    ctx.response.body = { counter: ++counter }
  }
)
router.post(
  '/counter',
  cache.remove(), // remove cached value for /counter route
  (ctx) => {
    counter = 0
    ctx.response.body = { counter: 0 }
  }
)

app.use(router.routes())
app.listen(8080)

Check our cache works:

$ curl http://localhost:8080/ # { "counter": 1 }
$ curl http://localhost:8080/ # { "counter": 1 }
$ sleep 10
$ curl http://localhost:8080/ # { "counter": 2 }

Config options

  • ttl: Number|String — time to live in milliseconds. Can be a number of milliseconds or string in ms package format. Default is 60000ms (1 minute);
  • key: Function(context, namespace) — function thats generates cache-key from current route context and cache action (use, remove or clear). By default returning ctx.request.href;
  • storage: Object — storage instance that contains get, set, remove and clear methods. See "Storage" section for more.

Cache methods

  • create([options]) — create new cache instance;
  • config([options]) — set configuration for current cache instance;
  • use([options]) — create middleware for cache current route;
  • remove([options]) — create middleware for remove cached value after successful execution of next route handlers;

Storage

Storage instance must implement only following methods: get, set, remove and clear:

const redisStorage = { // storage configuration
  /**
   * Set value to cache
   * @param {String} key Cache key
   * @param {*} data JSON-serializable data
   * @param {Number} [ttl=0] Cache lifetime in milliseconds
   * @throws {TypeError} If meet cyclic object value
   */
  async set (key, data, ttl = 0) {
    if (ttl) {
      await redis.psetex(`cache:${key}`, ttl, JSON.stringify(data))
    } else {
      await redis.set(`cache:${key}`, JSON.stringify(data))
    }
  },

  /**
   * Retrieve data from cache by key
   * @param {String} key Cache key
   * @return {Any|undefined} Cached value or undefined if cache is expired or not exists
   * @throws {SyntaxError} If can't parse cached data
   */
  async get (key) {
    const response = await redis.get(`cache:${key}`)
    if (response) {
      return JSON.parse(response)
    }
  },

  /**
   * Remove key from cache
   * @param {String} key Cache key
   */
  async remove (key) {
    await redis.del(`cache:${key}`)
  },

  /**
   * Remove keys and nested keys
   * @param {String} key First part of key
   */
  async clear (key) {
    const pattern = `cache:${key.replace('*', '\\*')}`
    while (true) {
      // get all keys prefixed with `key`
      const [ cursor, keys ] = await redis.scan(0, `${pattern}*`, 100)
      // remove all scanned keys in one multi/exec transaction
      await keys.reduce((tx, key) => tx.del(key), redis.multi()).exec()

      if (Number(cursor) === 0) break
    }
  }
}

Methods can return any value or Promise thats will be resolved and used as storage response. Method get MUST return undefined if key is not present in cache.

0.1.1

8 years ago

0.0.0

8 years ago