NestJS is bad: чому фреймворк критикують і чи актуальний він у 2025?

Побачила на Reddit цікаву дискусію під назвою «NestJS is bad, change my mind», де автор досить різко висловився про фреймворк:

Спільнота у коментарях переважно стала на захист NestJS. Один з найпопулярніших коментарів наголошує, що концепції Dependency Injection та IoC насправді є стандартними патернами. І якщо вони викликають труднощі у команди, то це питання компетенції та знання архітектури, а не вада самого фреймворку. А ще нагадують, що NestJS — це зрілий продукт із десятирічною історією, тому те, що виглядає як «зайва складність» чи бюрократія в коді, часто є необхідною платою за стабільність.
Хоча є і думка, що тягнути NestJS у прості проєкти часто немає сенсу, проте для складних систем його модульність стає тим фундаментом, який рятує від хаосу в коді.

А як ви ставитесь до NestJS і його ролі у сучасному бекенд-стеку?
Чи використовуєте його у своїх проєктах і наскільки він виправдовує себе в реальній роботі?

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Nestjs — кал гімна пса.
Багато з ним працював, кожен раз люто ненавидів.
Згідно моїх спостережень розробка і підтримка з застосуванням nestjs коштує бізнесу дорожче ніж не юзати взагалі ніяких фреймворків.
Більше всього бісила incompatibility цього фреймворків з багатьма стандартними, простими тулзами|підходами і тонни бойлералейт гімна

Чудовий фреймворк. Але дійсно для середніх проектів краще наприклад Fastify взяти бо де пан Матео Коліна — там якість.

Щодо Матео Коліни і якості погоджуюся на всі 100%. На рахунок використання чистого Fastify це також має місце на середніх проєктах, якщо знаєш що робиш, але доведеться писати купу додаткового коду, який вже є з коробки у NestJS. До того ж, плюсом NestJS є те, що він диктує адеватну базову структуру проєкту, з мого досвіду, коли люди намагаються не брати NestJS а беруть якусь меншу лібу, то вони забувають про цю базу і проєкт виглядає як купа болота. Ну і в NestJS є можливість використовувати Fastify під капотом, хоч там буде дещо менший перформанс, ніж у чистого Fastify

Для 90% випадків:
Bun + Hono + Typescript.
Воно швидше, однаково безпечне та читаєме.

NestJS для важчіх випадків, і в основному на гігантських проектах. Але якщо брати круди, або апку з малим функціоналом, я не знаю нащо брати цього прожорливого та неповоротливого слона

Але якщо брати круди, або апку з малим функціоналом, я не знаю нащо брати цього прожорливого та неповоротливого слона

Повністю з вами тут погоджуюся, але подібного роду апки це аж ніяк не 90% випадків, це лише тестові, навчальні, або супер базові проєкти. Якщо ми говоримо про щось більш серйозне, то на Hono, чи скажімо Fastify звісно можна їхати, але тут добре треба розуміти що ми робимо і як це розумно зробити, з мого особистого досвіду, не всі це вміють і знають, але очі зробити щось простіше горять) і на ділі виходять апки в яких складно орієнтуватися, розширювати і т.д., хотілося простіше, а вийшло значно складніше. Тому навіщо придумувати велосипеди, якщо вони вже всі є в NestJS з коробки?
Щодо пункту «швидше» я не до кінця погоджуюся, Node.js можна зробити наближеною по швидкості до Bun, просто це йде не з коробки і потребує деяких додаткових знань

Знання, освіта, час, фінанси, кадри і малі ризики — так. Це коли мова йде про середній та великий бізнес.

А якщо ми говоримо про — приватну особу, стартап, неприбудкова організація та малий бізнес?
З рештою все одно можна переписати з Hono або Addonis або які тут ще ліби згадувалися на іншу.

Окей, тут я і справді трішки поторопився, бо дивлюся з власної перспективи, як людина яка з нульовим ресурсом, спробує піднятися якщо придумає щось круте.

