</>
  • Главная
  • Обо мне
  • Навыки
  • Опыт
  • Кейсы
  • Блог
  • Проекты
madrimov.uz

Keling, jiddiy
tizim quramiz

madrimov5014@gmail.com→
</>© 2026 Madrimov Xudoshukur
GitHubTelegramEmail
← Блог
16-июня, 2026

От NestJS + Prisma к Bun + Hono + Drizzle: почему я перешёл

BackendArxitekturaBunHonoNestJSDrizzlePrisma

Долгое время Node.js + NestJS + Prisma были моим основным стеком. С этой комбинацией можно работать быстро — всё на месте: DI-контейнер, модули, декораторы, guard'ы, pipe'ы, плюс типобезопасный ORM. Но в одном из последних проектов это ощущение «всё на месте» само превратилось в препятствие. В этой статье я расскажу, почему перешёл на Bun + Hono + Drizzle, какие проблемы меня к этому подтолкнули и какова разница в цифрах.

Сильная сторона NestJS — она же и слабая

NestJS — это полноценный фреймворк. Структура задана заранее, многое идёт из коробки. Тебе нужно думать только о бизнес-логике — остальное берёт на себя фреймворк. На старте это очень удобно.

Проблема в том, что «остальное берёт на себя фреймворк» означает — ты не знаешь, что происходит под капотом. Многие разработчики привыкают к этому автоматически: поставил декоратор, работает, идёшь дальше. В маленьких и средних проектах это не проблема. Но когда проект растёт и для производительности нужна нестандартная архитектура — эта неопределённость превращается в прямую боль.

Реальная проблема в проекте MNazorat

Проект MNazorat я построил на NestJS. Ради производительности мы собрали его как multi-core (отдельный инстанс на каждое ядро). Вот тут и начались главные трудности — особенно с event-driven архитектурой.

Суть проблемы: в multi-core режиме Node.js создаёт отдельный event emitter для каждого инстанса NestJS. То есть event, выпущенный одним инстансом, не доходит до других. В результате часть событий теряется. Код, который идеально работал в одном процессе, при переходе на multi-core начинает тихо ломаться.

Чтобы это предотвратить, приходится писать дополнительную, по сути ненужную логику: синхронизировать события между инстансами, заново отправлять потерянные и так далее. А это дополнительные ресурсы и дополнительное время.

Да, есть и другие решения. Например, драйверы на основе Redis — такие как BullMQ. BullMQ изначально предназначен для очередей и в этой задаче работает отлично. Но при необходимости его можно спокойно использовать и для cron job, и для event-driven архитектуры. Вместо in-memory event emitter — централизованный Redis-бэкбон, и события не теряются.

Но здесь встаёт главный вопрос: ради чего вся эта головная боль?

Всё ради производительности

Ответ прост — все эти решения нужны, чтобы сервер работал быстрее и обрабатывал больше запросов. Одним словом: ради производительности.

И именно тут у меня возник вопрос: если я трачу столько сил, строю дополнительную инфраструктуру и просто гонюсь за производительностью — может, проблема не в архитектуре, а в самом фундаменте? Что если сам Node.js + NestJS + Prisma и не вытягивает эту производительность?

Bun + Hono: многое делает под капотом сам

Когда я перешёл на Bun + Hono, первое, что я заметил: производительность и скорость обработки запросов — по сравнению с Node.js + NestJS — сами по себе под капотом быстрее и удобнее. Многое из того, что раньше приходилось собирать вручную, здесь решено на уровне рантайма.

Bun построен на движке JavaScriptCore (не V8), а HTTP-сервер Bun.serve в корне быстрее модуля node:http. Hono же — минималистичный, runtime-agnostic роутер. Вместе они — одна из самых быстрых комбинаций.

Недостаток Hono: это не фреймворк

Будем честны. Для разработчика, пришедшего из NestJS, самое неудобное в Hono — это то, что это не фреймворк, а обычная библиотека. То есть готовой структуры, как в NestJS, ты не найдёшь. Многое придётся писать вручную: структуру папок, DI, организацию модулей — всё это отдано тебе.

Но у медали есть и вторая сторона: Hono выглядит как классический Express-like. Для старых разработчиков, работавших с Express, это не вызывает никаких трудностей — наоборот, знакомо и прозрачно. Ты точно знаешь, где стоит каждый middleware и каким путём проходит запрос. Никакой «магии».

Сильные стороны Hono

Я выбрал Bun + Hono не просто за скорость. Есть ряд практичных вещей:

  • Нативная валидация. В Hono есть прямая интеграция вроде @hono/zod-validator. Тело запроса, query, param — всё проверяется Zod-схемой и передаётся в handler типобезопасно (type-safe). В NestJS для этого нужна была цепочка pipe + DTO + class-validator.
  • RPC и типобезопасность. Через RPC-режим Hono из серверных роутов генерируются автоматические типы для клиента. Контракт между фронтендом и бэкендом идёт из одного источника.
  • Основан на веб-стандартах. Hono работает на Web API Request/Response. Поэтому он работает везде — Bun, Deno, Cloudflare Workers, Node.js. Один код, много рантаймов.
  • All-in-one природа Bun. Test runner, бандлер, пакетный менеджер, чтение .env, прямой запуск TypeScript — всё из коробки. ts-node, nodemon, отдельный бандлер не нужны.

Вопрос ORM: Prisma vs Drizzle

Когда говорят о стеке, чаще всего без внимания остаётся, но на деле сильнее всего влияет — ORM. Потому что в реальном проекте узкое место чаще не в HTTP-слое, а именно в слое работы с базой данных.

Prisma: удобно, но тяжело

