大家好我是Joney 好久没回来了,最近在看Nest的release tag发现官方还是很活跃的,作为一个开源项目 迭代和维护是非常重要的事情, 这篇文章我们来唠嗑一下 Nest近期的更新内容。本文梳理了从24年1月份到 24年底的所有官方更新内容,接上篇文章 本篇是针对Bug相关的内容做的总结梳理和总统计
release一览
以下统计是简单粗略的统计,后续文章会改进此方法。数据上可能不是100%准确
Nest 从 24年1月开发的24年第一次 release v10.3.1 直到年底 一共发布了13个release v10.4.15 ,到25年直接从v10.x 迈入 v11.x
这些不断地release和仓库迭代, 看得出作者和开发社区还是很用心的,至少是一个值得信赖的项目,不像其它项目做完KPI就跑路, 蛐蛐一下国内的大厂哈(表情-狗头保命),是 开源是很重要,但是持续维护才能让开发者信任。
总结来说,24年 Nest的工作分布如下
可以看到许多工作是依赖升级迭代支持相关的内容。bug修复和功能升级还是很快的,截止发文官方仓库的openIssue在29个
再看 以月份为维度的分析:老外的年底也卷 哈哈哈 (表情-狗头)
Bug修复项
接下来我们分析一下 24年 重要的bug修复