А якщо ми говоримо про — приватну особу, стартап, неприбудкова організація та малий бізнес?

Погоджуюся зі всім, окрім стартапу, стартап це така штука, що не ясно куди він може вирости, як і будь який проєкт насправді, тому я вважаю, що краще одразу закласти фундамент для чогось складнішого, ніж потім займатися переписуванням проєкту

Зазвичай з тієї критики NestJS, що бачу є це «ой Боо, як складно, я візму Express.js (підставте будь-яку мінімалістичну лібу) і напишу TODO апку в якої буде менше коду, АБСОЛЮТНА ПЕРЕМОГА». Це такий крінж, що я не можу, до людей не доходить банальний факт, що комплексний застосунок потребує комплексного підходу в його реалізації, тому краще обрати гарний фреймворк з чудовим функціоналом з коробки і чудовою можливістю до розширення, тобто NestJS, ніж die trying написати цей фреймворк самому.
У NestJS звісно є деякі мінуси, у вигляді того самого DI, який як мені здається можна було б зробити трохи зручнішим, але це не критично.
Там бачу на скріншоті про webpack у production dependencies, що власне є неправдою, видно що автора поста з Reddit прокачений факт чекінг))

тому краще обрати гарний фреймворк з чудовим функціоналом з коробки і чудовою можливістю до розширення, тобто NestJS, ніж die trying написати цей фреймворк самому.

Інколи краще таки написати самому, ніж чекати впровадження фіч від такого чувака, як автор NestJS, який блокує майже кожне Issue на github. Не рідко це відбувається, коли люди висловлюють цілком адекватні прохання. Відкривали його github issues? Намагались з ним подискутувати? Закриє вам рота аж бігом за найменшої нагоди, навіть якщо ви з ним не знайомі. Перевірив це на власному досвіді.

Сам NestJS побудований так, що навіть елементарне впровадження підтримки нового HTTP-методу QUERY потребує зміни 6 окремих файлів, не враховуючи тестів. Уявляєте? Для такої елементарної речі, треба зміни в шести файлах! Причому це не унікальні проблеми інтеграції.

Архітектура NestJS так жахливо спроектована, що його базові пакети core + common треба підлаштовувати ледве не під кожний пакет, хоча б середньої складності, типу підтримки OpenAPI (він же Swagger) чи підтримки Sentry.

До поки ви використовуєте готові нативні пакети і на основі них будуєте нескладні проекти, доки вас він ще може влаштовувати. Як тільки щось треба складне, або якась інтеграція зі сторонніми складними сервісами, прийдеться писати купу костилів...

Краще вже вибрати якісь інші, добре продумані фреймворки.

Не з всіма наведеними тезами погоджуся.

Щодо вашого досвіду знахідок в тестах, я б не сказав, що це якась неадекватна відповідь, це OpenSource, при чому це великий проєкт, правильніше було б створити PR який ці зміни фіксить, ніж «я от знайшов неправильно написані тексти, пофіксіть самі». Погоджуюся, що у Каміля є досить різка політика із швидким закриванням issues, але зазвичай таке відбувається з чимось низькопріоритетним, як ваша знахідка власне, на мою думку.

Щодо Query, ну а що тут такого у цьому конкретному? Цікаво було б глянути як би ви організували код вашого фреймворку, щоб зміною одного файлу можна було проапдейтити і декоратор і інтерфейс і імплементацію інтерфейсу і енам.
Яка ваша пропозиція, запхати все в один файл? Чи зробити через якийсь нетипізований динамічний код? Урізати функціонал, щоб прибрати декоратори, модульність (інтерфейси) і т.д.?

Про підлаштування core+common взагалі не зрозумів про що ви.
Для OpenAPI є офіційний модуль + Swagger CLI Plugin.
Sentry підключається одним рядком коду як у будь якому іншому Node.js застосунку.

