</>
  • Home
  • About
  • Skills
  • Experience
  • Case Studies
  • Blog
  • Projects
madrimov.uz

Keling, jiddiy
tizim quramiz

madrimov5014@gmail.com→
</>© 2026 Madrimov Xudoshukur
GitHubTelegramEmail
← Blog
June 16, 2026

From NestJS + Prisma to Bun + Hono + Drizzle: why I switched

BackendArxitekturaBunHonoNestJSDrizzlePrisma

For a long time, Node.js + NestJS + Prisma was my go-to stack. With this combination you can move fast — everything is in place: a DI container, modules, decorators, guards, pipes, plus a type-safe ORM. But in one of my recent projects, that very feeling of "everything is in place" became the obstacle. In this article I'll explain why I switched to Bun + Hono + Drizzle, which problems pushed me there, and what the difference looks like in numbers.

NestJS's strength is also its weakness

NestJS is a full framework. Its structure is predefined, and a lot comes out of the box. You only need to think about business logic — the framework handles the rest. Early on, this is very convenient.

The problem is that "the framework handles the rest" means you don't know what's happening under the hood. Many developers get used to this automatically: drop in a decorator, it works, move on. In small and mid-size projects this is no issue. But once a project grows and you need a non-standard architecture for performance, that uncertainty turns into direct pain.

The real problem in the MNazorat project

I built the MNazorat project on NestJS. To boost performance, we built it as multi-core (a separate instance per core). That's where the biggest difficulty started — especially with an event-driven architecture.

The core of the problem: in multi-core mode, Node.js creates a separate event emitter for each NestJS instance. So an event emitted by one instance doesn't reach the others. As a result, some events get lost. Code that worked perfectly in a single process quietly starts breaking when moved to multi-core.

To prevent this, you're forced to write extra, fundamentally unnecessary logic: synchronizing events across instances, re-sending lost ones, and so on. And that means extra resources and extra time.

True, there are other solutions. For example, Redis-based drivers — like BullMQ. BullMQ is primarily designed for queues and is excellent at that. But when needed, you can freely use it for cron jobs and for event-driven architecture too. Instead of an in-memory event emitter, you get a centralized Redis backbone — and events don't get lost.

But here the main question arises: what is all this headache for?

It's all for performance

The answer is simple — all these solutions exist so the server runs faster and handles more requests. In a word: for performance.

And that's exactly where the question hit me: if I'm spending this much effort, building extra infrastructure, and just chasing performance — maybe the problem isn't the architecture, but the foundation itself? What if Node.js + NestJS + Prisma simply struggles to deliver that performance?

Bun + Hono: it does a lot under the hood for you

When I switched to Bun + Hono, the first thing I noticed was this: performance and request-handling speed — compared to Node.js + NestJS — are faster and more convenient out of the box, under the hood. Much of what I previously had to assemble by hand is solved here at the runtime level.

Bun is built on the JavaScriptCore engine (not V8), and the Bun.serve HTTP server is fundamentally faster than Node.js's node:http module. Hono, in turn, is a minimalist, runtime-agnostic router. Together they're one of the fastest combinations.

Hono's drawback: it's not a framework

Let's be honest. For a developer coming from NestJS, the most inconvenient thing about Hono is that it's not a framework, it's a plain library. So if you expect a ready-made structure like in NestJS, you won't find it. You'll have to write a lot by hand: folder structure, DI, module organization — all of that is left to you.

But there's a flip side to that coin: Hono looks like classic Express-like code. For older developers who worked with Express, this poses no difficulty — on the contrary, it's familiar and transparent. You know exactly where each middleware sits and which path a request takes. No "magic."

Hono's strengths

I didn't choose Bun + Hono just for speed. There are several practical things:

  • Native validation. Hono has direct integrations like @hono/zod-validator. The request body, query, and params are all validated against a Zod schema and passed to the handler type-safely. In NestJS this required a pipe + DTO + class-validator chain.
  • RPC and type safety. Through Hono's RPC mode, automatic types for the client are generated from the server routes. The contract between frontend and backend comes from a single source.
  • Built on web standards. Hono runs on the Web API Request/Response. That's why it works everywhere — Bun, Deno, Cloudflare Workers, Node.js. One codebase, many runtimes.
  • Bun's all-in-one nature. Test runner, bundler, package manager, .env reading, running TypeScript directly — all out of the box. No ts-node, no nodemon, no separate bundler needed.

The ORM question: Prisma vs Drizzle

When people talk about the stack, the part most often overlooked — but with the biggest real impact — is the ORM. Because in a real project, the bottleneck is usually not in the HTTP layer, but precisely in the database access layer.

Prisma: convenient, but heavy

I used to love Prisma — a clear schema, convenient migrations, excellent type safety, Prisma Studio as a bonus. From a developer-experience standpoint, it's still one of the best.

