Since "Express.js is an old framework that has not evolved for a long time. It's not a good choice for new projects since it can easily lead to security issues and memory leaks." -- H3. Which is also the case for Koa. (Update 2024-09-23: After about 10 years in alpha and beta, Express v5.0 was just released)
The focus here will be on those frameworks which support running service workers on the server-side and the modern Fetch API standard, so they can be run in Serverless and Edge environments (Cloudflare Workers etc.). So Fastify didn't make the cut for this article, even though it has a 2 year old experiment called fastify-edge. (But they wrote an excellent piece about the evolution from Node to Worker Environments that I recommend reading afterwards.)
Worker Runtimes fulfill the original promise of NodeJS: To use one language and share code between client and server. In practice, this never came to be. Instead the APIs of node and browsers have diverged. Worker Runtimes are bringing them back together. https://workers.js.org/#backend-for-frontend
Hono, H3, HatTip and Elysia are such next generation HTTP server frameworks (aka. web server middlewares). They run everywhere, on any JS runtime, even Serverless and Edge runtimes. Meaning not just on Node.js servers. They are also all TypeScript friendly.
Here's a quick overview of each, and then a comparison of some essential distinctions. (I had to dig up this information from a lot of various sources, so hopefully it's helpful to you all that it's now available here when you Google it.)
All of them have support for the Web Fetch API (Request/Response objects). But I'll show their most Express-like API's here, for familiarity.
Hono's API is similar to Express:
import { Hono } from 'hono';
const app = new Hono();
app.get('/hello', (c) => {
return c.json({ message: 'Hello!' });
});
export default app;
Repo started: Dec 12, 2021 - https://github.com/honojs/hono
Main contributors: 2.5
Github stars now: 12.4 k
NPM weekly downloads now: 143 796
Current state: Hono is on version 4+.
Tagline: "Fast, Lightweight, Web-standards. Runs on any JavaScript runtime."
Made for Cloudflare Workers first, but has a Node adapter. But Hono on Node seems terribly slow. But this is not reflected in the other benchmark: Web Frameworks Benchmark.
Hono is very web standards focused.
Hono has an RPC feature.
Hono aims to have batteries included: built-in, custom, and third-party middlewares, and helpers.
H3 allows manually registering router instances:
import { createApp, createRouter, defineEventHandler, toNodeListener } from "h3";
import { createServer } from "node:http";
export const app = createApp();
const router = createRouter();
app.use(router);
router.get('/', defineEventHandler((event) => {
return { message: 'Hello!' };
}),
);
createServer(toNodeListener(app)).listen(8000);
Repo started: Nov 15, 2020 - https://github.com/unjs/h3
Main contributors: 1.5
Github stars now: 2.9 k
NPM weekly downloads now: 976 744
Current state: H3 is on version 1.11.1.
Tagline: "The Web Framework for Modern JavaScript Era: H(TTP) server framework built for high performance and portability running in any JavaScript runtime."
"H3 is a composable [and tree-shakeable] framework. Instead of providing a big core, you start with a lightweight app instance and for every functionality, there is either a built-in utility or you can make yours. Composable utilities have huge advantages comparing to traditional plugin/middleware approaches: ..." https://h3.unjs.io/utils
H3 was extracted from Nitro/Nuxt around Jul 2, 2023 or later. Which may explain the relatively lower amount of github stars.
Made for Node first, but has adapters for Serverless or Edge JS runtimes (Cloudflare Workers etc.). H3 can also run on Bun with an adapter. This is not reflected in the bun-http-framework-benchmark yet, so follow this issue to see when it's added. Update: In v2 then H3 will be web native which suggests that no adapters would be needed.
H3 integrates with the UnJS ecosystem of JS tools.
NuxtJS (Vue meta-framework) is built upon Nitro (http-server extensions) which is built upon H3. Nitro adds file-based routing, asset handling, storage abstraction, etc. onto H3. (Nitro can selectively use Vite in one of its routing handlers, since Vite is only needed for the client server, not the static server, according to Nikhil, author of Vinxi.)
HatTip also has an Express style imperative routing API:
import { createRouter } from "@hattip/router";
import { json } from "@hattip/response";
const router = createRouter();
router.get("/", () => {
return json({ message: "Hello!" });
}
export default router.buildHandler();
Repo started: Feb 20, 2022 - https://github.com/hattipjs/hattip
Main contributors: 1.5
Github stars now: 1.1 k
NPM weekly downloads now: 1 299
Current state: HatTip is on version 0.0.44. (Generally, people consider version 1.0 to represent production-ready.)
Tagline: "Like Express, but for the future".
HatTip should work well with the Vite-based NextJS alternatives Vike (aka. vite-plugin-ssr) or RakkasJS since HatTip is made by @cyco130 who is behind Rakkas. He collaborates closely with @brillout who is behind Vike and contributes to HatTip. (*Updated for clarity: previously I only said "made by the same guys")
HatTip integrates with graphql-yoga.
Elysia uses chaining / a fluent interface for type inference, and gets a distinct style:
import { Elysia } from 'elysia'
new Elysia()
.get('/json', () => ({
message: 'Hello!'
}))
.listen(8080);
Repo started: Jul 3, 2022 - https://github.com/elysiajs/elysia
Main contributors: 1
Github stars now: 7.1 k
NPM weekly downloads now: 23 837
Current state: Elysia released version 1.0 on 16th of March 2024. It was the first stable release after 1.8 years in development. See the currently latest released version of Elysia here. (Generally, people consider version 1.0 to represent production-ready.)
Tagline: "Ergonomic Framework for Humans".
Elysia does some advanced static code analysis (called Sucrose) which optimizes the performance of your code (by improving the handling of functions).
Comparison:
All of the frameworks are very web standards focused: Fetch API (the current standard) & WinterCG (the future standard).
Github stars
Hono and Elysia has had a meteoric rise in GitHub stars.
H3 was extracted from Nitro/Nuxt around Jul 2, 2023 or later. So people might have heard about and starred those higher-level frameworks more than specifically H3. Which may explain the relatively lower amount of GitHub stars for H3 than the others here, even though H3 has been open sourced the longest (since Nov 15, 2020).
For the current up-to-date statistics, see: GitHub star history graph for Hono vs. H3 vs. HatTip vs. Elysia.
NPM downloads
H3 is by far the most downloaded, as seen on Moiva.io, likely because being included with Nitro and NuxtJS (the NextJS of the Vue world):
If we exclude H3 and zoom in, we see that Hono takes the lead, followed by the younger Elysia, and then HatTip (not yet on v1):
But how is their rate of growth changing? I.e. How fast do they grow?
Hono is currently growing quickest in NPM downloads month-over-month, with 26.6 % growth. Elysia has 17.9 %, quickly followed by HatTip with 17% and surprisingly the incumbent H3 at last with 15.3 % growth in NPM downloads.
For the current up-to-date statistics, see: NPM and GitHub comparison statistics on Moiva.io for Hono vs. H3 vs. HatTip vs. Elysia.
Here is a log chart that shows their percentage-wise growth compared to their current downloads. So that their relative growth is easier seen without excluding H3 from the chart due to dwarfing the others. (As a reminder: Logarithmic charts are good for showing trends, long-term perspectives, and a wider range of data compared to linear charts. The key difference from a linear scale is that a logarithmic scale shows percentage changes or orders of magnitude rather than absolute values. Equal vertical distances represent equal percentage changes, not equal absolute changes.)
Performance and benchmarks
Take all benchmarks with a pinch of salt. More than likely, all of these frameworks will be fast enough for most use cases, and it'll be your app logic that's the bottleneck. With that said, here are some benchmarks (contribute more, and I'll add them).
Web Frameworks Benchmark, from the-benchmarker's github repo. Results in request per second (RPS), and with concurrency 64:
Framework | RPS |
---|---|
Hono (Bun) | 131 177 |
H3 (Node?): | 75 422 |
Hono (Node?): | 68 263 |
Elysia (Node?): | 64 793 |
HatTip is not yet in this benchmark.
fastify-uws helloworld benchmark (using the Oha to generate the HTTP load for these frameworks):
Framework | RPS |
---|---|
Elysia (Bun) | 145 652 |
Hono (Bun) | 117 491 |
Hono (Node) | 65 704 |
H3 (Node) | 64 489 |
H3 (Bun) | 62 165 |
Denosaurs' helloworld benchmark results was excluded here. Since it didn't have results for either H3 or HatTip, and only Hono results for Deno. Makes little sense to compare that with Elysia on Bun, or with the other benchmarks which are run on other hardware. But now you know about the benchmark, and can improve it by adding these frameworks and/or running it yourself. Let me know if you do, and I can include the results here if you wish.
SaltyAom/bun-http-framework-benchmark (from ElysiaJS author).
- "exercising the same code path repeatedly like in here will show very different performance characteristics compared to a more realistic load profile" and other tweaking-sensitive issues in the benchmark, mentioned by HatTip author. Corroborated by others.
- biased benchmark when run on windows?
Results, in requests per second (RPS), on the repo's official benchmark (ran on an Intel-Core-i7-13700K CPU):
Framework | RPS |
---|---|
Elysia (Bun): | 255 574 |
Hono (Bun): | 203 937 |
H3 (Node): | 96 515 |
Hono (Node): | 29 036 |
Some other guy, running the same test on Linux, on a generally slower machine:
Framework | RPS |
---|---|
Elysia (Bun): | 86 841 |
Hono (Bun): | 73 614 |
He didn't test the other frameworks.
And another guy on Linux, on a slightly slower CPU than the official benchmark:
Framework | RPS |
---|---|
Elysia (Bun): | 199 328 |
Hono (Bun): | 196 504 |
H3 (Node): | 95 482 |
Hono (Node): | 20 781 |
Hono could allegedly be made about 20 % faster on the Bun benchmark. Hono on Node seems terribly slow. But this is not reflected in the other benchmark: Web Frameworks Benchmark.
The above tests and info indicate that Elysia and Hono are about as fast when run on Bun.
HatTip is allegedly about as fast as Hono, when both are run on Bun.
Hono uses a faster RegExpRouter instead of a Radix Tree based router such as Radix3 which H3 uses. But follow this issue to see if H3 implements the same RegExpRouter, or if it is upstreamed to radix3. But in general, it's likely that the router performance won't matter much, since it will be dwarfed by the time it takes to execute user logic.
What can be counter-intuitive is that some web frameworks that perform worse on a raw text (i.e. helloworld) response, can perform better than the others when querying a Postgres DB, likely due to specific optimisation. So the rankings can shift, depending on use case.
"If you are looking for the best performing nodejs framework, you should consider that the performance drops on all frameworks the moment you query a database. In that scenario, the difference in performance between the fastest and the slowest is reduced and other factors come into play." -- Yet another nodejs benchmark
Distinctions / focus
HatTip is more standards focused than Hono. At least when it comes to universal middlewares.
HatTip also focuses on:
- Zero-config CLI
- Zero-config library integrations
- Deep integration With Vite's development server. (Enabling a deeper zero config DX for Vite users.)
Whereas «Hono is just a web framework. That will not change in the future. No possibility of bundling CLI, at least not at this stage.«: according to it's author.
HatTip is better suited for (deeper integrated with) Vite than Hono (which requires plugins).
Other
Cutting-edge tooling like Vinxi (used by Solid Start and Tanstack Start) bets on H3 over Hono:
From Vinxi's perspective, it's way better to bet on H3 which is backed by the Nitro and UnJS teams and is used by Analog and Nuxt. So lot of people working on keep it stable, fast, bug free and working everywhere. (and its still quite a difficult task).
-- said Nikhil Saraf (Vinxi's author).
But it might also be due to other reasons, such as H3 allowing manually registering router instances (which is Vinxi's main feature).
Did I forget something important?
Please comment, and I'll try to update the article so the overview is better!
Like and share. :) Who else do you think might like it?
Top comments (8)
HatTip is used in Rakkas which works great for us 🙌
It's actually more built and maintained by the Rakkas creator @cyco130 than the vike (vite-plugin-ssr) creator, they collaborate at times tho from what I understand
yeah, that’s true!
Hono is likely a better choice for Cloudflare Workers than using Elysia.
Hono is better at supporting multiple runtimes. Elysia is very Bun focused.
I’m also a bit unsure of the Fluent API style Elysia uses… Such a style has been problematic with tree shaking before, since it uses a mega object, but tree shaking down the bundle size might not matter when you’re only using it on the backend anyway (unless you hit the 1MB limit of CF workers too quickly…).
Elysia author’s own words:
The team behind H3 implemented a faster router, replacing/renaming radix3 with the new router called rou3.
Benchmark of routers: rou3, radix3, medley, hono-regexp, hono-trie, koa-tree :
github.com/unjs/rou3/pull/107
github.com/unjs/rou3/issues/108
Some old benchmarks made by Medley:
github.com/medleyjs/router-benchmark
With some important note on Benchmark Normalization to ensure fairness:
github.com/medleyjs/router-benchma...
I've been searching for this article for a while. This is brilliant thank you.
Cool article, thanks for sharing it!
I want to do this combo, Elysia (Bun) thanks for sharing