Skip to Content

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 版本。

不兼容更改

querystringparamsbody 及响应模式现在需要完整的 JSON 模式

从 v5 版本开始,Fastify 要求为 querystringparamsbody 提供一个完整的 JSON 模式。请注意,jsonShortHand 选项已被移除。

如果使用默认的 JSON Schema 验证器,则需要提供 querystringparamsbody 和响应模式的完整 JSON 模式,包括 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 日志器选项以及自定义日志实例。这导致了很大的混淆。

因此,在 v5 版本中,logger 选项将不再接受自定义日志器。要使用自定义日志器,请改用 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 实例将不再默认支持在查询字符串中使用分号作为分隔符。这是因为这种行为不符合 RFC 3986  标准。

如果您仍然希望使用分号作为分隔符,可以在服务器配置中设置 useSemicolonDelimiter: true

const fastify = require('fastify')({ useSemicolonDelimiter: true });

参数对象不再具有原型

在 v4 版本中,parameters 对象有一个原型。而在 v5 中,这种情况已经改变。 这意味着您将无法再访问 Object 继承的属性(如 toStringhasOwnProperty)。

// 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 }; });

这通过防止原型污染攻击增强了应用程序的安全性。

类型提供者现在区分验证器和序列化器模式

在v4版本中,类型提供者的类型对于验证和序列化是一样的。 而在v5版本中,类型提供者被拆分为两个独立的类型:ValidatorSchemaSerializerSchema

@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() 方法的可变参数签名已被移除。 这意味着你不能再使用可变数量的参数调用 .listen()

// v4 fastify.listen(8000)

将变为:

// v5 fastify.listen({ port: 8000 })

这在v4版本中已经被标记为废弃(FSTDEP011),因此你应该已经更新了代码以使用新的签名。

直接返回尾部信息的功能已被移除

在v4版本中,您可以直接从处理程序返回尾部信息。 但在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.configrequest.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.configreply.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 被弃用,因此你应该已更新代码以使用新的签名。

路由版本化签名的约束条件

