V5 遷移指南
本指南旨在協助從 Fastify v4 遷移至 v5。
在遷移至 v5 之前,請確保您已修正 v4 中所有的棄用警告。所有 v4 的棄用都已被移除,升級後將不再運作。
長期支援週期
Fastify v5 將僅支援 Node.js v20+。如果您正在使用較舊版本的 Node.js,您需要升級到較新版本才能使用 Fastify v5。
Fastify v4 仍支援至 2025 年 6 月 30 日。如果您無法升級,您應該考慮從 HeroDevs 購買終止生命週期支援計畫。
為什麼是 Node.js v20?
Fastify v5 將僅支援 Node.js v20+,因為它與 v18 相比有顯著差異,例如更好地支援 node:test
。這讓我們能夠提供更好的開發人員體驗並簡化維護。
Node.js v18 將於 2025 年 4 月 30 日結束長期支援,因此您應該計劃升級到 v20。
重大變更
現在 querystring
、params
、body
和回應 schema 都需要完整的 JSON Schema
從 v5 開始,Fastify 將要求 querystring
、params
和 body
schema 使用完整的 JSON schema。請注意,jsonShortHand
選項也已移除。
如果使用預設的 JSON Schema 驗證器,您將需要為 querystring
、params
、body
和 response
schema 提供完整的 JSON schema,包括 type
屬性。
// v4
fastify.get('/route', {
schema: {
querystring: {
name: { type: 'string' }
}
}
}, (req, reply) => {
reply.send({ hello: req.query.name });
});
// v5
fastify.get('/route', {
schema: {
querystring: {
type: 'object',
properties: {
name: { type: 'string' }
},
required: ['name']
}
}
}, (req, reply) => {
reply.send({ hello: req.query.name });
});
請參閱 #5586 以瞭解更多詳細資訊
請注意,仍然可以覆寫 JSON Schema 驗證器以使用不同的格式,例如 Zod。此變更也簡化了該操作。
此變更有助於整合其他工具,例如 @fastify/swagger
。
新的記錄器建構子簽名
在 Fastify v4 中,Fastify 接受在 logger
選項中建立 pino 記錄器的選項,以及自訂記錄器實例。這是造成重大混淆的原因。
因此,logger
選項在 v5 中將不再接受自訂記錄器。若要使用自訂記錄器,您應該改用 loggerInstance
選項
// v4
const logger = require('pino')();
const fastify = require('fastify')({
logger
});
// v5
const loggerInstance = require('pino')();
const fastify = require('fastify')({
loggerInstance
});
useSemicolonDelimiter
預設為 false
從 v5 開始,Fastify 實例將不再預設支援在查詢字串中使用分號分隔符號,就像在 v4 中一樣。這是因為它是非標準行為,不符合 RFC 3986。
如果您仍然希望使用分號作為分隔符號,您可以在伺服器組態中設定 useSemicolonDelimiter: true
來實現。
const fastify = require('fastify')({
useSemicolonDelimiter: true
});
parameters 物件不再具有原型
在 v4 中,parameters
物件具有原型。v5 中不再如此。這表示您不再能夠存取從 Object
繼承的 parameters
物件上的屬性,例如 toString
或 hasOwnProperty
。
// v4
fastify.get('/route/:name', (req, reply) => {
console.log(req.params.hasOwnProperty('name')); // true
return { hello: req.params.name };
});
// v5
fastify.get('/route/:name', (req, reply) => {
console.log(Object.hasOwn(req.params, 'name')); // true
return { hello: req.params.name };
});
這透過加強防範原型汙染攻擊來提高應用程式的安全性。
類型提供器現在區分驗證器和序列化器 schema
在 v4 中,類型提供器對於驗證和序列化具有相同的類型。在 v5 中,類型提供器已分為兩種不同的類型:ValidatorSchema
和 SerializerSchema
。
@fastify/type-provider-json-schema-to-ts
和 @fastify/type-provider-typebox
已更新:升級到最新版本以取得新的類型。如果您正在使用自訂類型提供器,您需要像以下方式修改它
--- a/index.ts
+++ b/index.ts
@@ -11,7 +11,8 @@ import {
import { FromSchema, FromSchemaDefaultOptions, FromSchemaOptions, JSONSchema } from 'json-schema-to-ts'
export interface JsonSchemaToTsProvider<
Options extends FromSchemaOptions = FromSchemaDefaultOptions
> extends FastifyTypeProvider {
- output: this['input'] extends JSONSchema ? FromSchema<this['input'], Options> : unknown;
+ validator: this['schema'] extends JSONSchema ? FromSchema<this['schema'], Options> : unknown;
+ serializer: this['schema'] extends JSONSchema ? FromSchema<this['schema'], Options> : unknown;
}
.listen()
方法的變更
已移除 .listen()
方法的 variadic 引數簽名。這表示您不再能夠使用可變數量的引數來呼叫 .listen()
。
// v4
fastify.listen(8000)
將會變成
// v5
fastify.listen({ port: 8000 })
這在 v4 中已經被棄用為 FSTDEP011
,因此您應該已經更新您的程式碼以使用新的簽名。
已移除直接傳回 trailers
在 v4 中,您可以直接從處理常式傳回 trailers。這在 v5 中已不再可能。
// v4
fastify.get('/route', (req, reply) => {
reply.trailer('ETag', function (reply, payload) {
return 'custom-etag'
})
reply.send('')
});
// v5
fastify.get('/route', (req, reply) => {
reply.trailer('ETag', async function (reply, payload) {
return 'custom-etag'
})
reply.send('')
});
也可以使用回呼。這在 v4 中已經被棄用為 FSTDEP013
,因此您應該已經更新您的程式碼以使用新的簽名。
簡化對路由定義的存取
所有與存取路由定義相關的已棄用屬性都已被移除,現在可以透過 request.routeOptions
存取。
程式碼 | 描述 | 如何解決 | 討論 |
---|---|---|---|
FSTDEP012 | 您正在嘗試存取已棄用的 request.context 屬性。 | 請使用 request.routeOptions.config 或 request.routeOptions.schema 。 | #4216 #5084 |
FSTDEP015 | 您正在存取已棄用的 request.routeSchema 屬性。 | 請使用 request.routeOptions.schema 。 | #4470 |
FSTDEP016 | 您正在存取已棄用的 request.routeConfig 屬性。 | 請使用 request.routeOptions.config 。 | #4470 |
FSTDEP017 | 您正在存取已棄用的 request.routerPath 屬性。 | 請使用 request.routeOptions.url 。 | #4470 |
FSTDEP018 | 您正在存取已棄用的 request.routerMethod 屬性。 | 請使用 request.routeOptions.method 。 | #4470 |
FSTDEP019 | 您正在存取已棄用的 reply.context 屬性。 | 請使用 reply.routeOptions.config 或 reply.routeOptions.schema 。 | #5032 #5084 |
請參閱 #5616 以瞭解更多資訊。
reply.redirect()
有新的簽名
reply.redirect()
方法具有新的簽名:reply.redirect(url: string, code?: number)
。
// v4
reply.redirect(301, '/new-route')
將其變更為
// v5
reply.redirect('/new-route', 301)
這在 v4 中已經被棄用為 FSTDEP021
,因此您應該已經更新您的程式碼以使用新的簽名。
現在禁止修改 reply.sent
在 v4 中,您可以修改 reply.sent
屬性以防止傳送回應。這在 v5 中已不再可能,請改用 reply.hijack()
。
// v4
fastify.get('/route', (req, reply) => {
reply.sent = true;
reply.raw.end('hello');
});
將其變更為
// v5
fastify.get('/route', (req, reply) => {
reply.hijack();
reply.raw.end('hello');
});
這在 v4 中已經被棄用為 FSTDEP010
,因此您應該已經更新您的程式碼以使用新的簽名。
路由版本控制簽名變更的限制
我們已變更路由版本控制限制的簽名。已移除 version
和 versioning
選項,您應該改用 constraints
選項。
程式碼 | 描述 | 如何解決 | 討論 |
---|---|---|---|
FSTDEP008 | 您正在透過路由 {version: "..."} 選項使用路由限制。 | 請使用 {constraints: {version: "..."}} 選項。 | #2682 |
FSTDEP009 | 您正在透過伺服器 {versioning: "..."} 選項使用自訂路由版本控制策略。 | 請使用 {constraints: {version: "..."}} 選項。 | #2682 |
當 exposeHeadRoutes: true
時,HEAD
路由需要在 GET
之前註冊
當 exposeHeadRoutes: true
時,我們對自訂 HEAD
路由有更嚴格的要求。
當您提供自訂 HEAD
路由時,您必須明確將 exposeHeadRoutes
設定為 false
// v4
fastify.get('/route', {
}, (req, reply) => {
reply.send({ hello: 'world' });
});
fastify.head('/route', (req, reply) => {
// ...
});
// v5
fastify.get('/route', {
exposeHeadRoutes: false
}, (req, reply) => {
reply.send({ hello: 'world' });
});
fastify.head('/route', (req, reply) => {
// ...
});
或將 HEAD
路由放在 GET
之前。
// v5
fastify.head('/route', (req, reply) => {
// ...
});
fastify.get('/route', {
}, (req, reply) => {
reply.send({ hello: 'world' });
});
這在 #2700 中已變更,舊的行為在 v4 中已棄用為 FSTDEP007
。
已移除 request.connection
v5 中已移除 request.connection
屬性。您應該改用 request.socket
。
// v4
fastify.get('/route', (req, reply) => {
console.log(req.connection.remoteAddress);
return { hello: 'world' };
});
// v5
fastify.get('/route', (req, reply) => {
console.log(req.socket.remoteAddress);
return { hello: 'world' };
});
這在 v4 中已經被棄用為 FSTDEP05
,因此您應該已經更新您的程式碼以使用新的簽名。
已移除 reply.getResponseTime()
,請改用 reply.elapsedTime
v5 中已移除 reply.getResponseTime()
方法。您應該改用 reply.elapsedTime
。
// v4
fastify.get('/route', (req, reply) => {
console.log(reply.getResponseTime());
return { hello: 'world' };
});
// v5
fastify.get('/route', (req, reply) => {
console.log(reply.elapsedTime);
return { hello: 'world' };
});
這在 v4 中已經被棄用為 FSTDEP20
,因此您應該已經更新您的程式碼以使用新的簽名。
fastify.hasRoute()
現在符合 find-my-way
的行為
fastify.hasRoute()
方法現在符合 find-my-way
的行為,並且需要將路由定義作為在路由中定義的方式傳遞。
// v4
fastify.get('/example/:file(^\\d+).png', function (request, reply) { })
console.log(fastify.hasRoute({
method: 'GET',
url: '/example/12345.png'
)); // true
// v5
fastify.get('/example/:file(^\\d+).png', function (request, reply) { })
console.log(fastify.hasRoute({
method: 'GET',
url: '/example/:file(^\\d+).png'
)); // true
移除部分非標準 HTTP 方法
我們已從 Fastify 中移除下列 HTTP 方法:
PROPFIND
PROPPATCH
MKCOL
COPY
MOVE
LOCK
UNLOCK
TRACE
SEARCH
現在可以使用 acceptHTTPMethod
方法將它們加回來。
const fastify = Fastify()
// add a new http method on top of the default ones:
fastify.acceptHTTPMethod('REBIND')
// add a new HTTP method that accepts a body:
fastify.acceptHTTPMethod('REBIND', { hasBody: true })
// reads the HTTP methods list:
fastify.supportedMethods // returns a string array
詳情請參閱 #5567。
移除裝飾器中對參考型別的支援
現在禁止使用參考型別(Array
、Object
)裝飾 Request/Reply,因為此參考會在所有請求之間共用。
// v4
fastify.decorateRequest('myObject', { hello: 'world' });
// v5
fastify.decorateRequest('myObject');
fastify.addHook('onRequest', async (req, reply) => {
req.myObject = { hello: 'world' };
});
或將其轉為函式
// v5
fastify.decorateRequest('myObject', () => { hello: 'world' });
或是作為 getter
// v5
fastify.decorateRequest('myObject', {
getter () {
return { hello: 'world' }
}
});
詳情請參閱 #5462。
移除對帶有 Content-Type: application/json
標頭和空主體的 DELETE 請求的支援
在 v4 中,Fastify 允許接受帶有 Content-Type: application/json
標頭和空主體的 DELETE
請求。在 v5 中不再允許這樣做。
詳情請參閱 #5419。
外掛程式不再能混用回呼/Promise API
在 v4 中,外掛程式可以混用回呼和 Promise API,導致意外行為。在 v5 中不再允許這樣做。
// v4
fastify.register(async function (instance, opts, done) {
done();
});
// v5
fastify.register(async function (instance, opts) {
return;
});
或
// v5
fastify.register(function (instance, opts, done) {
done();
});
移除 getDefaultRoute
和 setDefaultRoute
方法
getDefaultRoute
和 setDefaultRoute
方法已在 v5 中移除。
詳情請參閱 #4485 和 #4480。這在 v4 中已棄用為 FSTDEP014
,因此您應該已經更新了程式碼。
新功能
診斷通道支援
Fastify v5 現在原生支援 診斷通道 API,並提供追蹤請求生命週期的方式。
'use strict'
const diagnostics = require('node:diagnostics_channel')
const sget = require('simple-get').concat
const Fastify = require('fastify')
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
console.log(msg.route.url) // '/:id'
console.log(msg.route.method) // 'GET'
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
// msg is the same as the one emitted by the 'tracing:fastify.request.handler:start' channel
console.log(msg)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
// in case of error
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/:id',
handler: function (req, reply) {
return { hello: 'world' }
}
})
fastify.listen({ port: 0 }, function () {
sget({
method: 'GET',
url: fastify.listeningOrigin + '/7'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.same(JSON.parse(body), { hello: 'world' })
})
})