fastify
Advanced tools
Comparing version 0.26.2 to 0.27.0
@@ -39,15 +39,19 @@ <h1 align="center">Fastify</h1> | ||
```js | ||
fastify.decorateReply('utility', () => { | ||
fastify.decorateReply('utility', function () { | ||
// something very useful | ||
}) | ||
Note: using an arrow function will break the binding of `this` to the Fastify `reply` instance. | ||
``` | ||
<a name="decorate-request"></a> | ||
**decorateRequest** | ||
<a name="decorate-request"></a> | ||
**decorateRequest** | ||
As above, this api is needed if you want to add new methods to the `Request` core object. | ||
Just call the `decorateRequest` api and pass the name of the new property and its value. | ||
```js | ||
fastify.decorateRequest('utility', () => { | ||
fastify.decorateRequest('utility', function () { | ||
// something very useful | ||
}) | ||
Note: using an arrow function will break the binding of `this` to the Fastify `request` instance. | ||
``` | ||
@@ -54,0 +58,0 @@ |
@@ -93,3 +93,3 @@ <h1 align="center">Fastify</h1> | ||
module.exports = function (fastify, options, next) { | ||
fastify.get('/', schema, function (req, reply) { | ||
fastify.get('/', function (req, reply) { | ||
reply.send({ hello: 'world' }) | ||
@@ -96,0 +96,0 @@ }) |
@@ -7,4 +7,4 @@ <h1 align="center">Fastify</h1> | ||
- `'onRequest'` | ||
- `'preRouting'` | ||
- `'preHandler'` | ||
- `'onResponse'` | ||
- `'onClose'` | ||
@@ -18,2 +18,12 @@ | ||
}) | ||
fastify.addHook('preHandler', (req, reply, next) => { | ||
// some code | ||
next() | ||
}) | ||
fastify.addHook('onResponse', (res, next) => { | ||
// some code | ||
next() | ||
}) | ||
``` | ||
@@ -25,7 +35,8 @@ | ||
If you want to pass a custom error code to the user, just pass it as second parameter to `next()`: | ||
If you want to pass a custom error code to the user, just use `reply.code()`: | ||
```js | ||
fastify.addHook('onRequest', (req, res, next) => { | ||
// some code | ||
next(new Error('some error'), 400) | ||
reply.code(400) | ||
next(new Error('some error')) | ||
}) | ||
@@ -35,3 +46,3 @@ ``` | ||
The function signature is always the same, `request`, `response`, `next`, it changes a little bit only in the `'preHandler'` hook, where the first two arguments are [`request`](https://github.com/fastify/fastify/blob/master/docs/Request.md) and [`reply`](https://github.com/fastify/fastify/blob/master/docs/Reply.md) core Fastify objects. | ||
Note that in the `'preHandler'` hook the request and rply objects are different from `'onRequest'`, because the two arguments are [`request`](https://github.com/fastify/fastify/blob/master/docs/Request.md) and [`reply`](https://github.com/fastify/fastify/blob/master/docs/Reply.md) core Fastify objects. | ||
@@ -50,3 +61,3 @@ <a name="on-close"></a> | ||
### Scope | ||
Talking about scope, the hooks works in a slightly different way from the Request/Reply encapsulation model. For instance, `onRequest`, `preRouting` and `onClose` are never encapsulated, not matter where you are declaring them, while the `preHandler` hook is always encapsulated if you declare it inside a `register`. | ||
Except for `'onClose'` all the hooks are encapsulated this means that you can decide where your hooks should run by using `register` as explained in the [plugins guide](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md). | ||
@@ -53,0 +64,0 @@ <a name="before-handler"></a> |
@@ -11,23 +11,23 @@ <h1 align="center">Fastify</h1> | ||
│ | ||
└─▶ onRequest Hook | ||
│ | ||
4**/5** ◀─┴─▶ run Middlewares | ||
│ | ||
4**/5** ◀─┴─▶ preRouting Hook | ||
│ | ||
4**/5** ◀─┴─▶ Routing | ||
│ | ||
404 ◀─┴─▶ Parsing | ||
│ | ||
415 ◀─┴─▶ Validation | ||
│ | ||
400 ◀─┴─▶ preHandler Hook | ||
│ | ||
4**/5** ◀─┴─▶ beforeHandler | ||
│ | ||
4**/5** ◀─┴─▶ User Handler | ||
│ | ||
└─▶ Reply | ||
│ | ||
└─▶ Outgoing Response | ||
└─▶ Routing | ||
│ | ||
404 ◀─┴─▶ onRequest Hook | ||
│ | ||
4**/5** ◀─┴─▶ run Middlewares | ||
│ | ||
4**/5** ◀─┴─▶ Parsing | ||
│ | ||
415 ◀─┴─▶ Validation | ||
│ | ||
400 ◀─┴─▶ preHandler Hook | ||
│ | ||
4**/5** ◀─┴─▶ beforeHandler | ||
│ | ||
4**/5** ◀─┴─▶ User Handler | ||
│ | ||
└─▶ Reply | ||
│ │ | ||
│ └─▶ Outgoing Response | ||
│ | ||
└─▶ onResponse Hook | ||
``` |
@@ -32,2 +32,3 @@ <h1 align="center">Fastify</h1> | ||
Also remember that middlewares can be encapsulated, this means that you can decide where your middlewares should run by using `register` as explained in the [plugins guide](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md). | ||
@@ -34,0 +35,0 @@ <a name="restrict-usage"></a> |
@@ -42,3 +42,3 @@ <h1 align="center">Fastify</h1> | ||
The Fastify's plugin model is fully reentrant and graph-based, it handles without any kind of problem asynchronous code and it guarantees the load order of the plugins, event the close order! *How?* Glad you asked, checkout [`avvio`](https://github.com/mcollina/avvio)! | ||
The Fastify's plugin model is fully reentrant and graph-based, it handles without any kind of problem asynchronous code and it guarantees the load order of the plugins, even the close order! *How?* Glad you asked, checkout [`avvio`](https://github.com/mcollina/avvio)! | ||
@@ -132,3 +132,3 @@ Inside a plugin you can do whatever you want, register routes, utilities (we'll see this in a moment) and do nested registers, just remember to call `next` when everything is set up! | ||
```js | ||
fastify.decorateReply('html', payload => { | ||
fastify.decorateReply('html', function (payload) { | ||
this.type('text/html') // this is the 'Reply' object | ||
@@ -160,3 +160,3 @@ this.send(generateHtml(payload)) | ||
```js | ||
fastify.decorateRequest('setHeader', header => { | ||
fastify.decorateRequest('setHeader', function (header) { | ||
this.isHappy = this.req.headers[header] | ||
@@ -250,3 +250,2 @@ }) | ||
``` | ||
*Note that middlewares executed [before](https://github.com/fastify/fastify/blob/master/docs/Lifecycle.md) routing and the encapsulation model is not applied, this means that if you declare a middleware inside a plugin it will run for all the plugins.* | ||
@@ -253,0 +252,0 @@ <a name="distribution"></a> |
@@ -44,4 +44,3 @@ <h1 align="center">Fastify</h1> | ||
### Serialize | ||
Usually you will send your data to the clients via JSON, and Fastify has two powerful tools to help you, [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) and [fast-safe-stringify](https://www.npmjs.com/package/fast-safe-stringify). | ||
The first one is used if you have provided an output schema in the route options, otherwise the second one will do de job. We encourage you to use an output schema, it will increase your throughput by x1-4 depending by your payload. | ||
Usually you will send your data to the clients via JSON, and Fastify has a powerful tools to help you: [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify), which is used if you have provided an output schema in the route options. We encourage you to use an output schema, as it will increase your throughput by x1-4 depending on your payload, and it will prevent accidental disclosure of sensitive information. | ||
@@ -48,0 +47,0 @@ Example: |
@@ -25,6 +25,2 @@ 'use strict' | ||
}) | ||
.addHook('preRouting', function (req, res, next) { | ||
console.log('preRouting') | ||
next() | ||
}) | ||
.addHook('preHandler', function (request, reply, next) { | ||
@@ -34,2 +30,6 @@ console.log('preHandler') | ||
}) | ||
.addHook('onResponse', function (res, next) { | ||
console.log('onResponse') | ||
next() | ||
}) | ||
@@ -36,0 +36,0 @@ fastify.get('/', opts, function (req, reply) { |
173
fastify.js
@@ -7,5 +7,4 @@ 'use strict' | ||
const https = require('https') | ||
const pinoHttp = require('pino-http') | ||
const Middie = require('middie') | ||
const fastseries = require('fastseries') | ||
const runHooks = require('fastseries')() | ||
var shot = null | ||
@@ -23,3 +22,3 @@ try { shot = require('shot') } catch (e) { } | ||
const Hooks = require('./lib/hooks') | ||
const serializers = require('./lib/serializers') | ||
const loggerUtils = require('./lib/logger') | ||
@@ -33,17 +32,21 @@ function build (options) { | ||
var logger | ||
if (options.logger && isValidLogger(options.logger)) { | ||
logger = pinoHttp({ logger: options.logger, serializers }) | ||
if (isValidLogger(options.logger)) { | ||
logger = loggerUtils.createLogger({ logger: options.logger, serializers: loggerUtils.serializers }) | ||
} else { | ||
options.logger = options.logger || {} | ||
options.logger.level = options.logger.level || 'fatal' | ||
options.logger.serializers = options.logger.serializers || serializers | ||
logger = pinoHttp(options.logger) | ||
options.logger.serializers = options.logger.serializers || loggerUtils.serializers | ||
logger = loggerUtils.createLogger(options.logger) | ||
} | ||
const router = FindMyWay({ defaultRoute: defaultRoute }) | ||
const middie = Middie(_runMiddlewares) | ||
const run = middie.run | ||
const map = new Map() | ||
const runHooks = fastseries() | ||
// logger utils | ||
const genReqId = loggerUtils.reqIdGenFactory() | ||
const now = loggerUtils.now | ||
const OnResponseState = loggerUtils.OnResponseState | ||
const onResponseIterator = loggerUtils.onResponseIterator | ||
const onResponseCallback = loggerUtils.onResponseCallback | ||
const app = avvio(fastify, {}) | ||
@@ -119,3 +122,5 @@ // Override to allow the plugin incapsulation | ||
// middleware support | ||
fastify.use = middie.use | ||
fastify.use = use | ||
fastify._middie = Middie(onRunMiddlewares) | ||
fastify._middlewares = [] | ||
@@ -131,54 +136,71 @@ // exposes the routes map | ||
function fastify (req, res) { | ||
logger(req, res) | ||
req.id = genReqId() | ||
req.log = res.log = logger.child({ req: req }) | ||
// onRequest hook | ||
setImmediate( | ||
runHooks, | ||
new State(req, res), | ||
hookIterator, | ||
fastify._hooks.onRequest, | ||
middlewareCallback | ||
) | ||
res._startTime = now() | ||
res._context = null | ||
res.on('finish', onResFinished) | ||
res.on('error', onResFinished) | ||
router.lookup(req, res) | ||
} | ||
function _runMiddlewares (err, req, res) { | ||
if (err) { | ||
const reply = new Reply(req, res, null) | ||
reply.send(err) | ||
return | ||
function onResFinished (err) { | ||
this.removeListener('finish', onResFinished) | ||
this.removeListener('error', onResFinished) | ||
var ctx = this._context | ||
if (ctx !== null && ctx.onResponse.length > 0) { | ||
// deferring this with setImmediate will | ||
// slow us by 10% | ||
runHooks(new OnResponseState(err, this), | ||
onResponseIterator, | ||
ctx.onResponse, | ||
wrapOnResponseCallback) | ||
} else { | ||
onResponseCallback(err, this) | ||
} | ||
} | ||
// preRouting hook | ||
setImmediate( | ||
runHooks, | ||
new State(req, res), | ||
hookIterator, | ||
fastify._hooks.preRouting, | ||
routeCallback | ||
) | ||
function wrapOnResponseCallback (err) { | ||
onResponseCallback(this.err || err, this.res) | ||
} | ||
function State (req, res) { | ||
this.req = req | ||
this.res = res | ||
function listen (port, address, cb) { | ||
const hasAddress = arguments.length === 3 | ||
const _cb = (hasAddress) ? cb : address | ||
fastify.ready(function (err) { | ||
if (err) return _cb(err) | ||
if (hasAddress) { | ||
server.listen(port, address, _cb) | ||
} else { | ||
server.listen(port, _cb) | ||
} | ||
listening = true | ||
}) | ||
} | ||
function hookIterator (fn, cb) { | ||
fn(this.req, this.res, cb) | ||
function startHooks (req, res, params, store) { | ||
res._context = store | ||
runHooks( | ||
new State(req, res, params, store), | ||
hookIterator, | ||
store.onRequest, | ||
middlewareCallback | ||
) | ||
} | ||
function middlewareCallback (err, code) { | ||
function middlewareCallback (err) { | ||
if (err) { | ||
const reply = new Reply(this.req, this.res, null) | ||
if (code[0]) reply.code(code[0]) | ||
const reply = new Reply(this.req, this.res, this.store) | ||
reply.send(err) | ||
return | ||
} | ||
run(this.req, this.res) | ||
this.store._middie.run(this.req, this.res, this) | ||
} | ||
function routeCallback (err, code) { | ||
function onRunMiddlewares (err, req, res, ctx) { | ||
if (err) { | ||
const reply = new Reply(this.req, this.res, null) | ||
if (code[0]) reply.code(code[0]) | ||
const reply = new Reply(req, res, ctx.store) | ||
reply.send(err) | ||
@@ -188,19 +210,16 @@ return | ||
router.lookup(this.req, this.res) | ||
handleRequest(req, res, ctx.params, ctx.store) | ||
} | ||
function listen (port, address, cb) { | ||
const hasAddress = arguments.length === 3 | ||
const _cb = (hasAddress) ? cb : address | ||
fastify.ready(function (err) { | ||
if (err) return _cb(err) | ||
if (hasAddress) { | ||
server.listen(port, address, _cb) | ||
} else { | ||
server.listen(port, _cb) | ||
} | ||
listening = true | ||
}) | ||
function State (req, res, params, store) { | ||
this.req = req | ||
this.res = res | ||
this.params = params | ||
this.store = store | ||
} | ||
function hookIterator (fn, cb) { | ||
fn(this.req, this.res, cb) | ||
} | ||
function override (instance, fn, opts) { | ||
@@ -211,2 +230,3 @@ if (fn[Symbol.for('skip-override')]) { | ||
const middlewares = Object.assign([], instance._middlewares) | ||
instance = Object.create(instance) | ||
@@ -218,3 +238,9 @@ instance._Reply = Reply.buildReply(instance._Reply) | ||
instance._RoutePrefix = buildRoutePrefix(instance._RoutePrefix, opts) | ||
instance._middlewares = [] | ||
instance._middie = Middie(onRunMiddlewares) | ||
for (var i = 0; i < middlewares.length; i++) { | ||
instance.use.apply(instance, middlewares[i]) | ||
} | ||
return instance | ||
@@ -282,6 +308,9 @@ } | ||
contentTypeParser: self._contentTypeParser, | ||
onRequest: self._hooks.onRequest, | ||
preHandler: self._hooks.preHandler, | ||
RoutePrefix: self._RoutePrefix, | ||
beforeHandler: options.beforeHandler, | ||
config: options.config | ||
onResponse: options.onResponse, | ||
config: options.config, | ||
middie: self._middie | ||
}) | ||
@@ -318,4 +347,7 @@ } | ||
opts.contentTypeParser || _fastify._contentTypeParser, | ||
opts.onRequest || _fastify._hooks.onRequest, | ||
[], | ||
config | ||
opts.onResponse || _fastify._hooks.onResponse, | ||
config, | ||
opts.middie || _fastify._middie | ||
) | ||
@@ -337,3 +369,3 @@ | ||
map.get(url)[opts.method] = store | ||
router.on(opts.method, url, handleRequest, store) | ||
router.on(opts.method, url, startHooks, store) | ||
} else { | ||
@@ -343,3 +375,3 @@ const node = {} | ||
map.set(url, node) | ||
router.on(opts.method, url, handleRequest, store) | ||
router.on(opts.method, url, startHooks, store) | ||
} | ||
@@ -353,3 +385,3 @@ done() | ||
function Store (schema, handler, Reply, Request, contentTypeParser, preHandler, config) { | ||
function Store (schema, handler, Reply, Request, contentTypeParser, onRequest, preHandler, onResponse, config, middie) { | ||
this.schema = schema | ||
@@ -360,4 +392,7 @@ this.handler = handler | ||
this.contentTypeParser = contentTypeParser | ||
this.onRequest = onRequest | ||
this.preHandler = preHandler | ||
this.onResponse = onResponse | ||
this.config = config | ||
this._middie = middie | ||
} | ||
@@ -411,2 +446,12 @@ | ||
function use (url, fn) { | ||
if (typeof url === 'string') { | ||
const prefix = this._RoutePrefix.prefix | ||
url = prefix + (url === '/' && prefix.length > 0 ? '' : url) | ||
} | ||
this._middlewares.push([url, fn]) | ||
this._middie.use(url, fn) | ||
return this | ||
} | ||
function addHook (name, fn) { | ||
@@ -432,3 +477,3 @@ this._hooks.add(name, fn) | ||
}) | ||
logger.logger.error(e, 'client error') | ||
logger.error(e, 'client error') | ||
socket.end(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: 'application/json'\r\n\r\n${body}`) | ||
@@ -435,0 +480,0 @@ } |
@@ -5,4 +5,4 @@ 'use strict' | ||
'onRequest', | ||
'preRouting', | ||
'preHandler', | ||
'onResponse', | ||
'onClose' | ||
@@ -13,4 +13,4 @@ ] | ||
this.onRequest = [] | ||
this.preRouting = [] | ||
this.preHandler = [] | ||
this.onResponse = [] | ||
this._fastify = fastify | ||
@@ -37,5 +37,5 @@ } | ||
const H = new _Hooks() | ||
H.onRequest = h.onRequest | ||
H.preRouting = h.preRouting | ||
H.onRequest = Object.create(h.onRequest) | ||
H.preHandler = Object.create(h.preHandler) | ||
H.onResponse = Object.create(h.onResponse) | ||
H._fastify = h._fastify | ||
@@ -42,0 +42,0 @@ return H |
@@ -6,3 +6,2 @@ /* eslint-disable no-useless-return */ | ||
const pump = require('pump') | ||
const safeStringify = require('fast-safe-stringify') | ||
const xtend = require('xtend') | ||
@@ -54,3 +53,3 @@ const validation = require('./validation') | ||
this, | ||
safeStringify(payload.output.payload) | ||
stringify(payload.output.payload) | ||
) | ||
@@ -65,3 +64,3 @@ return | ||
this._req.log.error(payload) | ||
this._req.log.error({ res: this.res, error: payload }, payload.message) | ||
this.res.setHeader('Content-Type', 'application/json') | ||
@@ -68,0 +67,0 @@ setImmediate( |
'use strict' | ||
const fastJsonStringify = require('fast-json-stringify') | ||
const fastSafeStringify = require('fast-safe-stringify') | ||
const Ajv = require('ajv') | ||
@@ -76,3 +75,3 @@ const ajv = new Ajv({ coerceTypes: true }) | ||
if (!responseSchemaDef) { | ||
return fastSafeStringify(data) | ||
return JSON.stringify(data) | ||
} | ||
@@ -86,6 +85,10 @@ if (responseSchemaDef[statusCode]) { | ||
} | ||
return fastSafeStringify(data) | ||
return JSON.stringify(data) | ||
} | ||
function isValidLogger (logger) { | ||
if (!logger) { | ||
return false | ||
} | ||
var result = true | ||
@@ -92,0 +95,0 @@ const methods = ['info', 'error', 'debug', 'fatal', 'warn', 'trace', 'child'] |
{ | ||
"name": "fastify", | ||
"version": "0.26.2", | ||
"version": "0.27.0", | ||
"description": "Fast and low overhead web framework, for Node.js", | ||
@@ -51,2 +51,3 @@ "main": "fastify.js", | ||
"fast-json-body": "^1.1.0", | ||
"fast-safe-stringify": "^1.2.0", | ||
"fastify-plugin": "^0.1.0", | ||
@@ -78,3 +79,2 @@ "frameguard": "^3.0.0", | ||
"fast-json-stringify": "^0.12.2", | ||
"fast-safe-stringify": "^1.2.0", | ||
"fastify-cli": "^0.6.1", | ||
@@ -84,4 +84,4 @@ "fastseries": "^1.7.2", | ||
"flatstr": "^1.0.5", | ||
"middie": "^1.0.0", | ||
"pino-http": "^2.6.1", | ||
"middie": "^2.0.0", | ||
"pino": "^4.7.1", | ||
"pump": "^1.0.2", | ||
@@ -88,0 +88,0 @@ "xtend": "^4.0.1" |
@@ -114,2 +114,3 @@ <div align="center"> | ||
- [`fastify-accepts-serializer`](https://github.com/fastify/fastify-accepts-serializer) to serialize to output according to `Accept` header | ||
- [`fastify-sse`](https://github.com/lolo32/fastify-sse) to provide Server-Sent Events with `reply.sse( … )` to Fastify | ||
- *More coming soon* | ||
@@ -116,0 +117,0 @@ |
@@ -45,2 +45,7 @@ 'use strict' | ||
fastify.addHook('onResponse', function (res, next) { | ||
t.ok('onResponse called') | ||
next() | ||
}) | ||
fastify.get('/', function (req, reply) { | ||
@@ -102,3 +107,3 @@ t.is(req.req.raw, 'the request is coming') | ||
test('onRequest hook should not support encapsulation / 1', t => { | ||
test('onRequest hook should support encapsulation / 1', t => { | ||
t.plan(3) | ||
@@ -115,7 +120,7 @@ const fastify = Fastify() | ||
t.error(err) | ||
t.is(fastify._hooks.onRequest.length, 1) | ||
t.is(fastify._hooks.onRequest.length, 0) | ||
}) | ||
}) | ||
test('onRequest hook should not support encapsulation / 2', t => { | ||
test('onRequest hook should support encapsulation / 2', t => { | ||
t.plan(3) | ||
@@ -134,7 +139,7 @@ const fastify = Fastify() | ||
t.error(err) | ||
t.is(fastify._hooks.onRequest.length, 2) | ||
t.is(fastify._hooks.onRequest.length, 1) | ||
}) | ||
}) | ||
test('onRequest hook should not support encapsulation / 3', t => { | ||
test('onRequest hook should support encapsulation / 3', t => { | ||
t.plan(13) | ||
@@ -150,3 +155,3 @@ const fastify = Fastify() | ||
t.ok(req.req.first) | ||
t.ok(req.req.second) | ||
t.notOk(req.req.second) | ||
reply.send({ hello: 'world' }) | ||
@@ -196,7 +201,7 @@ }) | ||
test('preRouting hook should not support encapsulation / 4', t => { | ||
test('preHandler hook should support encapsulation / 5', t => { | ||
t.plan(13) | ||
const fastify = Fastify() | ||
fastify.addHook('preRouting', (req, res, next) => { | ||
fastify.addHook('preHandler', (req, res, next) => { | ||
req.first = true | ||
@@ -207,4 +212,4 @@ next() | ||
fastify.get('/first', (req, reply) => { | ||
t.ok(req.req.first) | ||
t.ok(req.req.second) | ||
t.ok(req.first) | ||
t.notOk(req.second) | ||
reply.send({ hello: 'world' }) | ||
@@ -214,3 +219,3 @@ }) | ||
fastify.register((instance, opts, next) => { | ||
instance.addHook('preRouting', (req, res, next) => { | ||
instance.addHook('preHandler', (req, res, next) => { | ||
req.second = true | ||
@@ -221,4 +226,4 @@ next() | ||
instance.get('/second', (req, reply) => { | ||
t.ok(req.req.first) | ||
t.ok(req.req.second) | ||
t.ok(req.first) | ||
t.ok(req.second) | ||
reply.send({ hello: 'world' }) | ||
@@ -256,14 +261,46 @@ }) | ||
test('preHandler hook should support encapsulation / 5', t => { | ||
t.plan(13) | ||
test('onResponse hook should support encapsulation / 1', t => { | ||
t.plan(3) | ||
const fastify = Fastify() | ||
fastify.addHook('preHandler', (req, res, next) => { | ||
req.first = true | ||
fastify.register((instance, opts, next) => { | ||
instance.addHook('onResponse', () => {}) | ||
t.is(instance._hooks.onResponse.length, 1) | ||
next() | ||
}) | ||
fastify.ready(err => { | ||
t.error(err) | ||
t.is(fastify._hooks.onResponse.length, 0) | ||
}) | ||
}) | ||
test('onResponse hook should support encapsulation / 2', t => { | ||
t.plan(3) | ||
const fastify = Fastify() | ||
fastify.addHook('onResponse', () => {}) | ||
fastify.register((instance, opts, next) => { | ||
instance.addHook('onResponse', () => {}) | ||
t.is(instance._hooks.onResponse.length, 2) | ||
next() | ||
}) | ||
fastify.ready(err => { | ||
t.error(err) | ||
t.is(fastify._hooks.onResponse.length, 1) | ||
}) | ||
}) | ||
test('onResponse hook should support encapsulation / 3', t => { | ||
t.plan(12) | ||
const fastify = Fastify() | ||
fastify.addHook('onResponse', (res, next) => { | ||
t.ok('onResponse called') | ||
next() | ||
}) | ||
fastify.get('/first', (req, reply) => { | ||
t.ok(req.first) | ||
t.notOk(req.second) | ||
reply.send({ hello: 'world' }) | ||
@@ -273,4 +310,4 @@ }) | ||
fastify.register((instance, opts, next) => { | ||
instance.addHook('preHandler', (req, res, next) => { | ||
req.second = true | ||
instance.addHook('onResponse', (res, next) => { | ||
t.ok('onResponse called') | ||
next() | ||
@@ -280,4 +317,2 @@ }) | ||
instance.get('/second', (req, reply) => { | ||
t.ok(req.first) | ||
t.ok(req.second) | ||
reply.send({ hello: 'world' }) | ||
@@ -284,0 +319,0 @@ }) |
@@ -14,4 +14,4 @@ 'use strict' | ||
t.ok(Array.isArray(hooks.onRequest)) | ||
t.ok(Array.isArray(hooks.preRouting)) | ||
t.ok(Array.isArray(hooks.preHandler)) | ||
t.ok(Array.isArray(hooks.onResponse)) | ||
}) | ||
@@ -26,9 +26,9 @@ | ||
hooks.add('preRouting', noop) | ||
t.is(hooks.preRouting.length, 1) | ||
t.is(typeof hooks.preRouting[0], 'function') | ||
hooks.add('preHandler', noop) | ||
t.is(hooks.preHandler.length, 1) | ||
t.is(typeof hooks.preHandler[0], 'function') | ||
hooks.add('onResponse', noop) | ||
t.is(hooks.onResponse.length, 1) | ||
t.is(typeof hooks.onResponse[0], 'function') | ||
}) | ||
@@ -35,0 +35,0 @@ |
@@ -30,10 +30,12 @@ 'use strict' | ||
test('reply.header, reply.code and reply-serializer should return an instance of Reply', t => { | ||
t.plan(3) | ||
test('reply.send throw with circular JSON', t => { | ||
t.plan(1) | ||
const request = {} | ||
const response = { setHeader: () => {} } | ||
const reply = new Reply(request, response, null) | ||
t.type(reply.code(1), Reply) | ||
t.type(reply.serializer(() => {}), Reply) | ||
t.type(reply.header('hello', 'world'), Reply) | ||
t.throws(() => { | ||
var obj = {} | ||
obj.obj = obj | ||
reply.send(JSON.stringify(obj)) | ||
}) | ||
}) | ||
@@ -40,0 +42,0 @@ |
@@ -37,1 +37,23 @@ 'use strict' | ||
}) | ||
test('register after listen using Promise.resolve()', t => { | ||
t.plan(2) | ||
const f = Fastify() | ||
const handler = (req, res) => res.send({}) | ||
Promise.resolve() | ||
.then(() => { | ||
f.get('/', handler) | ||
f.register((f2, options, done) => { | ||
f2.get('/plugin', handler) | ||
done() | ||
}) | ||
}) | ||
.then(() => { | ||
f.listen(0, '127.0.0.1', (err) => { | ||
t.error(err) | ||
t.pass() | ||
f.close() | ||
}) | ||
}) | ||
}) |
@@ -8,24 +8,23 @@ 'use strict' | ||
const Fastify = require('..') | ||
var fastify = null | ||
var stream = split(JSON.parse) | ||
try { | ||
fastify = Fastify({ | ||
logger: { | ||
stream: stream, | ||
level: 'info' | ||
} | ||
test('test log stream', t => { | ||
t.plan(7) | ||
var fastify = null | ||
var stream = split(JSON.parse) | ||
try { | ||
fastify = Fastify({ | ||
logger: { | ||
stream: stream, | ||
level: 'info' | ||
} | ||
}) | ||
} catch (e) { | ||
t.fail() | ||
} | ||
fastify.get('/', function (req, reply) { | ||
t.ok(req.log) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
t.pass() | ||
} catch (e) { | ||
t.fail() | ||
} | ||
fastify.get('/', function (req, reply) { | ||
t.ok(req.log) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
test('test log stream', t => { | ||
t.plan(6) | ||
fastify.listen(0, err => { | ||
@@ -42,3 +41,2 @@ t.error(err) | ||
t.equal(line.res.statusCode, 200, 'statusCode is 200') | ||
t.end() | ||
}) | ||
@@ -48,20 +46,47 @@ }) | ||
test('can use external logger instance', t => { | ||
test('test error log stream', t => { | ||
t.plan(7) | ||
var fastify = null | ||
var stream = split(JSON.parse) | ||
try { | ||
fastify = Fastify({ | ||
logger: { | ||
stream: stream, | ||
level: 'info' | ||
} | ||
}) | ||
} catch (e) { | ||
t.fail() | ||
} | ||
const lines = [] | ||
fastify.get('/error', function (req, reply) { | ||
t.ok(req.log) | ||
reply.send(new Error('kaboom')) | ||
}) | ||
fastify.listen(0, err => { | ||
t.error(err) | ||
fastify.server.unref() | ||
http.get('http://localhost:' + fastify.server.address().port + '/error') | ||
stream.once('data', line => { | ||
t.ok(line.req, 'req is defined') | ||
t.ok(line.res, 'res is defined') | ||
t.equal(line.msg, 'kaboom', 'message is set') | ||
t.equal(line.req.method, 'GET', 'method is get') | ||
t.equal(line.res.statusCode, 500, 'statusCode is 500') | ||
}) | ||
}) | ||
}) | ||
test('can use external logger instance', t => { | ||
const lines = ['log success', 'request completed'] | ||
t.plan(lines.length + 2) | ||
const splitStream = split(JSON.parse) | ||
splitStream.on('data', (line) => { | ||
lines.push(line) | ||
t.is(line.msg, lines.shift()) | ||
}) | ||
splitStream.on('end', () => { | ||
t.is(lines.length, 4) | ||
t.is(lines[0].msg, 'log success') | ||
t.is(lines[1].msg, 'log success') | ||
t.is(lines[2].msg, 'log success') | ||
t.is(lines[3].msg, 'request completed') | ||
}) | ||
const logger = require('pino')(splitStream) | ||
logger.info('log success') | ||
@@ -74,8 +99,2 @@ const localFastify = Fastify({logger: logger}) | ||
reply.send({ hello: 'world' }) | ||
setImmediate(() => { | ||
localFastify.server.close(() => { | ||
splitStream.end() | ||
localFastify.server.unref() | ||
}) | ||
}) | ||
}) | ||
@@ -85,4 +104,8 @@ | ||
t.error(err) | ||
logger.info('log success') | ||
http.get('http://localhost:' + localFastify.server.address().port + '/foo') | ||
http.get('http://localhost:' + localFastify.server.address().port + '/foo', (res) => { | ||
res.resume() | ||
res.on('end', () => { | ||
localFastify.server.close() | ||
}) | ||
}) | ||
}) | ||
@@ -93,4 +116,17 @@ }) | ||
t.plan(2) | ||
var fastify = null | ||
var stream = split(JSON.parse) | ||
try { | ||
fastify = Fastify({ | ||
logger: { | ||
stream: stream, | ||
level: 'info' | ||
} | ||
}) | ||
} catch (e) { | ||
t.fail() | ||
} | ||
t.ok(fastify.logger) | ||
t.is(typeof fastify.logger, 'function') | ||
t.is(typeof fastify.logger, 'object') | ||
}) |
@@ -122,1 +122,362 @@ 'use strict' | ||
}) | ||
test('middlewares should support encapsulation / 1', t => { | ||
t.plan(9) | ||
const instance = fastify() | ||
instance.register((i, opts, done) => { | ||
t.ok(i._middlewares.length === 0) | ||
i.use(function (req, res, next) { | ||
t.fail('this should not be called') | ||
next() | ||
}) | ||
t.ok(i._middlewares.length > 0) | ||
done() | ||
}) | ||
instance.get('/', function (request, reply) { | ||
t.ok(instance._middlewares.length === 0) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
instance.listen(0, err => { | ||
t.error(err) | ||
t.ok(instance._middlewares.length === 0) | ||
t.tearDown(instance.server.close.bind(instance.server)) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
}) | ||
}) | ||
}) | ||
test('middlewares should support encapsulation / 2', t => { | ||
t.plan(13) | ||
const instance = fastify() | ||
instance.use(function (req, res, next) { | ||
req.global = true | ||
next() | ||
}) | ||
instance.register((i, opts, done) => { | ||
i.use(function (req, res, next) { | ||
req.local = true | ||
next() | ||
}) | ||
i.get('/local', function (request, reply) { | ||
t.ok(request.req.global) | ||
t.ok(request.req.local) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
done() | ||
}) | ||
instance.get('/global', function (request, reply) { | ||
t.ok(request.req.global) | ||
t.notOk(request.req.local) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
instance.listen(0, err => { | ||
t.error(err) | ||
t.tearDown(instance.server.close.bind(instance.server)) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/global' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/local' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
}) | ||
}) | ||
}) | ||
}) | ||
test('middlewares should support encapsulation / 3', t => { | ||
t.plan(15) | ||
const instance = fastify() | ||
instance.use(function (req, res, next) { | ||
req.global = true | ||
next() | ||
}) | ||
instance.register((i, opts, done) => { | ||
i.use(function (req, res, next) { | ||
req.firstLocal = true | ||
next() | ||
}) | ||
i.use(function (req, res, next) { | ||
req.secondLocal = true | ||
next() | ||
}) | ||
i.get('/local', function (request, reply) { | ||
t.ok(request.req.global) | ||
t.ok(request.req.firstLocal) | ||
t.ok(request.req.secondLocal) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
done() | ||
}) | ||
instance.get('/global', function (request, reply) { | ||
t.ok(request.req.global) | ||
t.notOk(request.req.firstLocal) | ||
t.notOk(request.req.secondLocal) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
instance.listen(0, err => { | ||
t.error(err) | ||
t.tearDown(instance.server.close.bind(instance.server)) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/global' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/local' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
}) | ||
}) | ||
}) | ||
}) | ||
test('middlewares should support encapsulation / 4', t => { | ||
t.plan(25) | ||
const instance = fastify() | ||
instance.use(function (req, res, next) { | ||
req.global = true | ||
next() | ||
}) | ||
instance.register((i, opts, done) => { | ||
i.use(function (req, res, next) { | ||
req.firstLocal = true | ||
next() | ||
}) | ||
i.register((f, opts, d) => { | ||
f.use(function (req, res, next) { | ||
req.secondLocal = true | ||
next() | ||
}) | ||
f.get('/secondLocal', function (request, reply) { | ||
t.ok(request.req.global) | ||
t.ok(request.req.firstLocal) | ||
t.ok(request.req.secondLocal) | ||
t.ok(request.req.thirdLocal) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
f.use(function (req, res, next) { | ||
req.thirdLocal = true | ||
next() | ||
}) | ||
d() | ||
}, done) | ||
i.get('/firstLocal', function (request, reply) { | ||
t.ok(request.req.global) | ||
t.ok(request.req.firstLocal) | ||
t.notOk(request.req.secondLocal) | ||
t.notOk(request.req.thirdLocal) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
}) | ||
instance.get('/global', function (request, reply) { | ||
t.ok(request.req.global) | ||
t.notOk(request.req.firstLocal) | ||
t.notOk(request.req.secondLocal) | ||
t.notOk(request.req.thirdLocal) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
instance.listen(0, err => { | ||
t.error(err) | ||
t.tearDown(instance.server.close.bind(instance.server)) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/global' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/firstLocal' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/secondLocal' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
test('middlewares should support encapsulation / 5', t => { | ||
t.plan(9) | ||
const instance = fastify() | ||
instance.use(function (req, res, next) { | ||
req.global = true | ||
next() | ||
}) | ||
instance.register((i, opts, done) => { | ||
i.use(function (req, res, next) { | ||
next(new Error('kaboom!')) | ||
}) | ||
i.get('/local', function (request, reply) { | ||
t.fail('this should not be called') | ||
}) | ||
done() | ||
}) | ||
instance.get('/global', function (request, reply) { | ||
t.ok(request.req.global) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
instance.listen(0, err => { | ||
t.error(err) | ||
t.tearDown(instance.server.close.bind(instance.server)) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/global' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/local' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 500) | ||
t.deepEqual(JSON.parse(body), { | ||
error: 'Internal Server Error', | ||
message: 'kaboom!', | ||
statusCode: 500 | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
test('middlewares should support encapsulation with prefix', t => { | ||
t.plan(9) | ||
const instance = fastify() | ||
instance.use(function (req, res, next) { | ||
req.global = true | ||
next() | ||
}) | ||
instance.register((i, opts, done) => { | ||
i.use(function (req, res, next) { | ||
next(new Error('kaboom!')) | ||
}) | ||
i.get('/', function (request, reply) { | ||
t.fail('this should not be called') | ||
}) | ||
done() | ||
}, { prefix: '/local' }) | ||
instance.get('/global', function (request, reply) { | ||
t.ok(request.req.global) | ||
reply.send({ hello: 'world' }) | ||
}) | ||
instance.listen(0, err => { | ||
t.error(err) | ||
t.tearDown(instance.server.close.bind(instance.server)) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/global' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 200) | ||
t.strictEqual(response.headers['content-length'], '' + body.length) | ||
t.deepEqual(JSON.parse(body), { hello: 'world' }) | ||
request({ | ||
method: 'GET', | ||
uri: 'http://localhost:' + instance.server.address().port + '/local' | ||
}, (err, response, body) => { | ||
t.error(err) | ||
t.strictEqual(response.statusCode, 500) | ||
t.deepEqual(JSON.parse(body), { | ||
error: 'Internal Server Error', | ||
message: 'kaboom!', | ||
statusCode: 500 | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
@@ -73,3 +73,3 @@ 'use strict' | ||
test('preHandler hook error handling with code inside done', t => { | ||
test('onRequest hook error handling with external done', t => { | ||
t.plan(2) | ||
@@ -79,4 +79,5 @@ const fastify = Fastify() | ||
fastify.addHook('preHandler', (req, reply, done) => { | ||
done(err, 400) | ||
fastify.addHook('onRequest', (req, res, done) => { | ||
res.statusCode = 400 | ||
done(err) | ||
}) | ||
@@ -102,56 +103,2 @@ | ||
test('preRouting hook error handling with code inside done', t => { | ||
t.plan(2) | ||
const fastify = Fastify() | ||
const err = new Error('winter is coming') | ||
fastify.addHook('preRouting', (req, reply, done) => { | ||
done(err, 400) | ||
}) | ||
fastify.get('/', () => {}) | ||
fastify.inject({ | ||
method: 'GET', | ||
url: '/' | ||
}, res => { | ||
t.strictEqual(res.statusCode, 400) | ||
t.deepEqual( | ||
{ | ||
error: statusCodes['400'], | ||
message: err.message, | ||
statusCode: 400 | ||
}, | ||
JSON.parse(res.payload) | ||
) | ||
}) | ||
}) | ||
test('onRequest hook error handling with code inside done', t => { | ||
t.plan(2) | ||
const fastify = Fastify() | ||
const err = new Error('winter is coming') | ||
fastify.addHook('onRequest', (req, reply, done) => { | ||
done(err, 400) | ||
}) | ||
fastify.get('/', () => {}) | ||
fastify.inject({ | ||
method: 'GET', | ||
url: '/' | ||
}, res => { | ||
t.strictEqual(res.statusCode, 400) | ||
t.deepEqual( | ||
{ | ||
error: statusCodes['400'], | ||
message: err.message, | ||
statusCode: 400 | ||
}, | ||
JSON.parse(res.payload) | ||
) | ||
}) | ||
}) | ||
test('support for boom', t => { | ||
@@ -158,0 +105,0 @@ t.plan(3) |
240710
11
96
6252
145
30
+ Addedpino@^4.7.1
+ Addedmiddie@2.1.1(transitive)
+ Addedpath-to-regexp@2.4.0(transitive)
- Removedfast-safe-stringify@^1.2.0
- Removedpino-http@^2.6.1
- Removedmiddie@1.1.0(transitive)
- Removedpino-http@2.6.2(transitive)
Updatedmiddie@^2.0.0