express-interceptor
Advanced tools
Comparing version 0.1.0 to 1.0.0
29
index.js
@@ -0,1 +1,13 @@ | ||
function validateParams(methods){ | ||
var ACCEPT = ['isInterceptable', 'intercept', 'afterSend']; | ||
for(var k in methods){ | ||
if(ACCEPT.indexOf(k) < 0){ | ||
throw(new Error(k+' isn\'t a valid param ('+ACCEPT.join(', ')+')')); | ||
} | ||
} | ||
if(!('isInterceptable' in methods)){ | ||
throw('isInterceptable is a required param (function)'); | ||
} | ||
} | ||
module.exports = function(fn) { | ||
@@ -6,2 +18,3 @@ var debug = require('debug')('express-interceptor'); | ||
var methods = fn(req,res); | ||
validateParams(methods); | ||
@@ -17,3 +30,3 @@ var originalEnd = res.end; | ||
isFirstWrite = false; | ||
isIntercepting = methods.initerceptPredicate(); | ||
isIntercepting = methods.isInterceptable(); | ||
} | ||
@@ -55,11 +68,9 @@ debug('isIntercepting? %s', isIntercepting); | ||
var oldBody = chunks.join(''); | ||
if (typeof methods.send === 'function'){ | ||
debug(' methods.send is defined'); | ||
if (methods.intercept){ | ||
if(typeof methods.intercept !== 'function'){ | ||
throw new Error('`send` must be a function with the body to be sent as the only param'); | ||
} | ||
res.removeHeader('Content-Length'); | ||
// allow the user to re-write destiny | ||
methods.send(oldBody, function(err,newBody) { | ||
// debug(' newBody is %s',newBody); | ||
if(err){ | ||
return cb && cb(err); | ||
} | ||
// allow the user to re-write response | ||
methods.intercept(oldBody, function(newBody) { | ||
args[0] = newBody; | ||
@@ -66,0 +77,0 @@ originalEnd.apply(res,args); |
{ | ||
"name": "express-interceptor", | ||
"version": "0.1.0", | ||
"version": "1.0.0", | ||
"description": "A tiny interceptor for Express responses", | ||
@@ -10,6 +10,15 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "./node_modules/mocha/bin/mocha -R spec --recursive examples" | ||
"test": "./node_modules/mocha/bin/mocha -R spec --recursive tests" | ||
}, | ||
"author": "AxiomZen (https://axiomzen.co)", | ||
"keywords": ["express", "expressjs", "middleware", "modify", "response", "hack", "interceptor", "hijack"], | ||
"keywords": [ | ||
"express", | ||
"expressjs", | ||
"middleware", | ||
"modify", | ||
"response", | ||
"hack", | ||
"interceptor", | ||
"hijack" | ||
], | ||
"contributors": [ | ||
@@ -27,2 +36,9 @@ { | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/axiomzen/express-interceptor" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/axiomzen/express-interceptor/issues" | ||
}, | ||
"license": "MIT", | ||
@@ -35,3 +51,4 @@ "devDependencies": { | ||
"supertest": "~0.15.0", | ||
"mocha": "~2.1.0" | ||
"mocha": "~2.1.0", | ||
"cheerio": "~0.18.0" | ||
}, | ||
@@ -38,0 +55,0 @@ "dependencies": { |
128
README.md
@@ -1,68 +0,120 @@ | ||
# raison d'etre | ||
# express-interceptor | ||
Born out of the need for a reliable, customized and maintenable middleware we need for Sesame CMS. | ||
_A tiny Express response interceptor_ | ||
[![NPM](https://nodei.co/npm/express-interceptor.png)](https://nodei.co/npm/express-interceptor/) | ||
[![Build Status](https://travis-ci.org/axiomzen/express-interceptor.svg)](https://travis-ci.org/axiomzen/express-interceptor) [![Dependencies](https://david-dm.org/axiomzen/express-interceptor.png)](https://david-dm.org/axiomzen/express-interceptor.png) | ||
<a href="https://zenhub.io"><img src="https://raw.githubusercontent.com/ZenHubIO/support/master/zenhub-badge.png" height="18px"></a> | ||
Express-interceptor allows you to define a previous step before sending a response. This allows you to do anything you want with the response, such as processing, transforming, replacing, or logging it. Express-interceptor allows you to avoid calling `next()` over and over. Further more, you can avoid managing nested scopes. Using a declarative API, it’s simple to use and maintain. | ||
Some use cases include: | ||
- conditionally transform any kind of response | ||
- have access to responses after they are sent | ||
- Transpile custom elements into standard HTML elements. | ||
- Transform JSX or compile less files on the fly. | ||
- Store statistics about responses. | ||
- Set response headers based on tags in the response body. | ||
- Dynamically inject live-reload scripts to HTML. | ||
## API | ||
## Usage | ||
npm i --save express-interceptor | ||
Install the package | ||
npm i --save express-interceptor | ||
Define your interceptor | ||
```javascript | ||
var express = require('express'); | ||
var cheerio = require('cheerio'); | ||
var interceptor = require('express-interceptor'); | ||
app.use(interceptor(function(req,res){ | ||
var app = express(); | ||
var finalParagraphInterceptor = interceptor(function(req, res){ | ||
return { | ||
// define your custom condition to intercept this response | ||
// returning `true` cause to buffer this request, and activate methods below | ||
// otherwise we ignore it completely | ||
initerceptPredicate: function(){ | ||
// Only HTML responses will be intercepted | ||
isInterceptable: function(){ | ||
return /text\/html/.test(res.get('Content-Type')); | ||
}, | ||
// can transform the body, it's a properly encoded String, | ||
// done(err, string) param[1] will become the new response | ||
// may omit secrets, erase words, append stuff, etc. | ||
send: function(body, done) { | ||
// Appends a paragraph at the end of the response body | ||
intercept: function(body, send) { | ||
var $document = cheerio.load(body); | ||
$document('body').append('<p>From interceptor!</p>'); | ||
}, | ||
// useful for caching, keeping stats, etc. | ||
afterSend: function(oldBody, newBody) { | ||
send($document.html()); | ||
} | ||
}; | ||
})); | ||
}) | ||
// Add the interceptor middleware | ||
app.use(finalParagraphInterceptor); | ||
app.use(express.static(__dirname + '/public/')); | ||
app.listen(3000); | ||
``` | ||
You can find many other examples at [/examples folder](https://github.com/axiomzen/express-interceptor/tree/master/examples) - which also happen to be the tests. | ||
You're defining an interceptor that will be executed whenever the response contains html as Content-Type. If this is true, it will append a child at the end of the body. In other words, it will transform the response: | ||
## technicalities | ||
```html | ||
<html> | ||
<head></head> | ||
<body> | ||
<p>"Hello world"</p> | ||
</body> | ||
</html> | ||
``` | ||
Express extends Node.js functionalities, that by default: | ||
Into: | ||
- allow setting and modifying of headers until the moment there is a write to the socket, this will submit headers. | ||
- when `initerceptPredicate` returns true, we will buffer the contents, preventing `.write` to be called | ||
- the whole buffer is presented to `send` function that can modify response headers right there, as well as return a new `body` that will be sent right away. | ||
- `afterSend` is optional and happens on next tick. | ||
- this package tries to be minimally obtrusive on the way Node or Express works, original `write` and `end` methods hijacked but then at the end. | ||
```html | ||
<html> | ||
<head></head> | ||
<body> | ||
<p>"Hello world"</p> | ||
<p>From interceptor!</p> | ||
</body> | ||
</html> | ||
``` | ||
See [more examples](https://github.com/axiomzen/express-interceptor/tree/master/examples). | ||
## similar to | ||
## API | ||
- [tamper](https://www.npmjs.com/package/tamper) | ||
Similar functionality, with different APIs. | ||
* `isInterceptable()` (required): is a predicate function where you define a condition whether or not to intercept a response. Returning `true` buffers the request, and proceeds calling `intercept()` as well as `afterSend()`. Typically, you want to check for this condition in the `res` object in the definition of the middleware. | ||
* `intercept(body, send)` (required): Parse the body as an encoded string. After processing the body, call `send(newBody)` with the content to be sent back to the client. | ||
* `afterSend(oldBody, newBody)`: This method will be called after sending the response to the client – after the `done()` callback in the `send()` method is executed. This method would typically be used to cache something, log stats, fire a job, among other things. | ||
## Similar to | ||
- [express-hijackresponse](https://github.com/papandreou/express-hijackresponse) | ||
Have issues with cache, code is hard to maintain. | ||
Different API, using callbacks with no top down structure, more obtrusive to HTTP internals. | ||
## words of advice | ||
- [tamper](https://www.npmjs.com/package/tamper) | ||
Similar functionality but different internals and API. | ||
This module is new, tests are appreciated. There might be edge cases that need fix. | ||
## Words of advice | ||
Not recommended to intercept and transform big responses. | ||
If your `intercept` method make calls to a database, or needs to make expensive transformations to the original response, you should take in account the time that will add to the response. You should define your `isInterceptable` method carefully, checking the type of the response, or even the route that was fired by the request, also you may consider implementing a cache strategy to get faster responses. | ||
Activate debug with `DEBUG=express-interceptor npm test` | ||
If you face any issue, don't hesitate to submit it [here](https://github.com/axiomzen/express-interceptor/issues). | ||
## Author | ||
* [AxiomZen](https://www.axiomzen.co/). | ||
## Collaborators | ||
* [Fabiano Soriani](https://github.com/flockonus). | ||
* [Raul Pino](https://github.com/p1nox). | ||
## License | ||
[MIT](LICENSE) |
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 bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
18354
19
357
1
1
121
7
6
1