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
及响应模式现在需要完整的 JSON 模式
从 v5 版本开始,Fastify 要求为 querystring
、params
和 body
提供一个完整的 JSON 模式。请注意,jsonShortHand
选项已被移除。
如果使用默认的 JSON Schema 验证器,则需要提供 querystring
、params
、body
和响应模式的完整 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
继承的属性(如 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 };
});
这通过防止原型污染攻击增强了应用程序的安全性。
类型提供者现在区分验证器和序列化器模式
在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()
方法的可变参数签名已被移除。
这意味着你不能再使用可变数量的参数调用 .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.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 中,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();
});
请求现在包含 host
、hostname
和 port
,且 hostname
不再包括端口号
在 Fastify v4 中,req.hostname
包含主机名和服务器的端口,因此本地可能值为 localhost:1234
。
从 v5 开始,我们与 Node.js URL 对象对齐,并现在包含 host
、hostname
和 port
属性。req.host
的值与 v4 中的 req.hostname
相同,
而 req.hostname
在存在端口的情况下仅包括主机名(不带端口号),并且 req.port
包含纯端口号。
更多信息请参见 #4766 和 #4682 。
移除 getDefaultRoute
和 setDefaultRoute
方法
在 v5 中,移除了 getDefaultRoute
和 setDefaultRoute
方法。
更多信息请参见 #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' })
})
})
贡献者
以下是核心 Fastify 包的所有贡献者的完整列表。请考虑为那些能够接受赞助的项目做出贡献。
贡献者 | 赞助链接 | 包名 |
---|---|---|
10xLaCroixDrinker | ❤️ sponsor | fastify-cli |
Bram-dc | fastify; fastify-swagger | |
BrianValente | fastify | |
BryanAbate | fastify-cli | |
Cadienvan | ❤️ sponsor | fastify |
Cangit | fastify | |
Cyberlane | fastify-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 |
Gehbt | fastify-secure-session | |
Gesma94 | fastify-routes-stats | |
H4ad | ❤️ sponsor | aws-lambda-fastify |
JohanManders | fastify-secure-session | |
LiviaMedeiros | fastify | |
Momy93 | fastify-secure-session | |
MunifTanjim | fastify-swagger-ui | |
Nanosync | fastify-secure-session | |
RafaelGSS | ❤️ sponsor | fastify; under-pressure |
Rantoledo | fastify | |
SMNBLMRR | fastify | |
SimoneDevkt | fastify-cli | |
Tony133 | fastify | |
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 |
Zamiell | fastify-secure-session | |
aadito123 | fastify | |
aaroncadillac | ❤️ sponsor | fastify |
aarontravass | fastify | |
acro5piano | ❤️ sponsor | fastify-secure-session |
adamward459 | fastify-cli | |
adrai | ❤️ sponsor | aws-lambda-fastify |
alenap93 | fastify | |
alexandrucancescu | fastify-nextjs | |
anthonyringoet | aws-lambda-fastify | |
arshcodemod | fastify | |
autopulated | point-of-view | |
barbieri | fastify | |
beyazit | fastify | |
big-kahuna-burger | ❤️ sponsor | fastify-cli; fastify-compress; fastify-helmet |
bilalshareef | fastify-routes | |
blue86321 | fastify-swagger-ui | |
bodinsamuel | fastify-rate-limit | |
busybox11 | ❤️ sponsor | fastify |
climba03003 | csrf-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-andrews | fastify-kafka | |
davidcralph | ❤️ sponsor | csrf-protection |
davideroffo | under-pressure | |
dhensby | fastify-cli | |
dmkng | fastify | |
domdomegg | fastify | |
faustman | fastify-cli | |
floridemai | fluent-json-schema | |
fox1t | fastify-autoload | |
giuliowaitforitdavide | fastify | |
gunters63 | fastify-reply-from | |
gurgunday | fastify; 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 | |
ildella | under-pressure | |
james-kaguru | fastify | |
jcbain | fastify-http-proxy | |
jdhollander | fastify-swagger-ui | |
jean-michelet | fastify; fastify-autoload; fastify-cli; fastify-mysql; fastify-sensible | |
johaven | fastify-multipart | |
jordanebelanger | fastify-plugin | |
jscheffner | fastify | |
jsprw | fastify-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 |
karankraina | under-pressure | |
kerolloz | ❤️ sponsor | fastify-jwt |
kibertoad | fastify-rate-limit | |
kukidon-dev | fastify-passport | |
kunal097 | fastify | |
lamweili | fastify-sensible | |
lemonclown | fastify-mongodb | |
liuhanqu | fastify | |
matthyk | fastify-plugin | |
mch-dsk | fastify | |
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 |
mhamann | fastify-cli | |
mihaur | fastify-elasticsearch | |
mikesamm | fastify | |
mikhael-abdallah | secure-json-parse | |
miquelfire | ❤️ sponsor | fastify-routes |
miraries | fastify-swagger-ui | |
mohab-sameh | fastify | |
monish001 | fastify | |
moradebianchetti81 | fastify | |
mouhannad-sh | aws-lambda-fastify | |
multivoltage | point-of-view | |
muya | ❤️ sponsor | under-pressure |
mweberxyz | point-of-view | |
nflaig | fastify | |
nickfla1 | avvio | |
o-az | process-warning | |
ojeytonwilliams | csrf-protection | |
onosendi | fastify-formbody | |
philippviereck | fastify | |
pip77 | fastify-mongodb | |
puskin94 | fastify | |
remidewitte | fastify | |
rozzilla | fastify | |
samialdury | fastify-cli | |
sknetl | fastify-cors | |
sourcecodeit | fastify | |
synapse | env-schema | |
timursaurus | secure-json-parse | |
tlhunter | fastify | |
tlund101 | fastify-rate-limit | |
ttshivers | fastify-http-proxy | |
voxpelli | ❤️ sponsor | fastify |
weixinwu | fastify-cli | |
zetaraku | fastify-cli |