Використовую багато самописного коду не з екосистеми NestJS (логер, авторизація, кешування, HTTP Client, інтеграція з БД, Third Party інтеграції), ніколи не доводилося писати костилів, навпаки модульність NestJS дозволяла то все гарно інтегрувати, до того ж гарно перевикористовується між проєктами.

Які на вашу думку фреймворки продумані краще і які вони переваги дають?

Щодо вашого досвіду знахідок в тестах, я б не сказав, що це якась неадекватна відповідь, це OpenSource, при чому це великий проєкт, правильніше було б створити PR який ці зміни фіксить, ніж «я от знайшов неправильно написані тексти, пофіксіть самі».
..низькопріоритетним, як ваша знахідка власне

Низькопріоритетний баг? Ви серйозно? Зверніть увагу, що це стосується нативного NestJS модуля безпеки, тести для якого тоді були написані просто жахливо, і Каміль їх пропустив, та ще й переводив стрілки «то не я написав код»... Ще зверніть увагу, що він не просто запропонував, щоб я сам виправити цей баг, він закрив issue і залочив його. Це прямо капець який поганий тон, коли контрибутор тобі повідомляє про купу помилок у модулі безпеки, а ти закриваєш issue та ще й лочиш його...

Щодо Query, ну а що тут такого у цьому конкретному? Цікаво було б глянути як би ви організували код вашого фреймворку, щоб зміною одного файлу можна було проапдейтити і декоратор і інтерфейс і імплементацію інтерфейсу і енам.

Ось вся повністю імплементація підтримки в моєму фреймворку: github.com/...​ditsmod/commit/f47a7381e8

Про підлаштування core+common взагалі не зрозумів про що ви.
Для OpenAPI є офіційний модуль + Swagger CLI Plugin.
Sentry підключається одним рядком коду як у будь якому іншому Node.js застосунку.

Дійсно не зрозуміли. Я ж сказав, що це стосується випадків, коли вам не достатньо тих NestJS-нативних пакетів, які написав сам Каміль. Тобто я казав про сторонніх розробників модулів, які хочуть публікувати свої пакети на npmjs як бібліотеки.

Каміль то якраз і підлаштовує core + common під те, що він пише. А сторонні розробники знаєте що роблять? Вдаються до костилів, зокрема до так званого «monkey patching». Наприклад, згадану підтримку Sentry для NestJS нагівнокодили самі розробники Sentry з використанням саме «monkey patching», бо інших можливостей не надає фреймворк. Нагадаю, що «monkey patching» — це антипатерн.

модульність NestJS дозволяла то все гарно інтегрувати

Така крута модульність, яка не дозволяє оголошувати на рівні модуля ні гардів, ні пайпів, ні інтерсепторів, ні фільтрів. У NestJS усе або глобальне, або локальне. Якщо сторонні розробники викладають якусь бібліотеку на npmjs, і вам треба з неї перераховані «підсилювачі», то не достатньо просто модуль імпортувати, вам треба заново оголошувати ці підсилювачі у своєму модулі. Це саме справжнє порушення інкапсуляції модуля.

Модульність у NestJS є, але вона досить слабка. Це стосується і вкладених маршрутів, і згаданих підсилювачів (гарди, пайпи, інтерсептори, фільтри), і DI-провайдерів.

Низькопріоритетний баг? Ви серйозно? Зверніть увагу, що це стосується нативного NestJS модуля безпеки, тести для якого тоді були написані просто жахливо, і Каміль їх пропустив, та ще й переводив стрілки «то не я написав код»...

По-перше це не баг, а погано написані тести, чи я чогось не зрозумів? По-друге добре, переконали, дійсно виглядає не тактовно зі сторони Каміля.

Ось вся повністю імплементація підтримки в моєму фреймворку: github.com/...ditsmod/commit/f47a7381e8

