fastify
Advanced tools
Comparing version 4.13.0 to 4.14.0
@@ -651,3 +651,5 @@ <h1 align="center">Fastify</h1> | ||
generator by directory structure. | ||
- [`fastify-flux`](https://github.com/Jnig/fastify-flux) Tool for building | ||
Fastify APIs using decorators and convert Typescript interface to JSON Schema. | ||
- [`simple-tjscli`](https://github.com/imjuni/simple-tjscli) CLI tool to | ||
generate JSON Schema from TypeScript interfaces. |
@@ -22,2 +22,3 @@ <h1 align="center">Fastify</h1> | ||
- [onTimeout](#ontimeout) | ||
- [onRequestAbort](#onrequestabort) | ||
- [Manage Errors from a hook](#manage-errors-from-a-hook) | ||
@@ -271,3 +272,23 @@ - [Respond to a request from a hook](#respond-to-a-request-from-a-hook) | ||
### onRequestAbort | ||
```js | ||
fastify.addHook('onRequestAbort', (request, reply, done) => { | ||
// Some code | ||
done() | ||
}) | ||
``` | ||
Or `async/await`: | ||
```js | ||
fastify.addHook('onRequestAbort', async (request, reply) => { | ||
// Some code | ||
await asyncMethod() | ||
}) | ||
``` | ||
The `onRequestAbort` hook is executed when a client closes the connection before | ||
the entire request has been received. Therefore, you will not be able to send | ||
data to the client. | ||
**Notice:** client abort detection is not completely reliable. See: [`Detecting-When-Clients-Abort.md`](../Guides/Detecting-When-Clients-Abort.md) | ||
### Manage Errors from a hook | ||
@@ -274,0 +295,0 @@ If you get an error during the execution of your hook, just pass it to `done()` |
@@ -65,3 +65,3 @@ <h1 align="center">Fastify</h1> | ||
| Linux | `ubuntu-latest` | npm | 14,16,18 | | ||
| Linux | `ubuntu-18.04` | yarn,pnpm | 14,16,18 | | ||
| Linux | `ubuntu-latest` | yarn,pnpm | 14,16,18 | | ||
| Windows | `windows-latest` | npm | 14,16,18 | | ||
@@ -68,0 +68,0 @@ | MacOS | `macos-latest` | npm | 14,16,18 | |
@@ -385,3 +385,5 @@ <h1 align="center">Fastify</h1> | ||
Please note that this setting will also disable an error log written by the | ||
default `onResponse` hook on reply callback errors. | ||
default `onResponse` hook on reply callback errors. Other log messages | ||
emitted by Fastify will stay enabled, like deprecation warnings and messages | ||
emitted when requests are received while the server is closing. | ||
@@ -388,0 +390,0 @@ ### `serverFactory` |
@@ -6,5 +6,5 @@ import * as http from 'http' | ||
import { Options as AjvOptions, ValidatorCompiler } from '@fastify/ajv-compiler' | ||
import { Options as AjvOptions, ValidatorFactory } from '@fastify/ajv-compiler' | ||
import { FastifyError } from '@fastify/error' | ||
import { Options as FJSOptions, SerializerCompiler } from '@fastify/fast-json-stringify-compiler' | ||
import { Options as FJSOptions, SerializerFactory } from '@fastify/fast-json-stringify-compiler' | ||
import { ConstraintStrategy, HTTPVersion } from 'find-my-way' | ||
@@ -16,3 +16,3 @@ import { Chain as LightMyRequestChain, InjectOptions, Response as LightMyRequestResponse, CallbackFunc as LightMyRequestCallback } from 'light-my-request' | ||
import { FastifyErrorCodes } from './types/errors' | ||
import { DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler } from './types/hooks' | ||
import { DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, onRequestAbortHookHandler, onRequestAbortAsyncHookHandler } from './types/hooks' | ||
import { FastifyListenOptions, FastifyInstance, PrintRoutesOptions } from './types/instance' | ||
@@ -25,3 +25,3 @@ import { FastifyBaseLogger, FastifyLoggerInstance, FastifyLoggerOptions, PinoLoggerOptions, FastifyLogFn, LogLevel } from './types/logger' | ||
import { RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler, RouteGenericInterface } from './types/route' | ||
import { FastifySchema, FastifySchemaCompiler, FastifySchemaValidationError } from './types/schema' | ||
import { FastifySchema, FastifySchemaCompiler, SchemaErrorDataVar, SchemaErrorFormatter } from './types/schema' | ||
import { FastifyServerFactory, FastifyServerFactoryHandler } from './types/serverFactory' | ||
@@ -34,3 +34,3 @@ import { FastifyTypeProvider, FastifyTypeProviderDefault } from './types/type-provider' | ||
validation?: fastify.ValidationResult[]; | ||
validationContext?: 'body' | 'headers' | 'parameters' | 'querystring'; | ||
validationContext?: SchemaErrorDataVar; | ||
} | ||
@@ -141,4 +141,4 @@ } | ||
compilersFactory?: { | ||
buildValidator?: ValidatorCompiler; | ||
buildSerializer?: SerializerCompiler; | ||
buildValidator?: ValidatorFactory; | ||
buildSerializer?: SerializerFactory; | ||
}; | ||
@@ -157,3 +157,3 @@ }; | ||
rewriteUrl?: (req: RawRequestDefaultExpression<RawServer>) => string, | ||
schemaErrorFormatter?: (errors: FastifySchemaValidationError[], dataVar: string) => Error, | ||
schemaErrorFormatter?: SchemaErrorFormatter, | ||
/** | ||
@@ -188,3 +188,3 @@ * listener to error events emitted by client connections | ||
HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault, // './types/utils' | ||
DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, // './types/hooks' | ||
DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, onRequestAbortHookHandler, onRequestAbortAsyncHookHandler, // './types/hooks' | ||
FastifyServerFactory, FastifyServerFactoryHandler, // './types/serverFactory' | ||
@@ -191,0 +191,0 @@ FastifyTypeProvider, FastifyTypeProviderDefault, // './types/type-provider' |
'use strict' | ||
const VERSION = '4.13.0' | ||
const VERSION = '4.14.0' | ||
@@ -591,2 +591,6 @@ const Avvio = require('avvio') | ||
} | ||
} else if (name === 'onRequestAbort') { | ||
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 1) { | ||
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER() | ||
} | ||
} else { | ||
@@ -593,0 +597,0 @@ if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) { |
@@ -51,2 +51,3 @@ 'use strict' | ||
this.preSerialization = null | ||
this.onRequestAbort = null | ||
this.config = config | ||
@@ -53,0 +54,0 @@ this.errorHandler = errorHandler || server[kErrorHandler] |
@@ -14,3 +14,4 @@ 'use strict' | ||
const { | ||
FST_ERR_REP_INVALID_PAYLOAD_TYPE | ||
FST_ERR_REP_INVALID_PAYLOAD_TYPE, | ||
FST_ERR_FAILED_ERROR_SERIALIZATION | ||
} = require('./errors') | ||
@@ -117,7 +118,3 @@ | ||
reply.code(500) | ||
payload = serializeError({ | ||
error: statusCodes['500'], | ||
message: err.message, | ||
statusCode: 500 | ||
}) | ||
payload = serializeError(new FST_ERR_FAILED_ERROR_SERIALIZATION(err.message, error.message)) | ||
} | ||
@@ -124,0 +121,0 @@ |
@@ -5,3 +5,2 @@ // This file is autogenerated by build/build-error-serializer.js, do not edit | ||
'use strict' | ||
@@ -13,4 +12,4 @@ | ||
class Serializer { | ||
constructor (options = {}) { | ||
switch (options.rounding) { | ||
constructor (options) { | ||
switch (options && options.rounding) { | ||
case 'floor': | ||
@@ -25,2 +24,3 @@ this.parseInteger = Math.floor | ||
break | ||
case 'trunc': | ||
default: | ||
@@ -33,13 +33,24 @@ this.parseInteger = Math.trunc | ||
asInteger (i) { | ||
if (typeof i === 'bigint') { | ||
if (typeof i === 'number') { | ||
if (i === Infinity || i === -Infinity) { | ||
throw new Error(`The value "${i}" cannot be converted to an integer.`) | ||
} | ||
if (Number.isInteger(i)) { | ||
return '' + i | ||
} | ||
if (Number.isNaN(i)) { | ||
throw new Error(`The value "${i}" cannot be converted to an integer.`) | ||
} | ||
return this.parseInteger(i) | ||
} else if (i === null) { | ||
return '0' | ||
} else if (typeof i === 'bigint') { | ||
return i.toString() | ||
} else if (Number.isInteger(i)) { | ||
return '' + i | ||
} else { | ||
/* eslint no-undef: "off" */ | ||
const integer = this.parseInteger(i) | ||
if (Number.isNaN(integer) || !Number.isFinite(integer)) { | ||
if (Number.isFinite(integer)) { | ||
return '' + integer | ||
} else { | ||
throw new Error(`The value "${i}" cannot be converted to an integer.`) | ||
} else { | ||
return '' + integer | ||
} | ||
@@ -98,11 +109,14 @@ } | ||
asString (str) { | ||
const quotes = '"' | ||
if (str instanceof Date) { | ||
return quotes + str.toISOString() + quotes | ||
} else if (str === null) { | ||
return quotes + quotes | ||
} else if (str instanceof RegExp) { | ||
str = str.source | ||
} else if (typeof str !== 'string') { | ||
str = str.toString() | ||
if (typeof str !== 'string') { | ||
if (str === null) { | ||
return '""' | ||
} | ||
if (str instanceof Date) { | ||
return '"' + str.toISOString() + '"' | ||
} | ||
if (str instanceof RegExp) { | ||
str = str.source | ||
} else { | ||
str = str.toString() | ||
} | ||
} | ||
@@ -112,6 +126,4 @@ | ||
if (!STR_ESCAPE.test(str)) { | ||
return quotes + str + quotes | ||
} | ||
if (str.length < 42) { | ||
return '"' + str + '"' | ||
} else if (str.length < 42) { | ||
return this.asStringSmall(str) | ||
@@ -160,12 +172,6 @@ } else { | ||
const serializer = new Serializer({"mode":"standalone"}) | ||
const serializer = new Serializer() | ||
function main (input) { | ||
let json = '' | ||
json += anonymous0(input) | ||
return json | ||
} | ||
@@ -175,18 +181,12 @@ function anonymous0 (input) { | ||
var obj = (input && typeof input.toJSON === 'function') | ||
const obj = (input && typeof input.toJSON === 'function') | ||
? input.toJSON() | ||
: input | ||
var json = '{' | ||
var addComma = false | ||
let json = '{' | ||
let addComma = false | ||
if (obj["statusCode"] !== undefined) { | ||
if (addComma) { | ||
json += ',' | ||
} else { | ||
addComma = true | ||
} | ||
json += "\"statusCode\"" + ':' | ||
!addComma && (addComma = true) || (json += ',') | ||
json += "\"statusCode\":" | ||
json += serializer.asNumber(obj["statusCode"]) | ||
@@ -196,10 +196,4 @@ } | ||
if (obj["code"] !== undefined) { | ||
if (addComma) { | ||
json += ',' | ||
} else { | ||
addComma = true | ||
} | ||
json += "\"code\"" + ':' | ||
!addComma && (addComma = true) || (json += ',') | ||
json += "\"code\":" | ||
json += serializer.asString(obj["code"]) | ||
@@ -209,10 +203,4 @@ } | ||
if (obj["error"] !== undefined) { | ||
if (addComma) { | ||
json += ',' | ||
} else { | ||
addComma = true | ||
} | ||
json += "\"error\"" + ':' | ||
!addComma && (addComma = true) || (json += ',') | ||
json += "\"error\":" | ||
json += serializer.asString(obj["error"]) | ||
@@ -222,21 +210,14 @@ } | ||
if (obj["message"] !== undefined) { | ||
if (addComma) { | ||
json += ',' | ||
} else { | ||
addComma = true | ||
} | ||
json += "\"message\"" + ':' | ||
!addComma && (addComma = true) || (json += ',') | ||
json += "\"message\":" | ||
json += serializer.asString(obj["message"]) | ||
} | ||
json += '}' | ||
return json | ||
return json + '}' | ||
} | ||
const main = anonymous0 | ||
module.exports = main | ||
@@ -217,2 +217,6 @@ 'use strict' | ||
), | ||
FST_ERR_FAILED_ERROR_SERIALIZATION: createError( | ||
'FST_ERR_FAILED_ERROR_SERIALIZATION', | ||
'Failed to serialize an error. Error: %s. Original error: %s' | ||
), | ||
FST_ERR_MISSING_SERIALIZATION_FN: createError( | ||
@@ -219,0 +223,0 @@ 'FST_ERR_MISSING_SERIALIZATION_FN', |
@@ -18,3 +18,4 @@ 'use strict' | ||
'onResponse', | ||
'onError' | ||
'onError', | ||
'onRequestAbort' | ||
] | ||
@@ -50,2 +51,3 @@ const supportedHooks = lifecycleHooks.concat(applicationHooks) | ||
this.onTimeout = [] | ||
this.onRequestAbort = [] | ||
} | ||
@@ -79,2 +81,3 @@ | ||
hooks.onTimeout = h.onTimeout.slice() | ||
hooks.onRequestAbort = h.onRequestAbort.slice() | ||
hooks.onReady = [] | ||
@@ -252,2 +255,38 @@ return hooks | ||
function onRequestAbortHookRunner (functions, runner, request, cb) { | ||
let i = 0 | ||
function next (err) { | ||
if (err || i === functions.length) { | ||
cb(err, request) | ||
return | ||
} | ||
let result | ||
try { | ||
result = runner(functions[i++], request, next) | ||
} catch (error) { | ||
next(error) | ||
return | ||
} | ||
if (result && typeof result.then === 'function') { | ||
result.then(handleResolve, handleReject) | ||
} | ||
} | ||
function handleResolve () { | ||
next() | ||
} | ||
function handleReject (err) { | ||
if (!err) { | ||
err = new FST_ERR_SEND_UNDEFINED_ERR() | ||
} | ||
cb(err, request) | ||
} | ||
next() | ||
} | ||
function hookIterator (fn, request, reply, next) { | ||
@@ -263,2 +302,3 @@ if (reply.sent === true) return undefined | ||
onSendHookRunner, | ||
onRequestAbortHookRunner, | ||
hookIterator, | ||
@@ -265,0 +305,0 @@ hookRunnerApplication, |
@@ -6,3 +6,3 @@ 'use strict' | ||
const handleRequest = require('./handleRequest') | ||
const { hookRunner, hookIterator, lifecycleHooks } = require('./hooks') | ||
const { hookRunner, hookIterator, onRequestAbortHookRunner, lifecycleHooks } = require('./hooks') | ||
const { supportedMethods } = require('./httpMethods') | ||
@@ -307,3 +307,3 @@ const { normalizeSchema } = require('./schemas') | ||
if (hasHEADHandler && !context[kRouteByFastify] && headHandler.store[kRouteByFastify]) { | ||
router.off(opts.method, opts.url, { constraints }) | ||
router.off('HEAD', opts.url, { constraints }) | ||
} | ||
@@ -399,4 +399,21 @@ | ||
function routeHandler (req, res, params, context, query) { | ||
const id = genReqId(req) | ||
const loggerBinding = { | ||
[requestIdLogLabel]: id | ||
} | ||
const loggerOpts = { | ||
level: context.logLevel | ||
} | ||
if (context.logSerializers) { | ||
loggerOpts.serializers = context.logSerializers | ||
} | ||
const childLogger = logger.child(loggerBinding, loggerOpts) | ||
childLogger[kDisableRequestLogging] = disableRequestLogging | ||
// TODO: The check here should be removed once https://github.com/nodejs/node/issues/43115 resolve in core. | ||
if (!validateHTTPVersion(req.httpVersion)) { | ||
childLogger.info({ res: { statusCode: 505 } }, 'request aborted - invalid HTTP version') | ||
const message = '{"error":"HTTP Version Not Supported","message":"HTTP Version Not Supported","statusCode":505}' | ||
@@ -430,2 +447,3 @@ const headers = { | ||
res.end('{"error":"Service Unavailable","message":"Service Unavailable","statusCode":503}') | ||
childLogger.info({ res: { statusCode: 503 } }, 'request aborted - refusing to accept new requests as server is closing') | ||
return | ||
@@ -452,18 +470,2 @@ } | ||
const id = genReqId(req) | ||
const loggerBinding = { | ||
[requestIdLogLabel]: id | ||
} | ||
const loggerOpts = { | ||
level: context.logLevel | ||
} | ||
if (context.logSerializers) { | ||
loggerOpts.serializers = context.logSerializers | ||
} | ||
const childLogger = logger.child(loggerBinding, loggerOpts) | ||
childLogger[kDisableRequestLogging] = disableRequestLogging | ||
const request = new context.Request(id, params, req, query, childLogger, context) | ||
@@ -492,2 +494,16 @@ const reply = new context.Reply(res, request, childLogger) | ||
if (context.onRequestAbort !== null) { | ||
req.on('close', () => { | ||
/* istanbul ignore else */ | ||
if (req.aborted) { | ||
onRequestAbortHookRunner( | ||
context.onRequestAbort, | ||
hookIterator, | ||
request, | ||
handleOnRequestAbortHooksErrors.bind(null, reply) | ||
) | ||
} | ||
}) | ||
} | ||
if (context.onTimeout !== null) { | ||
@@ -502,2 +518,8 @@ if (!request.raw.socket._meta) { | ||
function handleOnRequestAbortHooksErrors (reply, err) { | ||
if (err) { | ||
reply.log.error({ err }, 'onRequestAborted hook failed') | ||
} | ||
} | ||
function handleTimeout () { | ||
@@ -504,0 +526,0 @@ const { context, request, reply } = this._meta |
@@ -33,2 +33,3 @@ 'use strict' | ||
kRequestAcceptVersion: Symbol('fastify.RequestAcceptVersion'), | ||
kRequestValidateWeakMap: Symbol('fastify.request.cache.validators'), | ||
// 404 | ||
@@ -35,0 +36,0 @@ kFourOhFour: Symbol('fastify.404'), |
{ | ||
"name": "fastify", | ||
"version": "4.13.0", | ||
"version": "4.14.0", | ||
"description": "Fast and low overhead web framework, for Node.js", | ||
@@ -171,6 +171,6 @@ "main": "fastify.js", | ||
"vary": "^1.1.2", | ||
"yup": "^0.32.11" | ||
"yup": "^1.0.0" | ||
}, | ||
"dependencies": { | ||
"@fastify/ajv-compiler": "^3.3.1", | ||
"@fastify/ajv-compiler": "^3.5.0", | ||
"@fastify/error": "^3.0.0", | ||
@@ -177,0 +177,0 @@ "@fastify/fast-json-stringify-compiler": "^4.1.0", |
@@ -10,2 +10,3 @@ 'use strict' | ||
const semver = require('semver') | ||
const split = require('split2') | ||
@@ -307,2 +308,36 @@ test('close callback', t => { | ||
t.test('rejected incoming connections should be logged', t => { | ||
t.plan(2) | ||
const stream = split(JSON.parse) | ||
const fastify = Fastify({ | ||
forceCloseConnections: false, | ||
logger: { | ||
stream, | ||
level: 'info' | ||
} | ||
}) | ||
const messages = [] | ||
stream.on('data', message => { | ||
messages.push(message) | ||
}) | ||
fastify.get('/', (req, reply) => { | ||
fastify.close() | ||
setTimeout(() => { | ||
reply.send({ hello: 'world' }) | ||
}, 250) | ||
}) | ||
fastify.listen({ port: 0 }, err => { | ||
t.error(err) | ||
const instance = new Client('http://localhost:' + fastify.server.address().port) | ||
// initial request to trigger close | ||
instance.request({ path: '/', method: 'GET' }) | ||
// subsequent request should be rejected | ||
instance.request({ path: '/', method: 'GET' }).then(() => { | ||
t.ok(messages.find(message => message.msg.includes('request aborted'))) | ||
}) | ||
}) | ||
}) | ||
test('Cannot be reopened the closed server without listen callback', async t => { | ||
@@ -309,0 +344,0 @@ t.plan(2) |
@@ -587,2 +587,14 @@ 'use strict' | ||
test('Should log a warning if is an async function with `done`', t => { | ||
t.test('2 arguments', t => { | ||
t.plan(2) | ||
const fastify = Fastify() | ||
try { | ||
fastify.addHook('onRequestAbort', async (req, done) => {}) | ||
} catch (e) { | ||
t.ok(e.code, 'FST_ERR_HOOK_INVALID_ASYNC_HANDLER') | ||
t.ok(e.message === 'Async function has too many arguments. Async hooks should not use the \'done\' argument.') | ||
} | ||
}) | ||
t.test('3 arguments', t => { | ||
@@ -589,0 +601,0 @@ t.plan(2) |
@@ -31,11 +31,13 @@ 'use strict' | ||
teardown(() => { | ||
files.forEach((file) => { | ||
try { | ||
fs.unlinkSync(file) | ||
} catch (e) { | ||
console.log(e) | ||
} | ||
if (process.env.CI) { | ||
teardown(() => { | ||
files.forEach((file) => { | ||
try { | ||
fs.unlinkSync(file) | ||
} catch (e) { | ||
console.log(e) | ||
} | ||
}) | ||
}) | ||
}) | ||
} | ||
@@ -42,0 +44,0 @@ test('defaults to info level', t => { |
@@ -1496,1 +1496,15 @@ 'use strict' | ||
}) | ||
test('using fastify.all when a catchall is defined does not degrade performance', { timeout: 5000 }, async t => { | ||
t.plan(1) | ||
const fastify = Fastify() | ||
fastify.get('/*', async (_, reply) => reply.json({ ok: true })) | ||
for (let i = 0; i < 100; i++) { | ||
fastify.all(`/${i}`, async (_, reply) => reply.json({ ok: true })) | ||
} | ||
t.pass() | ||
}) |
@@ -836,4 +836,5 @@ 'use strict' | ||
statusCode: 500, | ||
error: 'Internal Server Error', | ||
message: '"code" is required!' | ||
code: 'FST_ERR_FAILED_ERROR_SERIALIZATION', | ||
message: 'Failed to serialize an error. Error: "code" is required!. ' + | ||
'Original error: querystring must have required property \'foo\'' | ||
}) | ||
@@ -840,0 +841,0 @@ }) |
@@ -243,3 +243,3 @@ import fastify, { | ||
expectAssignable<FastifyError['validationContext']>('headers') | ||
expectAssignable<FastifyError['validationContext']>('parameters') | ||
expectAssignable<FastifyError['validationContext']>('params') | ||
expectAssignable<FastifyError['validationContext']>('querystring') | ||
@@ -246,0 +246,0 @@ |
@@ -117,2 +117,10 @@ import { FastifyError } from '@fastify/error' | ||
server.addHook('onRequestAbort', function (request, done) { | ||
expectType<FastifyInstance>(this) | ||
expectType<FastifyRequest>(request) | ||
expectAssignable<(err?: FastifyError) => void>(done) | ||
expectAssignable<(err?: NodeJS.ErrnoException) => void>(done) | ||
expectType<void>(done(new Error())) | ||
}) | ||
server.addHook('onRoute', function (opts) { | ||
@@ -205,2 +213,7 @@ expectType<FastifyInstance>(this) | ||
server.addHook('onRequestAbort', async function (request) { | ||
expectType<FastifyInstance>(this) | ||
expectType<FastifyRequest>(request) | ||
}) | ||
server.addHook('onRegister', async (instance, opts) => { | ||
@@ -207,0 +220,0 @@ expectType<FastifyInstance>(instance) |
@@ -1,7 +0,6 @@ | ||
import { expectAssignable, expectError } from 'tsd' | ||
import fastify, { FastifyInstance, FastifyRequest, FastifySchema } from '../../fastify' | ||
import { RouteGenericInterface } from '../../types/route' | ||
import { ContextConfigDefault } from '../../types/utils' | ||
import { FastifyReply } from '../../types/reply' | ||
import { expectAssignable } from 'tsd' | ||
import fastify, { FastifyInstance, FastifySchema } from '../../fastify' | ||
import Ajv from 'ajv' | ||
import { StandaloneValidator } from '@fastify/ajv-compiler' | ||
import { StandaloneSerializer } from '@fastify/fast-json-stringify-compiler' | ||
@@ -66,1 +65,34 @@ const server = fastify() | ||
)) | ||
// https://github.com/fastify/ajv-compiler/issues/95 | ||
{ | ||
const factory = StandaloneValidator({ | ||
readMode: false, | ||
storeFunction (routeOpts, schemaValidationCode) { } | ||
}) | ||
const app = fastify({ | ||
jsonShorthand: false, | ||
schemaController: { | ||
compilersFactory: { | ||
buildValidator: factory | ||
} | ||
} | ||
}) | ||
} | ||
{ | ||
const factory = StandaloneSerializer({ | ||
readMode: false, | ||
storeFunction (routeOpts, schemaValidationCode) { } | ||
}) | ||
const app = fastify({ | ||
jsonShorthand: false, | ||
schemaController: { | ||
compilersFactory: { | ||
buildSerializer: factory | ||
} | ||
} | ||
}) | ||
} |
@@ -31,2 +31,3 @@ import { FastifyErrorConstructor } from '@fastify/error' | ||
'FST_ERR_BAD_TRAILER_VALUE' | | ||
'FST_ERR_FAILED_ERROR_SERIALIZATION' | | ||
'FST_ERR_MISSING_SERIALIZATION_FN' | | ||
@@ -33,0 +34,0 @@ 'FST_ERR_REQ_INVALID_VALIDATION_INVOCATION' | |
@@ -397,2 +397,40 @@ import { Readable } from 'stream' | ||
/** | ||
* `onRequestAbort` is useful if you need to monitor the if the client aborts the request (if the `request.raw.aborted` property is set to `true`). | ||
* The `onRequestAbort` hook is executed when a client closes the connection before the entire request has been received. Therefore, you will not be able to send data to the client. | ||
* Notice: client abort detection is not completely reliable. See: https://github.com/fastify/fastify/blob/main/docs/Guides/Detecting-When-Clients-Abort.md | ||
*/ | ||
export interface onRequestAbortHookHandler< | ||
RawServer extends RawServerBase = RawServerDefault, | ||
RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>, | ||
RawReply extends RawReplyDefaultExpression<RawServer> = RawReplyDefaultExpression<RawServer>, | ||
RouteGeneric extends RouteGenericInterface = RouteGenericInterface, | ||
ContextConfig = ContextConfigDefault, | ||
SchemaCompiler extends FastifySchema = FastifySchema, | ||
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, | ||
Logger extends FastifyBaseLogger = FastifyBaseLogger | ||
> { | ||
( | ||
this: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>, | ||
request: FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider, ContextConfig, Logger>, | ||
done: HookHandlerDoneFunction | ||
): void; | ||
} | ||
export interface onRequestAbortAsyncHookHandler< | ||
RawServer extends RawServerBase = RawServerDefault, | ||
RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>, | ||
RawReply extends RawReplyDefaultExpression<RawServer> = RawReplyDefaultExpression<RawServer>, | ||
RouteGeneric extends RouteGenericInterface = RouteGenericInterface, | ||
ContextConfig = ContextConfigDefault, | ||
SchemaCompiler extends FastifySchema = FastifySchema, | ||
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, | ||
Logger extends FastifyBaseLogger = FastifyBaseLogger | ||
> { | ||
( | ||
this: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>, | ||
request: FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider, ContextConfig, Logger>, | ||
): Promise<unknown>; | ||
} | ||
// Application Hooks | ||
@@ -399,0 +437,0 @@ |
@@ -6,3 +6,3 @@ import { FastifyError } from '@fastify/error' | ||
import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, getDefaultJsonParser, hasContentTypeParser, ProtoAction, removeAllContentTypeParsers, removeContentTypeParser } from './content-type-parser' | ||
import { onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler } from './hooks' | ||
import { onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onRequestAbortAsyncHookHandler, onRequestAbortHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler } from './hooks' | ||
import { FastifyBaseLogger } from './logger' | ||
@@ -17,4 +17,4 @@ import { FastifyRegister } from './register' | ||
FastifySchemaControllerOptions, | ||
FastifySchemaValidationError, | ||
FastifySerializerCompiler | ||
FastifySerializerCompiler, | ||
SchemaErrorFormatter | ||
} from './schema' | ||
@@ -399,2 +399,27 @@ import { | ||
/** | ||
* `onRequestAbort` is useful if you need to monitor the if the client aborts the request (if the `request.raw.aborted` property is set to `true`). | ||
* The `onRequestAbort` hook is executed when a client closes the connection before the entire request has been received. Therefore, you will not be able to send data to the client. | ||
* Notice: client abort detection is not completely reliable. See: https://github.com/fastify/fastify/blob/main/docs/Guides/Detecting-When-Clients-Abort.md | ||
*/ | ||
addHook< | ||
RouteGeneric extends RouteGenericInterface = RouteGenericInterface, | ||
ContextConfig = ContextConfigDefault, | ||
SchemaCompiler extends FastifySchema = FastifySchema, | ||
Logger extends FastifyBaseLogger = FastifyBaseLogger | ||
>( | ||
name: 'onRequestAbort', | ||
hook: onRequestAbortHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger> | ||
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>; | ||
addHook< | ||
RouteGeneric extends RouteGenericInterface = RouteGenericInterface, | ||
ContextConfig = ContextConfigDefault, | ||
SchemaCompiler extends FastifySchema = FastifySchema, | ||
Logger extends FastifyBaseLogger = FastifyBaseLogger | ||
>( | ||
name: 'onRequestAbort', | ||
hook: onRequestAbortAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger> | ||
): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>; | ||
/** | ||
* This hook is useful if you need to do some custom error logging or add some specific header in case of error. | ||
@@ -536,3 +561,3 @@ * It is not intended for changing the error, and calling reply.send will throw an exception. | ||
*/ | ||
setSchemaErrorFormatter(errorFormatter: (errors: FastifySchemaValidationError[], dataVar: string) => Error): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>; | ||
setSchemaErrorFormatter(errorFormatter: SchemaErrorFormatter): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>; | ||
/** | ||
@@ -539,0 +564,0 @@ * Add a content type parser |
import { FastifyInstance } from './instance' | ||
import { FastifyRequest, RequestGenericInterface } from './request' | ||
import { FastifyReply, ReplyGenericInterface } from './reply' | ||
import { FastifySchema, FastifySchemaCompiler, FastifySchemaValidationError, FastifySerializerCompiler } from './schema' | ||
import { FastifySchema, FastifySchemaCompiler, FastifySerializerCompiler, SchemaErrorFormatter } from './schema' | ||
import { HTTPMethods, RawServerBase, RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, ContextConfigDefault } from './utils' | ||
import { preValidationHookHandler, preHandlerHookHandler, preSerializationHookHandler, onRequestHookHandler, preParsingHookHandler, onResponseHookHandler, onSendHookHandler, onErrorHookHandler, onTimeoutHookHandler } from './hooks' | ||
import { preValidationHookHandler, preHandlerHookHandler, preSerializationHookHandler, onRequestHookHandler, preParsingHookHandler, onResponseHookHandler, onSendHookHandler, onErrorHookHandler, onTimeoutHookHandler, onRequestAbortHookHandler } from './hooks' | ||
import { FastifyError } from '@fastify/error' | ||
@@ -44,4 +44,3 @@ import { FastifyContext } from './context' | ||
errorHandler?: (this: FastifyInstance, error: FastifyError, request: FastifyRequest, reply: FastifyReply) => void; | ||
// TODO: Change to actual type. | ||
schemaErrorFormatter?: (errors: FastifySchemaValidationError[], dataVar: string) => Error; | ||
schemaErrorFormatter?: SchemaErrorFormatter; | ||
@@ -58,2 +57,3 @@ // hooks | ||
onError?: onErrorHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, FastifyError, SchemaCompiler, TypeProvider, Logger> | onErrorHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, FastifyError, SchemaCompiler, TypeProvider, Logger>[]; | ||
onRequestAbort?: onRequestAbortHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger> | onRequestAbortHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>[]; | ||
} | ||
@@ -60,0 +60,0 @@ |
@@ -1,3 +0,4 @@ | ||
import { ValidatorCompiler } from '@fastify/ajv-compiler' | ||
import { FastifyInstance, FastifyServerOptions } from '../fastify' | ||
import { ValidatorFactory } from '@fastify/ajv-compiler' | ||
import { SerializerFactory } from '@fastify/fast-json-stringify-compiler' | ||
import { FastifyInstance } from '../fastify' | ||
/** | ||
@@ -53,5 +54,9 @@ * Schemas in Fastify follow the JSON-Schema standard. For this reason | ||
compilersFactory?: { | ||
buildValidator?: ValidatorCompiler; | ||
buildSerializer?: (externalSchemas: unknown, serializerOptsServerOption: FastifyServerOptions['serializerOpts']) => FastifySerializerCompiler<unknown>; | ||
buildValidator?: ValidatorFactory; | ||
buildSerializer?: SerializerFactory; | ||
}; | ||
} | ||
export type SchemaErrorDataVar = 'body' | 'headers' | 'params' | 'querystring' | ||
export type SchemaErrorFormatter = (errors: FastifySchemaValidationError[], dataVar: SchemaErrorDataVar) => Error |
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
2052390
49035
21
48
Updated@fastify/ajv-compiler@^3.5.0