我们更改了路由版本化的签名。 移除了 versionversioning 选项,并建议使用 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 中,reply.getResponseTime() 方法已被标记为弃用(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

现在可以通过使用 addHttpMethod 方法重新添加这些方法。

const fastify = Fastify() // 在默认的 HTTP 方法之上新增一个 HTTP 方法: fastify.addHttpMethod('REBIND') // 添加一个新的接受请求体的 HTTP 方法: fastify.addHttpMethod('REBIND', { hasBody: true }) // 读取支持的 HTTP 方法列表: fastify.supportedMethods // 返回字符串数组

更多详细信息请参阅 #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(); });

请求现在包含 hosthostnameport,且 hostname 不再包括端口号

在 Fastify v4 中,req.hostname 包含主机名和服务器的端口,因此本地可能值为 localhost:1234。 从 v5 开始,我们与 Node.js URL 对象对齐,并现在包含 hosthostnameport 属性。req.host 的值与 v4 中的 req.hostname 相同, 而 req.hostname 在存在端口的情况下仅包括主机名(不带端口号),并且 req.port 包含纯端口号。 更多信息请参见 #4766 #4682 

移除 getDefaultRoutesetDefaultRoute 方法

在 v5 中,移除了 getDefaultRoutesetDefaultRoute 方法。

更多信息请参见 #4485 #4480 。 这些方法在 v4 中已经弃用为 FSTDEP014,因此您应该已更新了代码。

新功能

诊断通道支持

Fastify v5 现在原生支持 Diagnostics Channel  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 与由 'tracing:fastify.request.handler:start' 通道发出的消息相同 console.log(msg) }) diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => { // 在发生错误时 }) 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' }) }) })

有关更多详细信息,请参阅 文档 #5252 

贡献者

以下是核心 Fastify 包的所有贡献者的完整列表。请考虑为那些能够接受赞助的项目做出贡献。

贡献者赞助链接包名
10xLaCroixDrinker❤️ sponsor fastify-cli
Bram-dcfastify; fastify-swagger
BrianValentefastify
BryanAbatefastify-cli
Cadienvan❤️ sponsor fastify
Cangitfastify
Cyberlanefastify-elasticsearch
Eomm❤️ sponsor ajv-compiler; fastify; fastify-awilix; fastify-diagnostics-channel; fastify-elasticsearch; fastify-hotwire; fastify-mongodb; fastify-nextjs; fastify-swagger-ui; under-pressure
EstebanDalelR❤️ sponsor fastify-cli
Fdawgs❤️ sponsor aws-lambda-fastify; csrf-protection; env-schema; fastify; fastify-accepts; fastify-accepts-serializer; fastify-auth; fastify-awilix; fastify-basic-auth; fastify-bearer-auth; fastify-caching; fastify-circuit-breaker; fastify-cli; fastify-cookie; fastify-cors; fastify-diagnostics-channel; fastify-elasticsearch; fastify-env; fastify-error; fastify-etag; fastify-express; fastify-flash; fastify-formbody; fastify-funky; fastify-helmet; fastify-hotwire; fastify-http-proxy; fastify-jwt; fastify-kafka; fastify-leveldb; fastify-mongodb; fastify-multipart; fastify-mysql; fastify-nextjs; fastify-oauth2; fastify-passport; fastify-plugin; fastify-postgres; fastify-rate-limit; fastify-redis; fastify-reply-from; fastify-request-context; fastify-response-validation; fastify-routes; fastify-routes-stats; fastify-schedule; fastify-secure-session; fastify-sensible; fastify-swagger-ui; fastify-url-data; fastify-websocket; fastify-zipkin; fluent-json-schema; forwarded; middie; point-of-view; process-warning; proxy-addr; safe-regex2; secure-json-parse; under-pressure
Gehbtfastify-secure-session
Gesma94fastify-routes-stats
H4ad❤️ sponsor aws-lambda-fastify
JohanMandersfastify-secure-session
LiviaMedeirosfastify
Momy93fastify-secure-session
MunifTanjimfastify-swagger-ui
Nanosyncfastify-secure-session
RafaelGSS❤️ sponsor fastify; under-pressure
Rantoledofastify
SMNBLMRRfastify
SimoneDevktfastify-cli
Tony133fastify
Uzlopak❤️ sponsor fastify; fastify-autoload; fastify-diagnostics-channel; fastify-hotwire; fastify-nextjs; fastify-passport; fastify-plugin; fastify-rate-limit; fastify-routes; fastify-static; fastify-swagger-ui; point-of-view; under-pressure
Zamiellfastify-secure-session
aadito123fastify
aaroncadillac❤️ sponsor fastify
aarontravassfastify
acro5piano❤️ sponsor fastify-secure-session
adamward459fastify-cli
adrai❤️ sponsor aws-lambda-fastify
alenap93fastify
alexandrucancescufastify-nextjs
anthonyringoetaws-lambda-fastify
arshcodemodfastify
autopulatedpoint-of-view
barbierifastify
beyazitfastify
big-kahuna-burger❤️ sponsor fastify-cli; fastify-compress; fastify-helmet
bilalshareeffastify-routes
blue86321fastify-swagger-ui
bodinsamuelfastify-rate-limit
busybox11❤️ sponsor fastify
climba03003csrf-protection; fastify; fastify-accepts; fastify-accepts-serializer; fastify-auth; fastify-basic-auth; fastify-bearer-auth; fastify-caching; fastify-circuit-breaker; fastify-compress; fastify-cors; fastify-env; fastify-etag; fastify-flash; fastify-formbody; fastify-http-proxy; fastify-mongodb; fastify-swagger-ui; fastify-url-data; fastify-websocket; middie
dancastillo❤️ sponsor fastify; fastify-basic-auth; fastify-caching; fastify-circuit-breaker; fastify-cors; fastify-helmet; fastify-passport; fastify-response-validation; fastify-routes; fastify-schedule
danny-andrewsfastify-kafka
davidcralph❤️ sponsor csrf-protection
davideroffounder-pressure
dhensbyfastify-cli
dmkngfastify
domdomeggfastify
faustmanfastify-cli
floridemaifluent-json-schema
fox1tfastify-autoload
giuliowaitforitdavidefastify
gunters63fastify-reply-from
gurgundayfastify; fastify-circuit-breaker; fastify-cookie; fastify-multipart; fastify-mysql; fastify-rate-limit; fastify-response-validation; fastify-sensible; fastify-swagger-ui; fluent-json-schema; middie; proxy-addr; safe-regex2; secure-json-parse
ildellaunder-pressure
james-kagurufastify
jcbainfastify-http-proxy
jdhollanderfastify-swagger-ui
jean-micheletfastify; fastify-autoload; fastify-cli; fastify-mysql; fastify-sensible
johavenfastify-multipart
jordanebelangerfastify-plugin
jscheffnerfastify
jsprwfastify-secure-session
jsumners❤️ sponsor ajv-compiler; avvio; csrf-protection; env-schema; fast-json-stringify; fastify; fastify-accepts; fastify-accepts-serializer; fastify-auth; fastify-autoload; fastify-awilix; fastify-basic-auth; fastify-bearer-auth; fastify-caching; fastify-circuit-breaker; fastify-compress; fastify-cookie; fastify-cors; fastify-env; fastify-error; fastify-etag; fastify-express; fastify-flash; fastify-formbody; fastify-funky; fastify-helmet; fastify-http-proxy; fastify-jwt; fastify-kafka; fastify-leveldb; fastify-multipart; fastify-mysql; fastify-oauth2; fastify-plugin; fastify-postgres; fastify-redis; fastify-reply-from; fastify-request-context; fastify-response-validation; fastify-routes; fastify-routes-stats; fastify-schedule; fastify-secure-session; fastify-sensible; fastify-static; fastify-swagger; fastify-swagger-ui; fastify-url-data; fastify-websocket; fastify-zipkin; fluent-json-schema; forwarded; light-my-request; middie; process-warning; proxy-addr; safe-regex2; secure-json-parse; under-pressure
karankrainaunder-pressure
kerolloz❤️ sponsor fastify-jwt
kibertoadfastify-rate-limit
kukidon-devfastify-passport
kunal097fastify
lamweilifastify-sensible
lemonclownfastify-mongodb
liuhanqufastify
matthykfastify-plugin
mch-dskfastify
mcollina❤️ sponsor ajv-compiler; avvio; csrf-protection; fastify; fastify-accepts; fastify-accepts-serializer; fastify-auth; fastify-autoload; fastify-awilix; fastify-basic-auth; fastify-bearer-auth; fastify-caching; fastify-circuit-breaker; fastify-cli; fastify-compress; fastify-cookie; fastify-cors; fastify-diagnostics-channel; fastify-elasticsearch; fastify-env; fastify-etag; fastify-express; fastify-flash; fastify-formbody; fastify-funky; fastify-helmet; fastify-http-proxy; fastify-jwt; fastify-kafka; fastify-leveldb; fastify-multipart; fastify-mysql; fastify-oauth2; fastify-passport; fastify-plugin; fastify-postgres; fastify-rate-limit; fastify-redis; fastify-reply-from; fastify-request-context; fastify-response-validation; fastify-routes; fastify-routes-stats; fastify-schedule; fastify-secure-session; fastify-static; fastify-swagger; fastify-swagger-ui; fastify-url-data; fastify-websocket; fastify-zipkin; fluent-json-schema; light-my-request; middie; point-of-view; proxy-addr; secure-json-parse; under-pressure
melroy89❤️ sponsor under-pressure
metcoder95❤️ sponsor fastify-elasticsearch
mhamannfastify-cli
mihaurfastify-elasticsearch
mikesammfastify
mikhael-abdallahsecure-json-parse
miquelfire❤️ sponsor fastify-routes
mirariesfastify-swagger-ui
mohab-samehfastify
monish001fastify
moradebianchetti81fastify
mouhannad-shaws-lambda-fastify
multivoltagepoint-of-view
muya❤️ sponsor under-pressure
mweberxyzpoint-of-view
nflaigfastify
nickfla1avvio
o-azprocess-warning
ojeytonwilliamscsrf-protection
onosendifastify-formbody
philippviereckfastify
pip77fastify-mongodb
puskin94fastify
remidewittefastify
rozzillafastify
samialduryfastify-cli
sknetlfastify-cors
sourcecodeitfastify
synapseenv-schema
timursaurussecure-json-parse
tlhunterfastify
tlund101fastify-rate-limit
ttshiversfastify-http-proxy
voxpelli❤️ sponsor fastify
weixinwufastify-cli
zetarakufastify-cli