Переглянув код вашого фреймворку, ви собі можете таке дозволити бо у вас скромніший функціонал, а точніше є лише один спосіб додати роут, на скільки бачу, а у NestJS їх 2: через декоратор і через адаптер напряму. Якби ви пішли шляхом фреймворку з адаптерами навколо Third Party бібліотек типу Fastify, Express чи ще чогось, що дає велечезну перевагу у вигляді сумісністю з екосистемою бібліотеки навколо якої адаптер, у вас би було абсолютно те саме.

Дійсно не зрозуміли. Я ж сказав, що це стосується випадків, коли вам не достатньо тих NestJS-нативних пакетів, які написав сам Каміль. Тобто я казав про сторонніх розробників модулів, які хочуть публікувати свої пакети на npmjs як бібліотеки.

Я не писав це саме як бібліотеки що десь паблішаться, але писав багато окремих модулів і ніколи не мав ніяких проблем, знову ж таки використовую багато таких ненативних модулів, вище перелічував. Щодо Sentry і манкі патчингу, чесно не знаю що саме там у нутрощах того плагіну, що довелося аж манкі патчити щось, я у своїх проєктах підключав просто через Sentry.init() з @sentry/node всі метрики збиралися як треба, тому навіть не знаю що дає плагін під конкретно NestJS, якщо чесно.

Така крута модульність, яка не дозволяє оголошувати на рівні модуля ні гардів, ні пайпів, ні інтерсепторів, ні фільтрів. У NestJS усе або глобальне, або локальне.

Погоджуюся, що краще таку фічу мати, ніж не мати, але разом з тим особисто на своїй практиці не зустрічав коли це було б потрібне, звісно у когось іншого може бути по іншому.

Якщо сторонні розробники викладають якусь бібліотеку на npmjs, і вам треба з неї перераховані «підсилювачі», то не достатньо просто модуль імпортувати, вам треба заново оголошувати ці підсилювачі у своєму модулі. Це саме справжнє порушення інкапсуляції модуля.

Отут не зрозумів про що ви, можете, будь ласка, навести конкретніші приклади?

Отут не зрозумів про що ви, можете, будь ласка, навести конкретніші приклади?

Я ж лінк дав. Там усе описано саме про це.

Лінк на що? Що не можна гарди, інтерсептори і т.д. робити на рівні лише одного модуля? , Не розумію як відсутність можливості оголошення вище вказаних «хуків» впливає на можливість написання системного коду, коли вказані «хуки» відносяться до прикладного коду, тобто того за що ліба не повинна відповідати. Тому не бачу релевантного посилання щодо озвучених тез

Щодо Sentry і манкі патчингу, чесно не знаю що саме там у нутрощах того плагіну, що довелося аж манкі патчити щось

Винні не розробники Sentry, які це роблять, а розробник фреймворку, який не дає відповідного API для цього.

Так я не кажу, що розробники Senty винні в чомусь, я сказав, що не знаю якого саме функціоналу вони там намагалися добитися коли універасальна інтеграція з Node.js чудово працює

у вас скромніший функціонал, а точніше є лише один спосіб додати роут, на скільки бачу, а у NestJS їх 2

@ditsmod/core взагалі не знає щось про роути. Це означає, що імплементацій декоратора для роутів можна написати скільки завгодно, і всі вони будуть працювати як нативні, бо є відповідне API для цього. Якщо говорити за @ditsmod/rest, то роут можна додати через декоратор, або через розширення.

На відміну від @nestjs/core + @nestjs/common, де централізовано зашито конкретні декоратори і їхній синтаксис. А якщо самому спробувати написати кастомний декоратор — це ризик опинитись за межами життєвого циклу роутінгу NestJS (навіть не впевнений, що взагалі працюватиме кастомний декоратор для роутів).

Ну от, коли напишете скільки завгодно імплементацій декоратора, то і будете ці імплементації оновлювати, щоб додати новий метод. Плюс тут справа не централізації а в кількості функціоналу, як я казав раніше, якби ваш фреймворк мав можливість інтегруватися з умовним Express, Fastify чи ще чимось, то ви теж би мали абсолютно те саме