01.为动态模块和 微服务Client生成hash
在这个bug修复前,下面的这样的代码log 只输出了一次,就算你多层注册(前提条件 我们把 alwaysTransient: false 这意味着 :
- 即使多次注册 module(每次传入不同 options),provider 的工厂函数(比如
console.log(options)
)只执行了一次,而不是每次都执行。 - 期望行为:每次注册都应生成一个新的 provider,每次都应执行一次工厂函数。
- 原因 :原本的实现用的是
useFactory
,但由于工厂函数其实只在 provider 注册阶段执行一次,导致无论注册几次,实际上只实例化了一次。所以这个PR把 useFactory 换成了 useValue github.com/nestjs/nest...
ts
// my-custom-module.ts
import { Inject, Module } from '@nestjs/common';
import { ConfigurableModuleBuilder } from '@nestjs/common';
export const {
ConfigurableModuleClass,
MODULE_OPTIONS_TOKEN: MY_CUSTOM_MODULE_OPTIONS,
} = new ConfigurableModuleBuilder<{
baseUrl?: string;
}>().build();
@Module({})
export class MyCustomModule extends ConfigurableModuleClass {
constructor(
@Inject(MY_CUSTOM_MODULE_OPTIONS)
private readonly options,
) {
console.log(options);
super();
}
}
// app.module.ts
@Module({
imports: [
MyCustomModule.register({ baseUrl: 'https://google.com' }),
MyCustomModule.register({ baseUrl: 'https://google1.com' }),
MyCustomModule.register({ baseUrl: 'https://google2.com' }),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
这个bug修之后 就可以正常使用了
02.为gRPC 提供了正确的关闭方法
如果你之前有用GRPC 你会发现这样的一个问题
ts
const client = ClientProxyFactory.create({
transport: Transport.GRPC
});
const service = client.getService('ServiceName')
await firstValueFrom(service.callSomething())
// do nothing with connection 这样关闭是无效的
client.close()
// workaround to close connection properly 你得这样来关
client.getClientByServiceName<Client>('ServiceName').close();
Client from @grpc/proto-loader
究其原因其实 nest 底层实现有问题 见下图 现在改之后就能正确的close 了,虽然之前通过其他方法也能close ,但是nest提供的close 不能用感觉像个bug似的 所以现在修复了
03.@Search装饰器有问题 被修复了
SEARCH 不是标准HTTP方法,而是WebDAV扩展, 主要的用处是:日历服务器(CalDAV),文档管理、符合WebDAV规范,邮件服务器(Exchange)等, Nest其实是有些这个装饰器 但是在文档上没写。
问题是 这里没有对Search处理,会导致
ts
@Search()
searchHello(): string {
return 'SEARCH ' + this.appService.getHello();
}
返回的居然是 SEARCH Hello World! 而不是 Hello World,理论上要返回的Hello Word 才是
解决:+针对Search的处理
04. 和RabbitMQ有关的问题
我们发现如果是这样的 RabbitMQ 配置,Nest 不会响应 后续的报文
txt
prefetchCount: 1
noAck: false
当应用程序收到不可处理的消息时,如果 noAck 为 true,则应用程序将继续接收消息,因为已自动确认不可处理的消息,但是如果是false 那么就有问题了。现在已经修复了。
我们还发现 RabbitMQ 的 no-assert 配置项未被正确应用,导致连接参数不符合预期设。这点也修了 原因:在原有实现中,no-assert 选项未正确传递给队列声明(assertQueue)或相应的方法,配置未生效。, 解决:修正了配置传递逻辑,确保 no-assert 选项能准确传递到 RabbitMQ 的底层方法,从而实现预期的行为。
05.NATS 微服务中inboxPrefix没实现 实现了一下
06.ts类型错误问题
KafkaJs 和 Nest Kafka 微服务类型之间的类型不兼容。修复了
07.设置全局前缀时,不为根路由执行中间件
在代码中如果你写下了这样的代码
ts
//主应用程序 main
setGlobalPrefix('api')
// 主程序的 Module
@Module({
imports: [
ProviderPinoModule.forRoot({
pinoOptions: {
base: undefined,
formatters: { level: (label) => ({ level: label }) },
transport: { target: 'pino-pretty' },
},
}),
ObservabilityModule.forRoot({
level: ObservabilityLevel.DEBUG,
providers: [ProviderPino],
middleware: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
//然后又+了一个 ObservabilityModule 模块
@Global()
@Module({
providers: [
ObservabilityMiddleware,
ObservabilityLogger,
ObservabilityService,
ObservabilityProviderService,
],
exports: [ObservabilityLogger, ObservabilityService, ObservabilityMiddleware],
})
export class ObservabilityModule
extends ConfigurableModuleClass
implements NestModule
{
constructor(
@Inject(MODULE_OPTIONS_TOKEN)
private readonly observabilityOptions: ObservabilityModuleOptions
) {
super();
}
configure(consumer: MiddlewareConsumer) {
if (this.observabilityOptions.middleware === true) {
// 这句话就没有用了
consumer.apply(ObservabilityMiddleware).forRoutes('*');
}
}
}
主要的问题是: 当设置了全局前缀(global prefix)时,根路由(/)的中间件不会被执行,导致根路径的请求无法经过中间件处理。
处理:修正了路由与中间件的匹配逻辑,确保在设置全局前缀后,根路由(/)同样能够正确地执行对应的中间件。
08.日志堆栈不准确
问题:异常栈追踪(stacktrace)的正则表达式(regex)不正确,导致解析栈信息时出错或不准确。
原因: 原有的正则表达式无法正确匹配某些格式的堆栈信息,可能遗漏关键信息或导致解析失败。
修复:修正了 stacktrace 的正则表达式,使其能更准确地匹配和解析不同格式的堆栈信息。例如,更新后的正则更好地兼容了不同浏览器或 Node.js 的栈格式。

09.SSE 的潜在泄露问题
问题:在使用服务端事件(Server-Sent Events, SSE)时,存在潜在的内存泄漏问题。
问题:SSE 连接断开后,相关的订阅和资源未被正确清理,导致内存持续占用,最终可能引发内存泄漏。
解决: 在 SSE 连接关闭时,增加了订阅和资源的清理逻辑(如调用 unsubscribe 等),确保断开连接后可以及时释放资源,防止内存泄漏。+ 一个cb 过去

10.框架相关的问题 fastify
当未使用 enableVersioning 配置时,NestJS 在某些情况下会导致 Fastify 服务崩溃(crash)。
原因:内部处理版本控制相关逻辑时,未启用 enableVersioning 的情况下缺少必要的保护判断,访问了未定义的属性或方法。
解决:在相关代码中增加了判断,只有在启用 enableVersioning 时才执行相应的版本处理逻辑,避免因未启用配置而导致的服务崩溃。+一些可选链判断 packages/platform-fastify/adapters/fastify-adapter.ts

11.forward references(前向引用)异常
问题 在模块的 exports 数组中使用 forward references(前向引用)时,会导致 NestJS 的依赖解析或导出模块行为异常。
原因 原有实现未正确处理 exports 数组中的 forwardRef,导致前向引用的模块未被正确识别和处理,进而引发依赖注入或模块导出的问题。
解决方法 对 exports 数组中的每一项增加了 forwardRef 的判断和处理逻辑,确保在遇到 forwardRef 时能够正确解析其引用目标,从而保证模块导出和依赖注入机制的正常工作。
12.RabbitMQ 初始化时配置丢失
问题 RabbitMQ 客户端(rmq client)在初始化时,部分配置选项(options)被遗漏或丢弃,导致某些自定义配置无法生效。
原因 在原有代码中,部分传入的 RabbitMQ 客户端配置参数未被正确包含或传递给底层实现,从而被意外忽略。
解决方法 修改了客户端初始化逻辑,将之前被丢弃的配置选项重新包含进 rmq client 的参数传递中,确保所有用户配置能被正确应用。

13、MQTT, QoS参数问题
问题 当使用 MQTT 并设置了 QoS(服务质量)参数时,消息无法被正常发送(emit),导致消息丢失。
原因 原有实现没有正确处理 QoS 参数的相关逻辑,导致设置了 QoS 后消息发送机制失效,没有消息被发送出去。
解决方法 修复了对 QoS 参数的处理逻辑,确保在设置了 QoS 后,消息可以正常通过 MQTT 发送。代码中对发送消息的相关分支和参数传递做了修正,使其兼容并支持带 QoS 的消息。
14.上下文未能正确合并
请求实例(request instance)中租户信息(tenant payload)和请求上下文(req context)未能正确合并,导致多租户场景下部分上下文数据丢失。
原因 原有实现中,tenant payload 没有和 req context 做合并处理,导致请求实例没有包含完整的上下文和租户数据。
解决方法 在生成请求实例时,将 req context 与 tenant payload 进行了合并,确保两者的数据都能注入到 request 实例中。代码中直接对两个对象进行了合并操作,保证多租户环境下上下文信息完整。
15. Socket.IO 服务关闭 HTTP 过早关闭
问题
在 Socket.IO 服务关闭(shutdown)时,HTTP 服务器可能被过早关闭,导致连接未正确释放或出现异常。
原因
原先实现中,Socket.IO 关闭流程会直接关闭 HTTP 服务器,没有先确保所有 Socket 连接已经正确断开和清理,带来连接中断或资源未释放的问题。
解决方法
调整了 Socket.IO 的关闭流程,防止在主动 shutdown 时 HTTP 服务器被提前关闭,确保所有 Socket 连接和相关资源都能被安全释放后再关闭 HTTP 服务器。代码层面通过顺序调整或增加判断,杜绝了"早关闭"问题。
16.Space相关的问题
对于 transient(瞬态)和 lazy(延迟)provider,依赖项无法被正确解析,导致这些 provider 在运行时无法正常注入所需依赖。
原因 原有实现没有在创建 transient lazy provider 时正确处理其依赖解析逻辑,导致依赖未被正确注入。
解决方法 修复了 transient lazy provider 的依赖解析流程,确保在实例化这些 provider 时,依赖能够被正常解析和注入。代码中主要调整了依赖注入处理逻辑,使其对 transient/lazy 场景下的 provider 也能正确工作。
17.gRPC 使用client streaming有bug
问题 gRPC 客户端在使用流(client streaming)时存在 bug,可能导致流式通信异常或数据处理错误。
原因 原有实现对 gRPC 客户端流的处理不完善,导致数据流传递或事件监听过程中出现问题,比如无法正确响应、关闭或处理数据。
解决方法 完善了数据流的事件监听和响应机制,确保 client streaming 能够正常工作。代码中针对流事件的监听、数据传递和关闭流程做了修正,使流式 gRPC 通信更加可靠。
18.RawBody 和 pipes 缺少ts声明
问题 RawBody 装饰器在结合管道(pipes)使用时缺少类型声明,导致类型推断不准确,影响开发体验和类型安全。
原因 之前的实现没有为 RawBody 装饰器在使用管道时提供完善的类型定义,TypeScript 无法正确推导参数类型。
解决方法 为 RawBody 装饰器在结合 pipes 使用的场景增加了类型声明,使其参数类型能够被准确推断和检查。代码上增加了类型定义,提升了类型安全和开发体验。