Express vs Fastify

Just curious, for Meteor v3 transition, Express was chosen as the underlying framework that webapp utilizes. What was the thought process behind this decision versus other more performant frameworks like Fastify?

@italojs @nachocodoner @grubba

From my understanding express was just a few steps away from the beforehand used connect package from sencha labs.
I think earlier express versions even used connect as dependency.

1 Like

@filipenevola was the CEO in charge at that time if I recall correctly

This decision wasn’t made during my time on the core. My involvement with Express has been limited to the upgrade to Express 5 and the next feature of exposing the Meteor user context on it.

However, I can understand some insights on this decision.

Meteor has historically relied on connect, and as @jkuester mentioned, Express was built on top of it, so its API has always been close to what Meteor already used. For a mature framework like Meteor, where many production apps depend on stable web server behavior, minimizing compatibility issues is critical: not just for the core, but for all the apps that rely deeply on custom server endpoints. Express feels like the natural successor.

I’m aware that Fastify offers Express-compatibility plugins, but the performance overhead may not be worth it, and even the Fastify team recommends gradually moving away from those patterns toward native Fastify APIs. That defeats the purpose for a migration like this, in favor to a complete breaking change apporach.

If the decision had been mine, I would still prioritize migration safety over raw performance. The async/await migration from Meteor 2 to Meteor 3 was already a significant undertaking for the community, and introducing a new HTTP framework on top of that would have added unnecessary risk to an already complex transition. Also, has Express really a problem with performance within the Meteor ecosystem, knowing we strongly use the DDP protocol for API communication? Express endpoints are a secondary concern in Meteor, but the connect-style middleware API is deeply embedded in how Meteor core handles its internal request pipeline, and that matters more than raw HTTP performance.

Express is also more popular and mature, with a large ecosystem of plugins and battle-tested middleware that Fastify still does not fully match. It may have improved a lot, but it is not the same, and it does not outweigh compatibility.

In my opinion, performance isn’t always the deciding factor: it depends on context. If you can gain it without introducing breaking changes, migration costs, or ecosystem gaps, it’s an easy win. But when those risks are real, pragmatism should win.

This same logic applies elsewhere: SWC vs OXC, Rspack vs Vite, Capacitor vs Tauri. Tool fatigue is real. Rather than replacing the core all at once, a better path is often to make the core more adaptable, let community packages explore alternatives, and keep migration friction low. Once adoption is proven in the wild, a core change becomes a much more natural and safer decision. I know that these days, with AI, migrations have a lower cost. At some point, this argument could outweigh others and support introducing larger changes with more confidence.

6 Likes

Thank you for providing a thorough write up to my question! Answers a lot of questions I had regarding Express in Meteor.

1 Like

I was already CEO when we made that decision. Initially, it wasn’t included in version 3.0, but rather in 3.1 or something similar. However, connect was not being maintained, and since we already had to work on related areas, we decided to modify it and keep it as secure and close as possible to version 2.x.

@nachocodoner explained really well.

migration safety over raw performance. The async/await migration from Meteor 2 to Meteor 3 was already a significant undertaking for the community, and introducing a new HTTP framework on top of that would have added unnecessary risk to an already complex transition

2 Likes