Я любил Prisma — понятная схема, удобные миграции, отличная типобезопасность, Prisma Studio в бонус. С точки зрения developer experience он до сих пор один из лучших.

Но исторический недостаток Prisma — его тяжёлая архитектура. До недавнего времени Prisma выполняла запросы через отдельный Rust query engine. То есть вне твоего Node-процесса работал ещё один бинарник: он добавлял лишнюю задержку, раздувал размер бандла и заметно замедлял cold start в serverless-среде (Lambda). Именно этот движок во многих проектах был скрытым узким местом.

Ради честности: в конце 2025 года это изменилось. Prisma 7 (19 ноября 2025) полностью отказалась от Rust-движка и перешла на чистый TypeScript-клиент — бандл уменьшился почти на 90% (около 1.6 МБ), задержка запросов улучшилась примерно в 3 раза по сравнению с предыдущей версией. То есть главная критика теперь отчасти стала историей. Но та Prisma, с которой работал я, была именно той версией с тяжёлым движком.

Drizzle: без лишних слоёв

Drizzle построен на совершенно другой философии. Он не использует никакой query engine — генерирует SQL-строку прямо из написанного тобой кода, и всё. Посередине нет никакого дополнительного рантайма, никакого бинарника. В результате:

  • Меньше raw overhead. Слой ORM минимален, поэтому лишнее время на каждый запрос практически нулевое.
  • Быстрее cold start. Движок грузить не нужно — в serverless Drizzle поднимается заметно быстрее Prisma.
  • Близко к SQL. Drizzle «SQL-like» — если знаешь SQL, пишешь почти тот же SQL, только типобезопасно. В сложных join'ах он генерирует один оптимизированный SQL-стейтмент и не попадает в проблему N+1.
  • Недостаток: именно эта «близость к рукам» сложнее для новичка. Вместо простого декларативного DX как в Prisma тебе нужно самому понимать логику SQL. Тулинг миграций тоже не так зрел, как у Prisma (улучшается, но разрыв пока есть).

Честный вывод по ORM

В моём случае Drizzle оказался верным выбором: нет лишнего движка, быстрый cold start, полный контроль над SQL. Это идеально легло на философию Bun + Hono — «прозрачность и минимум магии».

Но буду честен: после Prisma 7 эта разница уже не так велика, как раньше. В сложных join'ах и serverless cold start Drizzle по-прежнему впереди, но в простых запросах разница между ними теперь — несколько миллисекунд. И не забывайте — в большинстве приложений round-trip к базе данных (обычно 5–50 мс) всё равно занимает основное время, независимо от того, какой ORM. Выбирайте ORM не только по скорости, но и по контролю и сложности вашего проекта.

Сравнение в цифрах

Теперь самое интересное — цифры. Ниже данные из открытых бенчмарков (синтетических, уровня HTTP-теста «hello world»):

  • `node:http` vs `Bun.serve` (анонс Bun 1.0): Node около 65 000 req/s, Bun около 183 000 req/s — примерно 2.8x.
  • Более свежий замер на Linux x64 (Node v23.6 vs Bun v1.2): Node около 19 000 req/s, Bun около 59 000 req/s — примерно 3x.
  • Hono: Node vs Bun (framework benchmark): Node около 27 000 req/s, Bun около 64 000 req/s.

На уровне фреймворка: Hono обрабатывает примерно в 3–4 раза больше запросов, чем Express, и использует почти на 40% меньше памяти. А Bun.serve — самый быстрый сервер в экосистеме Node: в 2.8x быстрее Fastify 5 и в 4.7x быстрее Express 5.

Со стороны ORM: в сложных join'ах Drizzle может показывать до 14x меньшую задержку по сравнению с ORM, попавшими в проблему N+1. По cold start: Drizzle — 20–50 мс на подключение, 50–100 мс на первый запрос; Prisma 7 — 40–80 мс на загрузку движка, 80–150 мс на первый запрос. То есть Prisma теперь в одной лиге с Drizzle, но всё же позади.

Честное предупреждение: в реальном проекте разница уменьшается

Не привязывайтесь к этим цифрам слишком сильно. Синтетический бенчмарк и реальный проект — две разные вещи. В чистом HTTP-тесте в стиле Express Bun выдавал около 52 000 req/s, Node около 13 000 req/s (разница примерно 4x). Но в реальном проекте URL-шортенера с роутингом + валидацией + базой данных разница составила Bun 12 400 req/s против Node 12 000 req/s — то есть упала до менее 3%.

Причина проста: в реальном проекте узкое место чаще не рантайм, а база данных, сеть, I/O. Даже если рантайм Bun быстрый, но запрос ждёт ответа от БД — большая часть этой скорости остаётся невидимой. Именно поэтому выбор ORM (тяжёлый движок Prisma vs лёгкость Drizzle) часто важнее выбора рантайма.

Вывод

Я не хочу сказать, что NestJS или Prisma плохи — оба отличные инструменты, особенно когда нужно навязать структуру большой команде. Но в моём случае — в проекте, где нужна была производительность и нестандартная архитектура — эти абстракции вместо помощи превратились в препятствие. Мне нужно было знать и контролировать, что происходит под капотом.

Bun + Hono + Drizzle дали мне именно это: прозрачность, скорость и меньше магии. Синтетические цифры тоже позади, но главное для меня не цифры — а контроль. Теперь я точно знаю, где и как работает каждый middleware, каждый роут, каждый SQL-запрос. И если я пишу дополнительную логику — это ради своего бизнеса, а не ради обхода ограничений фреймворка.

Если вам удобно работать на NestJS + Prisma — не спешите переходить, особенно после Prisma 7. Но если вы тоже боретесь за производительность и сопротивляетесь абстракциям — на Bun + Hono + Drizzle стоит взглянуть.