[{"data":1,"prerenderedAt":16},["ShallowReactive",2],{"$f3qRQvL53h8vIa69CA20CqS90jEIvWYqYvH3V_dcSYkA":3},{"slug":4,"title":5,"description":6,"date":7,"tags":8,"draft":12,"readingMinutes":13,"html":14,"related":15},"hono-vs-express","Hono vs Express: Which One I'd Start a Node API With in 2026","Hono vs Express in November 2026: Hono 4.13 with zero dependencies vs Express 5.3 with 64, where each runs, what breaks when you switch, and which to pick.","2026-11-05",[9,10,11],"hono","express","nodejs",false,4,"\u003Cp>Express is in my stack and has been for years. It&#39;s the framework most Node developers learned on, it has a middleware for everything, and most existing Node backends out there are Express. Hono is the one I get asked about now, mostly by people who&#39;ve seen it in a Cloudflare Workers tutorial and want to know if they should switch.\u003C/p>\n\u003Cp>Short answer: for a new API that might run anywhere other than a plain Node server, Hono. For an existing Express app, stay on Express 5 and stop worrying. The longer answer is mostly about where your code runs and which middleware you depend on, not speed.\u003C/p>\n\u003Cp>Versions checked on November 5, 2026 on npm: \u003Cstrong>Hono 4.13.16\u003C/strong> (published November 1) and \u003Cstrong>Express 5.3.0\u003C/strong> (October 9).\u003C/p>\n\u003Ch2>What you actually install\u003C/h2>\n\u003Cp>Installing each into an empty folder with npm, on Node 24:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Ccode>express@5.3.0\u003C/code>: \u003Cstrong>65 packages\u003C/strong> in \u003Ccode>node_modules\u003C/code> (Express plus 64 dependencies, 28 of them direct), 4.4 MB.\u003C/li>\n\u003Cli>\u003Ccode>hono@4.13.16\u003C/code> plus \u003Ccode>@hono/node-server@2.1.6\u003C/code> to run it on Node: \u003Cstrong>2 packages\u003C/strong>, 4.0 MB, and neither has a single dependency.\u003C/li>\n\u003C/ul>\n\u003Cp>The size on disk is about the same, because Hono ships builds and adapters for every runtime in one package. The package count is the difference that matters. Sixty-five packages is 65 things that can show up in \u003Ccode>npm audit\u003C/code>, and Express&#39;s tree includes the usual suspects (\u003Ccode>qs\u003C/code>, \u003Ccode>debug\u003C/code>, \u003Ccode>iconv-lite\u003C/code>, \u003Ccode>path-to-regexp\u003C/code>) that have each had their turn in a security advisory. With Hono, there&#39;s nothing to audit except Hono.\u003C/p>\n\u003Cimg src=\"/blog/hono-vs-express/hono-express-dependency-trees.webp\" alt=\"Dependency trees after npm install on Node 24: Express 5.3.0 pulls in 64 packages, Hono 4.13.16 with its Node adapter is 2\" width=\"1600\" height=\"900\" loading=\"lazy\">\u003Cp>Weekly downloads, for the &quot;nobody uses it&quot; crowd: Express had about 195 million in the week to November 3, Hono about 102 million. Hono isn&#39;t niche anymore.\u003C/p>\n\u003Ch2>Where each one runs\u003C/h2>\n\u003Cp>This is the real difference. Express is built on Node&#39;s \u003Ccode>http\u003C/code> module: \u003Ccode>req\u003C/code> and \u003Ccode>res\u003C/code> are Node objects, and it runs where Node runs. Bun runs it fine too, through its Node compatibility.\u003C/p>\n\u003Cp>Hono is built on the web standard \u003Ccode>Request\u003C/code> and \u003Ccode>Response\u003C/code>. The same app runs on \u003Ca href=\"https://hono.dev/docs/getting-started/cloudflare-workers\">Cloudflare Workers\u003C/a>, Deno, Bun, Vercel and AWS Lambda, and on Node through \u003Ccode>@hono/node-server\u003C/code> (which needs Node 20 or later as of 2.x). If there&#39;s any chance your API ends up at the edge, on Workers, or split between a Node server and a serverless function, Hono saves you a rewrite later. Express on Workers is a compatibility layer, and I wouldn&#39;t put production traffic on it.\u003C/p>\n\u003Cp>If you&#39;re on a plain VPS with PM2 or systemd and you&#39;ll stay there, this advantage is worth exactly nothing.\u003C/p>\n\u003Ch2>The code, side by side\u003C/h2>\n\u003Cp>They look closer than people expect:\u003C/p>\n\u003Cpre>\u003Ccode class=\"language-js\">// Express 5\napp.get(&#39;/users/:id&#39;, async (req, res) =&gt; {\n  const user = await db.user(req.params.id)\n  if (!user) return res.status(404).json({ error: &#39;not found&#39; })\n  res.json(user)\n})\n\n// Hono\napp.get(&#39;/users/:id&#39;, async (c) =&gt; {\n  const user = await db.user(c.req.param(&#39;id&#39;))\n  if (!user) return c.json({ error: &#39;not found&#39; }, 404)\n  return c.json(user)\n})\n\u003C/code>\u003C/pre>\n\u003Cp>Hono returns a response instead of calling methods on \u003Ccode>res\u003C/code>. Small change, but it means a handler that forgets to respond is a bug you can see in the code, not a request that hangs until the client times out.\u003C/p>\n\u003Cp>Two Hono features are worth the switch on their own. Validation with \u003Ccode>@hono/zod-validator\u003C/code> types \u003Ccode>c.req.valid(&#39;json&#39;)\u003C/code> from your schema, so the body is typed without a cast. And the RPC client, \u003Ccode>hc&lt;AppType&gt;\u003C/code>, gives a frontend in the same repo a typed client for your routes without OpenAPI or codegen. For a Vue or React app talking to its own API, that alone is a reason to pick it.\u003C/p>\n\u003Ch2>What breaks if you switch an Express app\u003C/h2>\n\u003Cp>Your middleware. Anything written against Node&#39;s \u003Ccode>req\u003C/code>/\u003Ccode>res\u003C/code> (\u003Ccode>passport\u003C/code>, \u003Ccode>multer\u003C/code>, \u003Ccode>express-session\u003C/code>, and most of the custom auth code in a real app) doesn&#39;t run in Hono. Hono has its own versions of the common ones (CORS, JWT, secure headers, body limits, cookies) and the rest you rewrite.\u003C/p>\n\u003Cp>That&#39;s why I wouldn&#39;t migrate a working Express app. A 40-route API with Passport strategies, file uploads through Multer and session auth is weeks of rewriting for a framework that does the same job. Upgrade it to Express 5 instead.\u003C/p>\n\u003Ch2>Express 5 fixed the two things everyone hated\u003C/h2>\n\u003Cp>If your mental picture of Express is version 4, it&#39;s out of date. Express 5 has been the \u003Ccode>latest\u003C/code> on npm since March 2025, and the \u003Ca href=\"https://expressjs.com/en/guide/migrating-5.html\">migration guide\u003C/a> covers it. The changes that matter day to day:\u003C/p>\n\u003Cul>\n\u003Cli>A rejected promise in an async handler now goes to your error middleware. In Express 4 it was an unhandled rejection, which is why every codebase had an \u003Ccode>asyncHandler\u003C/code> wrapper or \u003Ccode>express-async-errors\u003C/code>. Delete them.\u003C/li>\n\u003Cli>Route syntax changed with \u003Ccode>path-to-regexp\u003C/code> 8. A bare \u003Ccode>*\u003C/code> no longer works, wildcards need a name (\u003Ccode>/*splat\u003C/code>), and optional params are written \u003Ccode>{/:id}\u003C/code>. This is the one that breaks upgrades, usually in a catch-all route someone added for a SPA years ago. It fails at startup, which is the good kind of failure.\u003C/li>\n\u003C/ul>\n\u003Cp>Express 5 needs Node 18 or later.\u003C/p>\n\u003Ch2>So which one\u003C/h2>\n\u003Cp>New API, TypeScript, any chance of Workers, Bun or serverless: \u003Cstrong>Hono\u003C/strong>. You get types end to end, no dependency tree, and the option to move runtimes without touching the handlers.\u003C/p>\n\u003Cp>New API that&#39;s plain JavaScript on a Node server, and the team knows Express: \u003Cstrong>Express 5\u003C/strong> is fine. It&#39;s boring, documented to death, and every middleware you&#39;ll ever need exists.\u003C/p>\n\u003Cp>Existing Express app: \u003Cstrong>upgrade to 5, don&#39;t rewrite.\u003C/strong> Spend the time on tests.\u003C/p>\n\u003Cp>Speed comes up every time and I&#39;d leave it out of the decision. Yes, Hono wins the hello-world benchmarks. Your API spends its time waiting for Postgres, not routing, and neither framework is going to be the slow part.\u003C/p>\n\u003Cp>If you&#39;re deciding this for a real product, or you have an Express 4 app that needs upgrading without downtime, \u003Ca href=\"https://calendly.com/director-norsky/meeting\">book a free call\u003C/a>. I do \u003Ca href=\"/services/nodejs-development\">Node.js backend work\u003C/a> on both.\u003C/p>\n",[],1794181974982]