cache-manager-express-mw
Advanced tools
Comparing version 0.0.5 to 0.1.0
@@ -31,3 +31,3 @@ module.exports = function(grunt) { | ||
options: { | ||
istanbulOptions: [ "--include-all-sources" ] | ||
includes: [ "src/**/*.js" ] | ||
} | ||
@@ -44,5 +44,5 @@ } | ||
// Default task(s). | ||
grunt.registerTask("default", [ "jshint", "jscs", "test", "coverage" ]); | ||
grunt.registerTask("default", [ "jshint", "jscs", "coverage" ]); | ||
grunt.registerTask("test", [ "mochaTest:test" ]); | ||
grunt.registerTask("coverage", [ "mocha_istanbul" ]); | ||
}; |
{ | ||
"name": "cache-manager-express-mw", | ||
"version": "0.0.5", | ||
"version": "0.1.0", | ||
"description": "Middleware for Express that uses cache-manager to add a caching layer in front of your application.", | ||
"main": "src/index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "grunt" | ||
}, | ||
@@ -25,2 +25,4 @@ "repository": { | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"chai-spies": "^0.7.1", | ||
"grunt": "^1.0.1", | ||
@@ -32,3 +34,4 @@ "grunt-contrib-jshint": "^1.0.0", | ||
"mocha": "^3.0.2", | ||
"mocha-istanbul": "^0.3.0" | ||
"mocha-istanbul": "^0.3.0", | ||
"node-uuid": "^1.4.7" | ||
}, | ||
@@ -35,0 +38,0 @@ "dependencies": { |
# cache-manager-express-mw | ||
Middleware for Express that uses cache-manager to add a caching layer in front of your application. | ||
## Features | ||
* [cache-manager](https://github.com/BryanDonovan/node-cache-manager) is a robust caching solution for node that provides a number of features. | ||
* Uses [HTTP/1.1 Cache-Control header](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) to control the cache behavior. | ||
* Cache keys are autogenerated based on the HTTP method and route to ensure consistency. | ||
## Installation | ||
npm install cache-manager-express-mw | ||
## Examples | ||
You can register the middleware globally using ```app.use()```. | ||
```js | ||
var cacheManager = require("cache-manager"); | ||
var cache = cacheManager.caching({ store: "memory" }); | ||
var cacheManagerExpress = require("cache-manager-express-mw"); | ||
app.use(cacheManagerExpress(cache)); | ||
``` | ||
Alternatively, you register the middleware with a specific route to allow for different routes to use different caching mechanisms and options: | ||
```js | ||
app.get("/", cacheManagerExpress(cache), function(req, res) { | ||
// ... | ||
}); | ||
``` | ||
In addition to the cache, you can pass in a options object to control the behavior of the middleware: | ||
```js | ||
var cacheManagerExpress = require("cache-manager-express-mw"); | ||
app.use(cacheManagerExpress(cache, { prefix: "MyApp" })); | ||
``` | ||
A fully functional sample app is available in this repository under the [sample](sample) directory. | ||
## Options | ||
| Property | Default | Description | | ||
| ---------|-----------|-----------------------------------------------------------------------------------------------| | ||
| prefix | undefined | A prefix to append to the front of the generated cache key in case differentiation is needed. | | ||
| defaults | undefined | An object containing query string default values so that a missing query string value and the specified default resolve to the same cache key. For example, ```{ defaults: { val: "abc" } }``` will ensure that the routes ```/a/b/c``` and ```/a/b/c?val=abc``` resolve to the same cache key.| | ||
## License | ||
[MIT](LICENSE) |
102
src/index.js
@@ -1,9 +0,7 @@ | ||
var _ = require("lodash"), | ||
Promise = require("bluebird"); | ||
var _ = require("lodash"), | ||
getCacheKey = require("./helpers/getcachekey.js"), | ||
getCachingStrategy = require("./helpers/getcachingstrategy.js"), | ||
Promise = require("bluebird"); | ||
var caching = function(cache, options) { | ||
var prefix = options && options.prefix ? `${options.prefix}:` : ""; | ||
var cacheControlAccessibility = | ||
options && options.cacheControlAccessibility ? options.cacheControlAccessibility : "public"; | ||
var isProduction = function() { | ||
@@ -13,24 +11,10 @@ return process.env.NODE_ENV === "production"; | ||
var getMaxAge = function(res) { | ||
var cacheControlHeader = res.get("Cache-Control"); | ||
if (!cacheControlHeader) { | ||
return; | ||
} | ||
var match = cacheControlHeader.match(/.*max-age=(\d+).*/); | ||
if (!match || match.length < 2) { | ||
return; | ||
} | ||
var maxAge = parseInt(match[1]); | ||
return maxAge; | ||
}; | ||
var getValue = function(key) { | ||
return new Promise(function(resolve, reject) { | ||
cache.get(key, function(err, result) { | ||
if (err && !isProduction()) { | ||
var cacheGet = Promise.promisify(cache.get); | ||
return cacheGet(key) | ||
.catch(err => { | ||
if (!isProduction()) { | ||
console.warn("Error retrieving value from cache: " + err); | ||
} | ||
resolve(result); | ||
}); | ||
}); | ||
}; | ||
@@ -42,15 +26,19 @@ | ||
} | ||
return new Promise(function(resolve, reject) { | ||
cache.ttl(key, function(err, result) { | ||
if (err && !isProduction()) { | ||
var cacheTtl = Promise.promisify(cache.ttl); | ||
return cacheTtl(key) | ||
.catch(err => { | ||
if (!isProduction()) { | ||
console.warn("Error retrieving ttl from cache: " + err); | ||
} | ||
resolve(result); | ||
throw err; | ||
}); | ||
}); | ||
}; | ||
var setCacheControlHeader = function(res, ttl) { | ||
var setCacheControlHeader = function(res, accessibility, ttl) { | ||
if (ttl) { | ||
res.set("Cache-Control", `${cacheControlAccessibility}, max-age=${ttl}`); | ||
if (accessibility) { | ||
res.set("cache-control", `${accessibility}, max-age=${ttl}`); | ||
} else { | ||
res.set("cache-control", `max-age=${ttl}`); | ||
} | ||
} | ||
@@ -60,5 +48,11 @@ }; | ||
var handleCacheHit = function(res, key, value) { | ||
getTtl(key) | ||
.then(ttl => setCacheControlHeader(res, ttl)) | ||
.then(x => { | ||
if (!value) { | ||
return Promise.resolve(false); | ||
} | ||
return getTtl(key) | ||
.then(ttl => { | ||
setCacheControlHeader(res, value.accessibility, ttl); | ||
}) | ||
.then(() => { | ||
// This is dumb, but it results in a prettier JSON format | ||
@@ -68,6 +62,8 @@ try { | ||
res.status(value.statusCode).json(obj); | ||
} catch (error) { | ||
} catch (err) { | ||
res.status(value.statusCode).send(value.body); | ||
} | ||
}); | ||
}) | ||
.return(true) | ||
.catch(err => false); | ||
}; | ||
@@ -82,7 +78,17 @@ | ||
if (/^2/.test(res.statusCode)) { | ||
cache.set(key, { statusCode: res.statusCode, body: body }, { ttl: getMaxAge(res) }, function(err) { | ||
if (err && !isProduction()) { | ||
console.warn("Error setting value in cache: " + err); | ||
} | ||
}); | ||
var cachingStrategy = getCachingStrategy(res); | ||
if (cachingStrategy) { | ||
var cacheSet = Promise.promisify(cache.set); | ||
var cacheValue = { | ||
statusCode: res.statusCode, | ||
body: body, | ||
accessibility: cachingStrategy.accessibility | ||
}; | ||
cacheSet(key, cacheValue, { ttl: cachingStrategy.maxAge }) | ||
.catch(err => { | ||
if (!isProduction()) { | ||
console.warn("Error setting value in cache: " + err); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -100,11 +106,7 @@ | ||
var query = _.assign({ }, options.defaults, req.query); | ||
var sortedQueryString = _(query).keys().sortBy().map(key => `${key}=${query[key]}`).join("&"); | ||
var key = `${prefix}${req.method}:${req.path}?${sortedQueryString}`; | ||
var key = getCacheKey(req, _.get(options, "prefix"), _.get(options, "defaults")); | ||
getValue(key) | ||
.then(value => { | ||
if (value) { | ||
handleCacheHit(res, key, value); | ||
} else { | ||
.then(value => handleCacheHit(res, key, value)) | ||
.then(isHit => { | ||
if (!isHit) { | ||
handleCacheMiss(res, key); | ||
@@ -114,3 +116,3 @@ next(); | ||
}) | ||
.catch(error => { | ||
.catch(err => { | ||
if (!isProduction()) { | ||
@@ -117,0 +119,0 @@ console.warn("Error accessing cache: " + err); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
40134
17
735
1
51
10
1