- V3 Migration Guide
- Breaking changes
- Changed middleware support ( #2014 )
- Changed logging serialization ( #2017 )
- Changed schema substitution ( #2023 )
- Changed schema validation options ( #2023 )
- Changed preParsing hook behavior ( #2286 )
- Changed hooks behavior ( #2004 )
- Changed Content Type Parser syntax ( #2286 )
- Changed TypeScript support
- Manage uncaught exception ( #2073 )
- Further additions and improvements
- Breaking changes
V3 Migration Guide
This guide is intended to help with migration from Fastify v2 to v3.
Before beginning please ensure that any deprecation warnings from v2 are fixed. All v2 deprecations have been removed and they will no longer work after upgrading. (#1750)
Breaking changes
Changed middleware support (#2014)
From Fastify v3, middleware support does not come out-of-the-box with the framework itself.
If you use Express middleware in your application, please install and register the @fastify/express or middie plugin before doing so.
v2:
// Using the Express `cors` middleware in Fastify v2.
fastify.use(require('cors')());
v3:
// Using the Express `cors` middleware in Fastify v3.
await fastify.register(require('@fastify/express'));
fastify.use(require('cors')());
Changed logging serialization (#2017)
The logging Serializers have been updated to now Fastify Request and Reply objects instead of native ones.
Any custom serializers must be updated if they rely upon request
or reply
properties that are present on the native objects but not the Fastify objects.
v2:
const fastify = require('fastify')({
logger: {
serializers: {
res(res) {
return {
statusCode: res.statusCode,
customProp: res.customProp
};
}
}
}
});
v3:
const fastify = require('fastify')({
logger: {
serializers: {
res(reply) {
return {
statusCode: reply.statusCode, // No change required
customProp: reply.raw.customProp // Log custom property from res object
};
}
}
}
});
Changed schema substitution (#2023)
The non-standard replace-way
shared schema support has been removed. This feature has been replaced with JSON Schema specification compliant $ref
based substitution. To help understand this change read Validation and Serialization in Fastify v3.
v2:
const schema = {
body: 'schemaId#'
};
fastify.route({ method, url, schema, handler });
v3:
const schema = {
body: {
$ref: 'schemaId#'
}
};
fastify.route({ method, url, schema, handler });
Changed schema validation options (#2023)
The setSchemaCompiler
and setSchemaResolver
options have been replaced with the setValidatorCompiler
to enable future tooling improvements. To help understand this change read Validation and Serialization in Fastify v3.
v2:
const fastify = Fastify();
const ajv = new AJV();
ajv.addSchema(schemaA);
ajv.addSchema(schemaB);
fastify.setSchemaCompiler(schema => ajv.compile(schema));
fastify.setSchemaResolver(ref => ajv.getSchema(ref).schema);
v3:
const fastify = Fastify();
const ajv = new AJV();
ajv.addSchema(schemaA);
ajv.addSchema(schemaB);
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) =>
ajv.compile(schema)
);
Changed preParsing hook behavior (#2286)
From Fastify v3, the behavior of the preParsing
hook will change slightly in order to support request payload manipulation.
The hook now takes an additional argument, payload
, and therefore the new hook signature is fn(request, reply, payload, done)
or async fn(request, reply, payload)
.
The hook can optionally return a new stream via done(null, stream)
or returning the stream in case of async functions.
If the hook returns a new stream, it will be used instead of the original one in subsequent hooks. A sample use case for this is handling compressed requests.
The new stream should add the receivedEncodedLength
property to the stream that should reflect the actual data size received from the client. For instance, in a compressed request it should be the size of the compressed payload. This property can (and should) be dynamically updated during data
events.
The old syntax of Fastify v2 without payload is supported but it is deprecated.
Changed hooks behavior (#2004)
From Fastify v3, the behavior of onRoute
and onRegister
hooks will change slightly in order to support hook encapsulation.
onRoute
- The hook will be called asynchronously. The hook is now inherited when registering a new plugin within the same encapsulation scope. Thus, this hook should be registered before registering any plugins.onRegister
- Same as the onRoute hook. The only difference is that now the very first call will no longer be the framework itself, but the first registered plugin.
Changed Content Type Parser syntax (#2286)
In Fastify v3 the content type parsers now have a single signature for parsers.
The new signatures are fn(request, payload, done)
or async fn(request, payload)
. Note that request
is now a Fastify request, not an IncomingMessage
. The payload is by default a stream. If the parseAs
option is used in addContentTypeParser
, then payload
reflects the option value (string or buffer).
The old signatures fn(req, [done])
or fn(req, payload, [done])
(where req
is IncomingMessage
) are still supported but are deprecated.
Changed TypeScript support
The type system was changed in Fastify version 3. The new type system introduces generic constraining and defaulting, plus a new way to define schema types such as a request body, querystring, and more!
v2:
interface PingQuerystring {
foo?: number;
}
interface PingParams {
bar?: string;
}
interface PingHeaders {
a?: string;
}
interface PingBody {
baz?: string;
}
server.get<PingQuerystring, PingParams, PingHeaders, PingBody>(
'/ping/:bar',
opts,
(request, reply) => {
console.log(request.query); // This is of type `PingQuerystring`
console.log(request.params); // This is of type `PingParams`
console.log(request.headers); // This is of type `PingHeaders`
console.log(request.body); // This is of type `PingBody`
}
);
v3:
server.get<{
Querystring: PingQuerystring;
Params: PingParams;
Headers: PingHeaders;
Body: PingBody;
}>('/ping/:bar', opts, async (request, reply) => {
console.log(request.query); // This is of type `PingQuerystring`
console.log(request.params); // This is of type `PingParams`
console.log(request.headers); // This is of type `PingHeaders`
console.log(request.body); // This is of type `PingBody`
});
Manage uncaught exception (#2073)
In sync route handlers, if an error was thrown the server crashed by design without calling the configured .setErrorHandler()
. This has changed and now all unexpected errors in sync and async routes are managed.
v2:
fastify.setErrorHandler((error, request, reply) => {
// this is NOT called
reply.send(error)
})
fastify.get('/', (request, reply) => {
const maybeAnArray = request.body.something ? [] : 'I am a string'
maybeAnArray.substr() // Thrown: [].substr is not a function and crash the server
})
v3:
fastify.setErrorHandler((error, request, reply) => {
// this IS called
reply.send(error)
})
fastify.get('/', (request, reply) => {
const maybeAnArray = request.body.something ? [] : 'I am a string'
maybeAnArray.substr() // Thrown: [].substr is not a function, but it is handled
})
Further additions and improvements
- Hooks now have consistent context regardless of how they are registered (#2005)
- Deprecated
request.req
andreply.res
for request.raw and reply.raw (#2008) - Removed
modifyCoreObjects
option (#2015) - Added connectionTimeout option (#2086)
- Added keepAliveTimeout option (#2086)
- Added async-await support for plugins (#2093)
- Added the feature to throw object as error (#2134)