But Prisma's historical drawback was its heavy architecture. Until recently, Prisma executed queries through a separate Rust query engine. That is, outside your Node process, another binary was running: it added extra latency, bloated the bundle size, and noticeably slowed cold starts in serverless (Lambda) environments. That very engine was the hidden bottleneck in many projects.

For fairness: this changed in late 2025. Prisma 7 (November 19, 2025) dropped the Rust engine entirely and moved to a pure TypeScript client — the bundle shrank by nearly 90% (around 1.6 MB), and query latency improved roughly 3x compared to the previous version. So the biggest criticism is now partly history. But the Prisma I worked with was exactly that heavy-engine version.

Drizzle: no extra layers

Drizzle is built on a completely different philosophy. It uses no query engine — it generates a SQL string directly from the code you write, and that's it. There's no extra runtime in between, no binary. As a result:

  • Less raw overhead. The ORM layer is minimal, so the extra time added per query is practically zero.
  • Faster cold start. No engine to load — in serverless, Drizzle spins up noticeably faster than Prisma.
  • Close to SQL. Drizzle is "SQL-like" — if you know SQL, you write almost the same SQL, just type-safe. For complex joins it generates a single optimized SQL statement and avoids the N+1 problem.
  • Drawback: that very "closeness to the metal" is harder for a beginner. Instead of the simple, declarative DX of Prisma, you need to understand the SQL logic yourself. The migration tooling is also not as mature as Prisma's (it's improving, but a gap remains).

An honest ORM verdict

In my case, Drizzle was the right choice: no extra engine, fast cold start, full control over SQL. It fit perfectly with the Bun + Hono philosophy of "transparency and minimal magic."

But let me be honest: after Prisma 7, this gap isn't as large as it used to be. For complex joins and serverless cold starts, Drizzle is still ahead, but for simple queries the difference between them is now a few milliseconds. And don't forget — in most applications, the database round-trip (usually 5–50 ms) dominates the total time anyway, regardless of the ORM. Choose your ORM not only by speed, but by control and the complexity of your project.

Comparison in numbers

Now the interesting part — the numbers. The following come from public benchmarks (synthetic, at the "hello world" HTTP-test level):

  • `node:http` vs `Bun.serve` (Bun 1.0 announcement): Node around 65,000 req/s, Bun around 183,000 req/s — roughly 2.8x.
  • A more recent Linux x64 measurement (Node v23.6 vs Bun v1.2): Node around 19,000 req/s, Bun around 59,000 req/s — roughly 3x.
  • Hono: Node vs Bun (framework benchmark): Node around 27,000 req/s, Bun around 64,000 req/s.

At the framework level: Hono handles roughly 3–4x more requests than Express and uses nearly 40% less memory. And Bun.serve is the fastest server in the Node ecosystem — 2.8x faster than Fastify 5 and 4.7x faster than Express 5.

On the ORM side: for complex joins, Drizzle can show up to 14x lower latency compared to ORMs that fall into the N+1 problem. On cold start: Drizzle takes 20–50 ms to connect and 50–100 ms to the first query; Prisma 7 takes 40–80 ms to load the engine and 80–150 ms to the first query. So Prisma is now in the same league as Drizzle, but still behind.

An honest warning: in a real project the gap shrinks

Don't get too attached to these numbers. A synthetic benchmark and a real project are two different things. In a pure Express-style HTTP test, Bun delivered around 52,000 req/s and Node around 13,000 req/s (roughly a 4x difference). But in a real URL-shortener project with routing + validation + a database, the difference was Bun 12,400 req/s vs Node 12,000 req/s — that is, it dropped to less than 3%.

The reason is simple: in a real project the bottleneck is usually not the runtime, but the database, network, and I/O. Even if the Bun runtime is fast, if a request is waiting for a DB response, most of that speed stays invisible. That's exactly why the ORM choice (Prisma's heavy engine vs Drizzle's lightness) is often more important than the runtime choice.

Conclusion

I don't mean to say NestJS or Prisma are bad — both are great tools, especially when you need to impose structure on a large team. But in my case — a project that needed performance and a non-standard architecture — these abstractions became an obstacle instead of help. I needed to know and control what was happening under the hood.

Bun + Hono + Drizzle gave me exactly that: transparency, speed, and less magic. The synthetic numbers are in my favor too, but the main thing for me isn't the numbers — it's control. Now I know exactly where and how every middleware, every route, and every SQL query works. And if I write extra logic, it's for my own business, not to work around a framework's limitations.

If you're comfortable working on NestJS + Prisma, don't rush to switch — especially after Prisma 7. But if you too are fighting for performance and pushing back against abstractions, Bun + Hono + Drizzle is worth a look.