fastify
Advanced tools
Comparing version 0.7.1 to 0.8.0
@@ -9,2 +9,4 @@ 'use strict' | ||
const pinoHttp = require('pino-http') | ||
const Middie = require('middie') | ||
const Reply = require('./lib/reply') | ||
@@ -26,2 +28,4 @@ const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS'] | ||
const router = wayfarer('/404') | ||
const middie = Middie(_runMiddlewares) | ||
const run = middie.run | ||
const map = new Map() | ||
@@ -54,2 +58,5 @@ pluginLoader(fastify, {}) | ||
// middleware support | ||
fastify.use = middie.use | ||
return fastify | ||
@@ -59,2 +66,12 @@ | ||
logger(req, res) | ||
run(req, res) | ||
} | ||
function _runMiddlewares (err, req, res) { | ||
if (err) { | ||
const reply = new Reply(req, res, null) | ||
reply.send(err) | ||
return | ||
} | ||
router(stripUrl(req.url), req, res) | ||
@@ -129,2 +146,3 @@ } | ||
} | ||
// chainable api | ||
@@ -131,0 +149,0 @@ return fastify |
'use strict' | ||
const urlUtil = require('url') | ||
const pump = require('pump') | ||
const jsonParser = require('body/json') | ||
const safeStringify = require('fast-safe-stringify') | ||
const validation = require('./validation') | ||
const validateSchema = validation.validate | ||
const serialize = validation.serialize | ||
const Reply = require('./reply') | ||
const Request = require('./request') | ||
@@ -20,2 +19,3 @@ function bodyParsed (handle, params, req, res) { | ||
} | ||
return parsed | ||
@@ -66,2 +66,3 @@ } | ||
} | ||
return handle | ||
@@ -93,4 +94,3 @@ } | ||
const reply = new Reply(req, res, handle) | ||
const handler = handle.handler | ||
const result = handler(request, reply) | ||
const result = handle.handler(request, reply) | ||
if (result && typeof result.then === 'function') { | ||
@@ -106,97 +106,3 @@ reply.send(result) | ||
function Reply (req, res, handle) { | ||
this.res = res | ||
this.handle = handle | ||
this.req = req | ||
this.sent = false | ||
} | ||
/** | ||
* Instead of using directly res.end(), we are using setImmediate(…) | ||
* This because we have observed that with this technique we are faster at responding to the various requests, | ||
* since the setImmediate forwards the res.end at the end of the poll phase of libuv in the event loop. | ||
* So we can gather multiple requests and then handle all the replies in a different moment, | ||
* causing a general improvement of performances, ~+10%. | ||
*/ | ||
Reply.prototype.send = function (payload) { | ||
if (this.sent) { | ||
throw new Error('Reply already sent') | ||
} | ||
if (payload instanceof Error) { | ||
if (!this.res.statusCode || this.res.statusCode < 500) { | ||
this.res.statusCode = 500 | ||
} | ||
setImmediate(wrapReplyEnd, this, safeStringify(payload)) | ||
return | ||
} | ||
if (payload && typeof payload.then === 'function') { | ||
return payload.then(wrapReplySend(this)).catch(wrapReplySend(this)) | ||
} | ||
if (!payload && !this.res.statusCode) { | ||
this.res.statusCode = 204 | ||
} | ||
if (payload && payload._readableState) { | ||
if (!this.res.getHeader('Content-Type')) { | ||
this.res.setHeader('Content-Type', 'application/octet-stream') | ||
} | ||
return pump(payload, this.res, wrapPumpCallback(this)) | ||
} | ||
if (!this.res.getHeader('Content-Type') || this.res.getHeader('Content-Type') === 'application/json') { | ||
this.res.setHeader('Content-Type', 'application/json') | ||
const str = serialize(this.handle, payload) | ||
if (!this.res.getHeader('Content-Length')) { | ||
this.res.setHeader('Content-Length', Buffer.byteLength(str)) | ||
} | ||
setImmediate(wrapReplyEnd, this, str) | ||
return | ||
} | ||
setImmediate(wrapReplyEnd, this, payload) | ||
return | ||
} | ||
Reply.prototype.header = function (key, value) { | ||
this.res.setHeader(key, value) | ||
return this | ||
} | ||
Reply.prototype.code = function (code) { | ||
this.res.statusCode = code | ||
return this | ||
} | ||
function wrapPumpCallback (reply) { | ||
return function pumpCallback (err) { | ||
if (err) { | ||
reply.req.log.error(err) | ||
setImmediate(wrapReplyEnd, reply) | ||
} | ||
} | ||
} | ||
function wrapReplyEnd (reply, payload) { | ||
reply.sent = true | ||
reply.res.end(payload) | ||
} | ||
function wrapReplySend (reply, payload) { | ||
return function send (payload) { | ||
return reply.send(payload) | ||
} | ||
} | ||
function Request (params, req, body, query) { | ||
this.params = params | ||
this.req = req | ||
this.body = body | ||
this.query = query | ||
} | ||
module.exports = build | ||
module.exports[Symbol.for('internals')] = { bodyParsed, routerHandler, Request, handler, Reply } | ||
module.exports[Symbol.for('internals')] = { bodyParsed, routerHandler, handler } |
{ | ||
"name": "fastify", | ||
"version": "0.7.1", | ||
"version": "0.8.0", | ||
"description": "Fast and low overhead web framework, for Node.js", | ||
@@ -31,26 +31,35 @@ "main": "fastify.js", | ||
"bluebird": "^3.4.7", | ||
"coveralls": "^2.11.15", | ||
"express": "^4.14.0", | ||
"hapi": "^16.0.2", | ||
"koa": "^1.2.4", | ||
"pre-commit": "^1.1.3", | ||
"cors": "^2.8.1", | ||
"coveralls": "^2.11.16", | ||
"dns-prefetch-control": "^0.1.0", | ||
"express": "^4.14.1", | ||
"frameguard": "^3.0.0", | ||
"hapi": "^16.1.0", | ||
"helmet": "^3.4.0", | ||
"hide-powered-by": "^1.0.0", | ||
"hsts": "^2.0.0", | ||
"ienoopen": "^1.0.0", | ||
"koa": "^1.2.5", | ||
"pre-commit": "^1.2.2", | ||
"request": "^2.79.0", | ||
"snazzy": "^5.0.0", | ||
"split2": "^2.1.0", | ||
"snazzy": "^6.0.0", | ||
"split2": "^2.1.1", | ||
"standard": "^8.6.0", | ||
"take-five": "^1.3.1", | ||
"tap": "^9.0.1", | ||
"then-sleep": "^1.0.1" | ||
"take-five": "^1.3.3", | ||
"tap": "^10.1.0", | ||
"then-sleep": "^1.0.1", | ||
"x-xss-protection": "^1.0.0" | ||
}, | ||
"dependencies": { | ||
"ajv": "^4.10.4", | ||
"ajv": "^4.11.2", | ||
"body": "^5.1.0", | ||
"boot-in-the-arse": "^0.3.0", | ||
"fast-json-stringify": "^0.10.3", | ||
"fast-safe-stringify": "^1.1.3", | ||
"fast-safe-stringify": "^1.1.6", | ||
"middie": "^0.1.1", | ||
"pathname-match": "^1.2.0", | ||
"pino-http": "^2.2.0", | ||
"pino-http": "^2.4.1", | ||
"pump": "^1.0.2", | ||
"wayfarer": "^6.3.0" | ||
"wayfarer": "^6.4.0" | ||
} | ||
} |
@@ -75,3 +75,3 @@ <p align="center"> | ||
* Koa: 9640 req/sec | ||
* *Fastify: 19860 req/sec* | ||
* *Fastify: 20256 req/sec* | ||
@@ -96,2 +96,3 @@ All benchmarks where average taken over 5 seconds, on the second run of `autocannon -c 100 -d 5 -p 10 localhost:3000`. | ||
* <a href="#register"><code>fastify.<b>register()</b></code></a> | ||
* <a href="#use"><code>fastify.<b>use()</b></code></a> | ||
@@ -135,3 +136,4 @@ <a name="constructor"></a> | ||
Function called when all the plugins has been loaded. | ||
Function called when all the plugins has been loaded. | ||
Emitted by [boot-in-the-arse](https://github.com/mcollina/boot-in-the-arse). | ||
@@ -142,3 +144,4 @@ | ||
Starts the server on the given port after all the plugins are loaded, internally waits for the `.ready()` event. | ||
Starts the server on the given port after all the plugins are loaded, internally waits for the `.ready()` event. | ||
The callback is the same as the Node core. | ||
@@ -166,6 +169,8 @@ | ||
[fast-json-stringify][fast-json-stringify]. | ||
* `handler(request, reply)`: the function that will handle this request. | ||
`request` is defined in [Request](#request). | ||
`reply` is defined in [Reply](#reply). | ||
* `handler(request, reply)`: the function that will handle this request. | ||
`request` is defined in [Request](#request). | ||
`reply` is defined in [Reply](#reply). | ||
Example: | ||
@@ -275,3 +280,3 @@ ```js | ||
const fs = require('fs') | ||
const stream = fs.createReadStream('some-file', 'utf8') | ||
const stream = fs.createReadStream('some-file', 'utf8') | ||
reply.send(stream) | ||
@@ -325,6 +330,10 @@ }) | ||
### fastify.register(plugin, [options], [callback]) | ||
Used to register one or more plugins. | ||
`plugin` can be a single function or an array of functions. | ||
In case of the array of functions, the same options object and callback will be passed to them. | ||
[boot-in-the-arse](https://github.com/mcollina/boot-in-the-arse) is used to load the plugins. | ||
Used to register one or more plugins. | ||
`plugin` can be a single function or an array of functions. | ||
In case of the array of functions, the same options object and callback will be passed to them. | ||
[boot-in-the-arse](https://github.com/mcollina/boot-in-the-arse) is used to load the plugins. | ||
Example: | ||
@@ -368,9 +377,44 @@ ```js | ||
<a name="use"></a> | ||
### fastify.use(middleware(req, res, next)) | ||
Use to add one or more middlewares, [express](http://npm.im/express)/[connect](https://www.npmjs.com/package/connect) style. | ||
This does not support the full syntax `middleware(err, req, res, next)`, | ||
because error handling is done inside Fastify. | ||
Benchmarks with cors and helmet (used as single modules): | ||
* Express: 9.6k req/sec | ||
* *Fastify: 14.4k req/sec* | ||
Example: | ||
```js | ||
const fastify = require('fastify')() | ||
const cors = require('cors') | ||
const helmet = require('helmet') | ||
fastify | ||
.use(cors()) | ||
.use(helmet()) | ||
.get('/', function (req, reply) { | ||
reply.header('Content-Type', 'application/json').code(200) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
fastify.listen(3000, err => { | ||
if (err) throw err | ||
console.log(`server listening on ${fastify.server.address().port}`) | ||
}) | ||
``` | ||
<a name="logging"></a> | ||
## Logging | ||
Since Fastify is really focused on performances, we choose the best logger to achieve the goal. **[Pino](https://github.com/pinojs/pino)**! | ||
Since Fastify is really focused on performances, we choose the best logger to achieve the goal. **[Pino](https://github.com/pinojs/pino)**! | ||
By default Fastify uses [pino-http](https://github.com/pinojs/pino-http) as logger, with the log level setted to `'fatal'`. | ||
If you want to pass some options to the logger, just pass the logger option to fastify. | ||
You can find all the options [here](https://github.com/pinojs/pino#pinoopts-stream). If you want to pass a custom stream to the Pino instance, just add the stream field to the logger object. | ||
You can find all the options in the [Pino documentation](https://github.com/pinojs/pino/blob/master/docs/API.md#pinooptions-stream). If you want to pass a custom stream to the Pino instance, just add the stream field to the logger object. | ||
```js | ||
@@ -413,2 +457,3 @@ const split = require('split2') | ||
<a name="license"></a> | ||
## License | ||
@@ -415,0 +460,0 @@ |
@@ -8,7 +8,7 @@ 'use strict' | ||
const internals = require('../../lib/tier-node')[Symbol.for('internals')] | ||
const Reply = require('../../lib/reply') | ||
test('Reply should be an object', t => { | ||
t.plan(1) | ||
t.is(typeof internals.Reply, 'function') | ||
t.is(typeof Reply, 'function') | ||
}) | ||
@@ -21,3 +21,3 @@ | ||
function handle () {} | ||
const reply = new internals.Reply(request, response, handle) | ||
const reply = new Reply(request, response, handle) | ||
t.is(typeof reply, 'object') | ||
@@ -36,5 +36,5 @@ t.is(typeof reply.send, 'function') | ||
const response = { setHeader: () => {} } | ||
const reply = new internals.Reply(request, response, null) | ||
t.type(reply.code(1), internals.Reply) | ||
t.type(reply.header('hello', 'world'), internals.Reply) | ||
const reply = new Reply(request, response, null) | ||
t.type(reply.code(1), Reply) | ||
t.type(reply.header('hello', 'world'), Reply) | ||
}) | ||
@@ -41,0 +41,0 @@ |
@@ -7,2 +7,3 @@ 'use strict' | ||
const internals = require('../../lib/tier-node')[Symbol.for('internals')] | ||
const Request = require('../../lib/request') | ||
const buildSchema = require('../../lib/validation').build | ||
@@ -12,4 +13,4 @@ | ||
t.plan(5) | ||
const req = new internals.Request('params', 'req', 'body', 'query') | ||
t.type(req, internals.Request) | ||
const req = new Request('params', 'req', 'body', 'query') | ||
t.type(req, Request) | ||
t.equal(req.params, 'params') | ||
@@ -169,5 +170,8 @@ t.equal(req.req, 'req') | ||
method: 'GET', | ||
url: 'http://example.com' | ||
url: 'http://example.com', | ||
log: { | ||
error: () => {} | ||
} | ||
} | ||
handle(null, req, res) | ||
}) |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
80709
48
2422
460
10
21
4
6
+ Addedmiddie@^0.1.1
+ Addedmiddie@0.1.1(transitive)
Updatedajv@^4.11.2
Updatedfast-safe-stringify@^1.1.6
Updatedpino-http@^2.4.1
Updatedwayfarer@^6.4.0