strong-tie/inbound-calls
0
1# V4 Migration Guide2 3This guide is intended to help with migration from Fastify v3 to v4.4 5Before migrating to v4, please ensure that you have fixed all deprecation6warnings from v3. All v3 deprecations have been removed and they will no longer7work after upgrading.8 9## Codemods10### Fastify v4 Codemods11 12To help with the upgrade, we’ve worked with the team at13[Codemod](https://github.com/codemod-com/codemod) to14publish codemods that will automatically update your code to many of15the new APIs and patterns in Fastify v4.16 17Run the following18[migration recipe](https://go.codemod.com/fastify-4-migration-recipe) to19automatically update your code to Fastify v4:20 21```22npx codemod@latest fastify/4/migration-recipe23```24 25This will run the following codemods:26 27- [`fastify/4/remove-app-use`](https://go.codemod.com/fastify-4-remove-app-use)28- [`fastify/4/reply-raw-access`](https://go.codemod.com/fastify-4-reply-raw-access)29- [`fastify/4/wrap-routes-plugin`](https://go.codemod.com/fastify-4-wrap-routes-plugin)30- [`fastify/4/await-register-calls`](https://go.codemod.com/fastify-4-await-register-calls)31 32Each of these codemods automates the changes listed in the v4 migration guide.33For a complete list of available Fastify codemods and further details,34see [Codemod Registry](https://go.codemod.com/fastify).35 36 37## Breaking Changes38 39### Error handling composition ([#3261](https://github.com/fastify/fastify/pull/3261))40 41When an error is thrown in an async error handler function, the upper-level42error handler is executed if set. If there is no upper-level error handler,43the default will be executed as it was previously:44 45```js46import Fastify from 'fastify'47 48const fastify = Fastify()49 50fastify.register(async fastify => {51 fastify.setErrorHandler(async err => {52 console.log(err.message) // 'kaboom'53 throw new Error('caught')54 })55 56 fastify.get('/encapsulated', async () => {57 throw new Error('kaboom')58 })59})60 61fastify.setErrorHandler(async err => {62 console.log(err.message) // 'caught'63 throw new Error('wrapped')64})65 66const res = await fastify.inject('/encapsulated')67console.log(res.json().message) // 'wrapped'68```69 70>The root error handler is Fastify’s generic error handler.71>This error handler will use the headers and status code in the Error object,72>if they exist. **The headers and status code will not be automatically set if73>a custom error handler is provided**.74 75### Removed `app.use()` ([#3506](https://github.com/fastify/fastify/pull/3506))76 77With v4 of Fastify, `app.use()` has been removed and the use of middleware is78no longer supported.79 80If you need to use middleware, use81[`@fastify/middie`](https://github.com/fastify/middie) or82[`@fastify/express`](https://github.com/fastify/fastify-express), which will83continue to be maintained.84However, it is strongly recommended that you migrate to Fastify's [hooks](../Reference/Hooks.md).85 86> **Note**: Codemod remove `app.use()` with:87>88> ```bash89> npx codemod@latest fastify/4/remove-app-use90> ```91 92### `reply.res` moved to `reply.raw`93 94If you previously used the `reply.res` attribute to access the underlying Request95object you will now need to use `reply.raw`.96 97> **Note**: Codemod `reply.res` to `reply.raw` with:98>99> ```bash100> npx codemod@latest fastify/4/reply-raw-access101> ```102 103### Need to `return reply` to signal a "fork" of the promise chain104 105In some situations, like when a response is sent asynchronously or when you are106not explicitly returning a response, you will now need to return the `reply`107argument from your router handler.108 109### `exposeHeadRoutes` true by default110 111Starting with v4, every `GET` route will create a sibling `HEAD` route.112You can revert this behavior by setting `exposeHeadRoutes: false` in the server options.113 114### Synchronous route definitions ([#2954](https://github.com/fastify/fastify/pull/2954))115 116To improve error reporting in route definitions, route registration is now synchronous.117As a result, if you specify an `onRoute` hook in a plugin you should now either:118* wrap your routes in a plugin (recommended)119 120 For example, refactor this:121 ```js122 fastify.register((instance, opts, done) => {123 instance.addHook('onRoute', (routeOptions) => {124 const { path, method } = routeOptions;125 console.log({ path, method });126 done();127 });128 });129 130 fastify.get('/', (request, reply) => { reply.send('hello') });131 ```132 133 Into this:134 ```js135 fastify.register((instance, opts, done) => {136 instance.addHook('onRoute', (routeOptions) => {137 const { path, method } = routeOptions;138 console.log({ path, method });139 done();140 });141 });142 143 fastify.register((instance, opts, done) => {144 instance.get('/', (request, reply) => { reply.send('hello') });145 done();146 });147 ```148> **Note**: Codemod synchronous route definitions with:149>150> ```bash151> npx codemod@latest fastify/4/wrap-routes-plugin152> ```153 154* use `await register(...)`155 156 For example, refactor this:157 ```js158 fastify.register((instance, opts, done) => {159 instance.addHook('onRoute', (routeOptions) => {160 const { path, method } = routeOptions;161 console.log({ path, method });162 });163 done();164 });165 ```166 167 Into this:168 ```js169 await fastify.register((instance, opts, done) => {170 instance.addHook('onRoute', (routeOptions) => {171 const { path, method } = routeOptions;172 console.log({ path, method });173 });174 done();175 });176 ```177 178> **Note**: Codemod 'await register(...)' with:179>180> ```bash181> npx codemod@latest fastify/4/await-register-calls182> ```183 184 185### Optional URL parameters186 187If you've already used any implicitly optional parameters, you'll get a 404188error when trying to access the route. You will now need to declare the189optional parameters explicitly.190 191For example, if you have the same route for listing and showing a post,192refactor this:193```js194fastify.get('/posts/:id', (request, reply) => {195 const { id } = request.params;196});197```198 199Into this:200```js201fastify.get('/posts/:id?', (request, reply) => {202 const { id } = request.params;203});204```205 206## Non-Breaking Changes207 208### Deprecation of variadic `.listen()` signature209 210The [variadic signature](https://en.wikipedia.org/wiki/Variadic_function) of the211`fastify.listen()` method is now deprecated.212 213Prior to this release, the following invocations of this method were valid:214 215 - `fastify.listen(8000)`216 - `fastify.listen(8000, ‘127.0.0.1’)`217 - `fastify.listen(8000, ‘127.0.0.1’, 511)`218 - `fastify.listen(8000, (err) => { if (err) throw err })`219 - `fastify.listen({ port: 8000 }, (err) => { if (err) throw err })`220 221With Fastify v4, only the following invocations are valid:222 223 - `fastify.listen()`224 - `fastify.listen({ port: 8000 })`225 - `fastify.listen({ port: 8000 }, (err) => { if (err) throw err })`226 227### Change of schema for multiple types228 229Ajv has been upgraded to v8 in Fastify v4, meaning "type" keywords with multiple230types other than "null"231[are now prohibited](https://ajv.js.org/strict-mode.html#strict-types).232 233You may encounter a console warning such as:234```sh235strict mode: use allowUnionTypes to allow union type keyword at "#/properties/image" (strictTypes)236```237 238As such, schemas like below will need to be changed from:239```js240{241 type: 'object',242 properties: {243 api_key: { type: 'string' },244 image: { type: ['object', 'array'] }245 }246}247```248 249Into:250```js251{252 type: 'object',253 properties: {254 api_key: { type: 'string' },255 image: {256 anyOf: [257 { type: 'array' },258 { type: 'object' }259 ]260 }261 }262}263```264 265### Add `reply.trailers` methods ([#3794](https://github.com/fastify/fastify/pull/3794))266 267Fastify now supports the [HTTP Trailer] response headers.268 269 270[HTTP Trailer]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer271 