По-перше це не баг, а погано написані тести, чи я чогось не зрозумів?

Ну помилкові тести, які взагалі не працюють — це теж баг, просто не в коді основної програми. Працює не так, як задумано — це баг.

Помилкові тести не створюють баги в основній програмі, чи мала програма відхилення очікуваної поведінки від реальної? Якщо так — це баг, якщо ні, то це не баг.

Так виправдовувати NestJS може лише «свідок NestJS». Вибачте, оскільки ви не визнаєте навіть очевидні речі, з вами не цікаво вести дискусію.

Ставлюся негативно. Якщо є зайва бюрократія це може суттєво гальмувати проєкт/онбордінг. Є достатня бюрократія, накшталт RoR/Laravel. Якщо знаходиш себе у купі бюрократії, значить загальна архітектура системи/фреймворка вже має глибинні вади.

Якщо JS, краще звернути увагу на AdonisJS.

Яка бюрократія? NestJS з коробки має рекомендовану структуру файлів проєкту, яка інтуїтивно зрозуміла і є «випробувана боєм» вже протягом десятиліть, і зазвичай, якщо на проєкті NestJS то зазвичай цієї структури дотримуються, по вашому схожа структура файлів від проєкту до проєкту ускладнює онбордінг?

Які по вашому переваги AdonisJS над NestJS?

Я використовую Laravel, а AdonisJS це Laravel/RoR-like фреймворк, який притримується тих самих принцепів, які зарекомендовали себе з позитивної сторони. Це спрощує світч між фреймворками з різних мов, тому вважаю це загалом сильною стороною Adonis. Можна онбордити людей, котрі прийшли з Laravel, а не тільки в рамках фреймворка.

А якщо людина прийшла не з Laravel? Такий собі аргумент, якщо чесно.

Можна онбордити людей, котрі прийшли з Laravel, а не тільки в рамках фреймворка.

І вам не здається що ви собі тут трохи протиречите? Або я вас не так зрозумів, що означає «а не тільки в рамках фреймворка»? У рамках якого фреймворка? NestJS використовує старий добрий Layered Architecure, це не привʼязано ні до якого фреймворку, це база архітектури бекенд апки, це мають знати абсолютно всі бекенд розробники. То все ж будуть ще якісь переваги AdonisJS над NestJS окрім того, що перший близький для представників PHP?

Зайшов на сайт AdonisJS, там пропонуєтся всі контролери і т.д. складати в одну папку і називають це «thoughtful file structure» ну це просто крінж, така файлова структура працює лише в мікросервісах (справжніх маленьних мікросервісах) і тестових застосунках які в 100% випадках приводяться як приклад в новомодних лібах/фреймворках, які кажуть «нащо використовувати підхід, який добре працює десятиліттями, коли можна зробити „простіше“», а на ділі цей підхід не вивозить застосунки принаймні середнього масштабу

Головне вам підходить. Я лише звернув увагу на те, що в JS є RoR-like фреймворк. Якщо вам більше підходить NestJS, це не означає що іншим він теж обовʼязково повинен підходити. Проте краще мати декілька альтернативних фреймворків з різними поглядами. Це конкуренція і це робить екосистему краще.

То все ж будуть ще якісь переваги AdonisJS над NestJS окрім того, що перший близький для представників PHP?

Я навіть не натякав, що збираюся шукати переваги AdonisJS над NestJS. Я негативно ставлюся до NestJS, через те що він здався мені многословним суто по документації. Для мене AdonisJS зрозімілійший, тому що RoR-like (плюс документація близька до Laravel). RoR-like — це доволі поширена когорта фреймфорків, котрі використовують риси RoR. Ці фреймвокри мені здаються зручними, логічними, простими. Суто з практичного досвіду.

Щодо контроллерів в одній папці. Не певен, що це догма і забороняється створювати під-папки. ;)

У мене повинен бути такий самий сет знань щодо «Layered Architecure», що й у вас, щоб вести конструктивну дискусію.

тобто ви у своєму першому коментарі сказали, що ставитеся до NestJS негативно і радите Adonis.js, але при цьому не можете назвати переваг другого над першим, окрім того, що вам звичніший його підхід?

Саме так, я ставлюся негативно — це відповідь на питання у кінці статі «А як ви ставитесь до NestJS і його ролі у сучасному бекенд-стеку?», та я пропоную звернути увагу, а не раджу. Я не формулюю свою позицію, що вам неодмінно треба відмовитися від NestJS і пересісти на Adonis, радше «дивіться тут є ще й такий фреймворк, і мені здається, реалізація більш чиста, зрозуміла, доступна».

Моє негативне ставлення обумволене тим, що мені NestJS не сподобався в плані документації. Як приклад, наявність конкуруючих архітектурних елементів Middleware та Interceptor, що трохи збиває з пантилику. Нащо мати два схожих механізма? В Laravel/Adonis кешування і всі перехоплення Request/Response робляться в Middleware. Це натякає на те, що треба враховувати архітектуру ExpressJS-like мідлвар і більш потужну реалізацію NestJS Interceptor. Мені це не сподобалося, бо більше речей треба тримати в увазі, а значить меньше уваги приділяти бізнес-логіці. Можна апелювати, що немов так гнучкіше, але ні, це просто многослівність. Та мені, як працюючому з Laravel, доступнішим є AdonisJS (архітектурні елементи ті самі, означають і роблять те саме).

Буде нагода ознайомитися з NestJS ближче, можливо, зміню своє ставлення.

Все ж, для багатьох випадків ця структура просто не потрібна. Дуже часто доводиться писати велику купу нових гейтів по одній строці в модулі, контролері та сервісі, що могло було легко замінено одним методом де і буде вся логіка з цих трьох строк коду.
Це окей десь умовно в С# або Java робити, але в JS є можливість писати без цього, для чого JS і створювалася спочатку — написати швидкий та малий код(скриптова мова)

Мені цікаво почути для яких таких випадків ця структура не потрібна? Знаю лише мікросервіси, і тестові апки по типу крудів TODO списку. А що робити коли апка серйозна, хоча б середнього масштабу коли є багато бізнес сутностей (модулів) і зон відповідальності? От на цьому моменті використання «простішого підходу» дуже сильно все ускладнює, а підхід NestJS (який не сам придумав цю структуру, а взяв те, що добре працює десятиліттями) дозволяє логічно і зрозуміло розділяти зони відповідальності різних бізнес сутностей, а також відділяти бізнес логіку, транспорт, валідацію, авторизацію і data-access layer роблячи їх модульними і незалежними.

Це окей десь умовно в С# або Java робити, але в JS є можливість писати без цього, для чого JS і створювалася спочатку — написати швидкий та малий код(скриптова мова)

JS вже давним давно не використовується для того, для чого він створювався на початку це раз, два — комплексна задача, буде вимагати комплексного рішення, це ніяк не обійдеш «простими підходами». Тому Layered Architecure і розділення бізнес сутностей (модулів) це база в бекенд розробці, яка працює як годинник, спроби упростити це, що і так насправді є простим ні до чого доброго в проєктах хочаб середнього масштабу ні до чого доброго не підходять. Я погоджуюся, що JS/TS дозволяє писати код простіше за Java/C# але треба розуміти де спрощення використовувати має місце, а де не має.

Дуже часто доводиться писати велику купу нових гейтів по одній строці в модулі, контролері та сервісі, що могло було легко замінено одним методом де і буде вся логіка з цих трьох строк коду.

Не зрозумів що ви маєте на увазі, можете, будь ласка, привести приклади, чи пояснити іншими словами?

Підписатись на коментарі