Добрый день. В нашей компании мы очень трепетно относимся к архитектуре наших решений и удобству ее использования. Мы стараемся убирать узкие места для получения лучшей производительности, а также для сокращения времени разработки новых фич. Одним из таких способов мы решили поделиться с вами.
Сегодня поговорим о фронт-оптимизации, об express, nest.js и о том, как этим правильно пользоваться. Думаю, все уже обратили внимание на тенденцию, которой следуют фронтовые разработчики: делать бек на node.js. Это значит, что для фронта настало золотое время – давайте разберёмся почему. Сразу отмечу, что я нарочно буду избегать разбора механизмов работы node.js, а также всех его плюсов и минусов, так как сегодня речь пойдет не об этом.
Зачем писать бек на node.js?
Исходя из своего опыта, могу сказать, что эта программная платформа применяется в случаях, когда требуется вынести часть бизнес-логики в прослойку между историческим беком и свежим фронтом. Я встречал ситуации, где node.js служил инструментом для перехода от одного типа архитектуры (монолит) к другому (микросервисной). В качестве промежуточного вывода можем заключить: node.js позволяет нам создавать унифицированное API, если по какой либо причине бек не может выполнить свою работу.
На чистом node.js практически никто не пишет: большинство, как правило, используют какой-либо фреймворк. Давайте рассмотрим один из самых популярных на данный момент таких фреймворков.
Express
Большинство приложений написано на простом и минималистичном фреймворке express.js. Он прост в освоении, гибок и даже вроде как позволяет всё сделать легко. Однако у него есть недостатки, о которых расскажу далее.
Первый и самый главный — гибкость. Это означает, что при отсутствии навыков его использования вы или совершите ошибки, или случайно откроете временной портал.
Второй недостаток — импорт. Данный функционал реализован не слишком нативно, поэтому если вы просчитались с архитектурой, то мешанины вам не избежать.
Третий недостаток состоит в том, что большинство проверок на тип и пустоту требуется осуществлять вручную, так как в express.js применяется ES, а не TS. Также к минусам отнесу пляски с выставлением кукис и прочим серверным взаимодействием со стороны UI.
Все вышеперечисленное свидетельствует о том, что в express.js используется устаревший паттерн callback-функций, что в свою очередь зачастую приводит к печальным последствиям. Ещё раз напомню, что использование express.js в чистом виде практически невозможно, за исключением каких-то простых приложений. Поэтому я предлагаю взамен ему использовать nest.js.
Nest.js
Nest.js — фреймворк для создания серверных приложений на node.js, расскажу о его особенностях, плюсах и минусах.
Nest.js написан на TypeScript и полностью поддерживает его (даже современные версии). Что это нам дает?
Проверку на тип данных: как простых, так и сложных. Enum и прочие опции сильно выручают.
Декораторы – функционал декларативного программирования, позволяющий расширить любые методы, какие только пожелаем.
Классические импорты TypeScript (до боли знакомые разработчикам Angular). Побочным эффектом послужит ускорение разработки, а также возможность писать код большой командой, не мешая друг другу.
-
Четкое разделение функциональной нагрузки по элементам:
Interceptor — отвечает за дополнение, перехват запросов.
Guards — отвечает за проверки доступности по каким-либо критериям.
Pipes — выполняет две роли. Первая — это преобразование одного типа в другой. Например, когда у вас ID ожидается строкой, а пришло число, и чтобы не страдать преобразованием типов на фронте, pipe реализует это на беке. Вторая функция — это проверка соответствия типов.
Custom route decorators — если вы считаете себя гениями, которым море по колено, то можете создать что-то жизненно необходимое.
Exception filters — это все исключения, которые могут быть (согласно сетевому взаимодействию). В тех случаях, когда мы забыли сделать кастомную обработку ошибок, мы все равно получим данные по ним.
Middleware — предобработка запросов с доступами ко всей информации запроса.
Modules — используется для организации кода и архитектуры приложения.
Controllers — отвечает за обработку входящих запросов и возврат ответов клиенту. А также за роутинг, парсинг параметров, в общем за всё, что касается принятия и отдачи по запросу.
-
Nest.js поддерживает любые протоколы в добавок к HTTP, например, на основе RabbitMQ, Nats, Kafka или даже просто TCP-протокола.
Все это доступно просто из коробки, без плясок с бубном, до установки модулей, плагинов, расширений и прочего. А еще присутствует CLI — это значит, что с помощью командной строки код у нас будет генерироваться, как нужно, и прописываться везде, где нужно, что, в свою очередь, значительно ускорит работу. Не правда ли, звучит знакомо? Особенно для тех, кто хоть когда-то писал на Angular.
Теперь о плюсах и минусах.
Плюсы:
Легко использовать, учиться и осваивать.
Мощный интерфейс командной строки для повышения производительности и упрощения разработки.
Подробная и отлаженная документация с примерами (можно прочесть на https://nestjs.com/, а еще у них есть платные курсы, первый раз такое вижу).
Открытый исходный код.
Простые приложения для модульного тестирования.
-
Создан для монолитов и микросервисов.
Минусы:
Требуется выучить большой объём информации, чтобы правильно все использовать.
Придётся много писать DTO, для того чтобы данные корректно обрабатывались в запросе.
-
Придётся иногда делать множественные проверки по типам, например, Enum и прочие.
Резюмируя: Nest.js — прекрасный инструмент, который позволяет делать много, быстро, а главное, качественно, в обмен на хорошее описание DTO и прочих объектов. По мне, это небольшая плата за то, что снимает с меня львиную долю головной боли по проверкам, ответам, дополнению запросов и прочей рутины. И вот вам еще хинт, как из минусов выжать максимальное количество плюсов ;)
Как правильно пользоваться Nest.js
Axios — потребуется для перехвата и подсовывания в него мок (для тестов самое то).
@apidevtools/swagger-parser — позволяет автогенерировать код со swagger бека для упрощения написания интерфейсов, что значительно ускоряет написание кода.
Swagger-ui-express — позволяет создать свой swagger и тестировать свою документацию (можно отдельно отдавать на тестирование).
Swagger-typescript-api — позволяет спарсенный код swagger преобразовать в TypeScript и использовать для нужд проекта.
А еще пригодится https://editor.swagger.io/, в него можно вставить то, что сгенерировал наш swagger. Если кликнуть на Generate Client, а затем на typescript-angular, мы получим архив с автосгенерированными сервисами и интерфейсами для нашего приложения. Этот же архив можно использовать для react на тайп-скрипте. Если вы его внимательно изучите, то обнаружите, что он много для чего может подойти, чем облегчит вам жизнь и ускорит разработку.
P.S. Таков мой опыт использования фреймворков node.js, надеюсь, он окажется для вас полезным. С радостью отвечу на все ваши вопросы.
Автор: Дмитрий Ивко, ведущий разработчик Центра продуктов Dozor компании "РТК-Солар"
Комментарии (25)
Pab10
17.05.2022 12:26Декораторы – функционал декларативного программирования, позволяющий расширить любые методы, какие только пожелаем.
Это не функционал, а синтаксический сахар. В жс функции высшего порядка, так что декораторы встроены изначально.
jonezq
17.05.2022 14:02Третий недостаток состоит в том, что большинство проверок на тип и пустоту требуется осуществлять вручную, так как в express.js применяется ES, а не TS. Также к минусам отнесу пляски с выставлением кукис и прочим серверным взаимодействием со стороны UI.
ES? Что мешает использовать express с TS? какие проблемы у express с cookies? И как это решает nest?
Прочим серверным взаимодействием - что конкретно не так, и как это решает nest?
Почему axios, rest, swagger, angular
Тупо кликбейт
SolarSecurity
17.05.2022 14:11+1Во-первых, из коробки express не поддерживает TS, нужны костыли и прочее. Сама структура приложений на экспрессе не предусматривает использования всех фишек TS, что ведет к снижению его эффективности.
С кукис проблема следующего характера: их на экспрессе надо постоянно парсить при каждом запросе, затем, если надо, выставлять и совершать прочие манипуляции. Упрощение работы с кукис происходит только тогда, когда мы прикручиваем сторонние плагены, но опять же это сторонние зависимости.
У nest есть базовые компоненты для работы с кукис, например, интерсептор помогает подсовывать их в автоматическом режиме при запросе к беку, или в контролерах использовать через контекст при ответах на ui.
Почему axios, rest, swagger, angular. Потому что axios позволяет нам перехватывать запросы и подсовывать мок, что упростит нам тестирование в автоматическом режиме. Rest - самый используемый паттерн взаимодействия. А это значит, что он будет большинству полезен. Но nest умеет работать почти со всем.
Swagger - самый удобный инструмент для самодокументированного кода, что позволяет протестить бек, прослойку между беком и ui , спокойно, без лишних плясок.
Angular – мы его используем в повседневной работе, но в статье также упомянуто, что можно выбрать и другие приложения, просто зайдите на сайт.
jonezq
17.05.2022 16:01+1С каких пор добавление поддержки TS стало костылем? У express есть типы на TypeScript, которые зачастую используются в nestjs, nestjs все лишь набор оберток вокгур популярных решений(в том числе и express)
interceptor - это middleware которое есть в express, в express обычно так же мутируют реквест(точнее nestjs делает это так же как в express, fastify использует немного другую технику, но тут о нем речи нет)
да есть куча тулов и дока по несту имеет хорошее описание, так почему именно эти - в чем их эффективность?
Первый и самый главный — гибкость. Это означает, что при отсутствии навыков его использования вы или совершите ошибки, или случайно откроете временной портал.
дереватив гибче родителя? - ну ок
Второй недостаток — импорт. Данный функционал реализован не слишком нативно, поэтому если вы просчитались с архитектурой, то мешанины вам не избежать.
чего?
серьезно, я сам использую nest, но титул - как эффективно использовать nest.js, сводится к полуярным либам, и к сложности добавления tsconfig к экспрессу.
если использвать TS c express количество dto станет меньше?
Evil_Evis
17.05.2022 20:28С каких пор добавление поддержки TS стало костылем? У express есть типы на TypeScript, которые зачастую используются в nestjs, nestjs все лишь набор оберток вокгур популярных решений(в том числе и express)
Давайте по порядку, прекратить TS ко всему можно. Вопрос что может из коробки и сколько действий потребуется для того что бы начать разрабатывать с удовольствием и удобством.
interceptor - это middleware которое есть в express, в express обычно так же мутируют реквест(точнее nestjs делает это так же как в express, fastify использует немного другую технику, но тут о нем речи нет)
Его можно сгенерировать автоматически? он удобнее? Где его проще написать? Вопрос не в том что где есть, а где удобнее. Подход почти везде одинаков, исполнение везде разное. Как говориться дьявол, кроиться в деталях.
да есть куча тулов и дока по несту имеет хорошее описание, так почему именно эти - в чем их эффективность?
в статье вроде ни где не указано что они единственные и неповторимые, просто автор привел то с чем работает. Есть другие на примете, давайте делиться. С радостью прочитаю обзор и сравнение на них.
Первый и самый главный — гибкость. Это означает, что при отсутствии навыков его использования вы или совершите ошибки, или случайно откроете временной портал.
Чем гибче инструмент тем больше ответственность за архитектуру, можно натворить циклических зависимостей и прочего ненужного. Фреймворк - забирает гибкость но в замен дает стандарт использования. Так как фреймворк имеет четко очерченную зону ответственности у каждой его части.
Второй недостаток — импорт. Данный функционал реализован не слишком нативно, поэтому если вы просчитались с архитектурой, то мешанины вам не избежать
автор имел ввиду require.
серьезно, я сам использую nest, но титул - как эффективно использовать nest.js, сводится к полуярным либам, и к сложности добавления tsconfig к экспрессу.
если использвать TS c express количество dto станет меньше?
Что-то вас не понял, причем количество DTO, вопрос в том что можно автоматизировать данный процесс. С радостью выслушаем ваш опыт использования и советы по использованию.
jonezq
18.05.2022 15:55С радостью выслушаем ваш опыт использования и советы по использованию.
куда мне-то давать советы, я умею только в доку, зашел почитать как использовать оптимальнее
webdevium
17.05.2022 14:42+1Мне кажется, что сравнивать micro фреймворк и full-stack фреймворк — кощунство.
Это как сравнивать sinatra.rb и Ruby on Rails.Evil_Evis
17.05.2022 20:06ну как-то сравнивают же реакт и ангуляр? По сути тоже самое. Они оба выполняют одну и туже функцию. Вопрос удобства и порога вхождения
return
17.05.2022 14:53Как эффективно использовать-то? )
Evil_Evis
17.05.2022 20:04тут вроде рассказывается что и как. Приведенные пакеты помогут автоматизировать, алгоритм прост.
Перечень действий:
устанавливаем пакеты
с помощью @apidevtools/swagger-parser мы парсим свагер бека для того что получит JSON для обработки
потом его переводим в удобно используемый код с помощью Swagger-typescript-api
далее при помощи Swagger-ui-express мы сможем иметь свагер нашего BFF
А для автоматизации тестирования что бы подставлять моки (перехват запросов) используем Axios
выше изложенные действия экономят нам кучу времени на описания интерфейсов и прочего
Suvitruf
17.05.2022 19:31Сравнивать Express, который, по сути, просто библиотека, с полноценным фреймворком в виде Nest.js, это какое-то издевательство над первым ????
Evil_Evis
17.05.2022 19:38Ну тут вопрос реторический как реакт и ангуляр. Но реакт разработчики говорят что ангуляр не ровня.
martovsky
18.05.2022 10:41+1Особенно смешно читать по причине того, что nest использует как раз express.
Suvitruf
17.05.2022 19:34Требуется выучить большой объём информации, чтобы правильно все использовать.
Да нет. Его прелесть как раз в том, что стартовый проект легко заводится. Нет необходимости досконально знать каждый аспект/компонент, а можно изучать по мере необходимости.Придётся много писать DTO, для того чтобы данные корректно обрабатывались в запросе.
Странно жаловаться на это. Это как жаловаться на возможность/необходимость использовать типы в ts.Придётся иногда делать множественные проверки по типам, например, Enum и прочие.
Можно подробнее о каких проверках речь?Evil_Evis
17.05.2022 19:56Да нет. Его прелесть как раз в том, что стартовый проект легко заводится. Нет необходимости досконально знать каждый аспект/компонент, а можно изучать по мере необходимости.
Большой объём инфы, по сравнению с express. А так же не надо забывать что плоха можно написать везде, а вот правильно не всегда просто. Как пример Interceptor это же по сути сервис, и его функциональность можно руками сунуть во внутрь
Странно жаловаться на это. Это как жаловаться на возможность/необходимость использовать типы в ts.
Это не жалоба, это просто примечание того что boilerplate много писать придаться в части DTO. Как пример доскональное описание полей а так же их проверок.
Можно подробнее о каких проверках речь
пример описания пары полей ниже, просто представите то у вас порядка 50 полей и на каждое надо описание как ниже
// Пример описания ответа @ApiProperty({ description: 'Код сообщения', type: String, example: 'USERS_001', }) code?: string; @ApiProperty({ description: 'Уровень сообщения', enum: getEnumValues(SelectedLevelTypes), /сравнение с Enum required: false, example: SelectedLevelTypes.INFO, }) @IsEnum(SelectedLevelTypes) level?: SelectedLevelTypes; @ApiProperty({ description: 'Текст сообщения', type: String, example: 'Catalog service does not reply', }) text?: string; // описание одного поля для входного параметра @ApiProperty({ description: 'Координаты', type: GeolocationAddresses, required: false, }) @IsOptional() / параметр не обязателен в запросе @Type(() => GeolocationAddresses) // что данное поле имеет сложное тип (тоесть вложенность) @ValidateNested({ each: true }) // включение проверки вложенных типов geolocation?: GeolocationAddresses;
Suvitruf
17.05.2022 21:01пример описания пары полей ниже, просто представите то у вас порядка 50 полей и на каждое надо описание как ниже
Так это же для свагера. В express'е не меньше бы писать пришлось.Evil_Evis
18.05.2022 09:50не совсем оно так, к свагеру относиться только код нижу
@ApiProperty({ description: 'Координаты', type: GeolocationAddresses, required: false, })
А все остальное это пайпы самого nestjs. О которых вы можете прочитать здесь. Так же пайпов может быть довольно-таки много если сложные проверки например, когда одно поле зависит от другого.
jonezq
19.05.2022 16:40причем здесь пайпы - это обычные dto объекты которые будут в любом статически типизированном бэкенде. То что вам надо десерилизовать данные из запроса - в каком-нибдуь dotnetcore этого делать не надо? да надо
большие проекты на express и nestjs по структуре сильно похожи, теже +/-тулы, по сути команда nestjs обобщила хорошие практики из бэкендов на ноде и сделала удобный враппер, как next вокруг react
ReadOnlySadUser
Я может сейчас чего-то не понял, но где хоть одно слово про архитектуру фронта в этой статье?
Hrodvitnir
Ну да
Не смотря на полезность статьи – название абсолютно не отображает ее сути:)
SolarSecurity
Поменяли, приносим извинения :)
SolarSecurity
Добрый день. Смысл статьи в том, что необходимо разделять бизнес логику и ui логику. В целом, это первая статья из цикла статей по оптимизации и подходам на фронте. Для начала мы рассказали, как сделать жизнь легче путем автоматизации некоторых процессов (автогенерацию). В планах - рассказ, как сделать быстрый ui, затем, как все соединить в единое целое. Для ясности постараемся четче подбирать названия для статей.
ReadOnlySadUser
Да я вообще не сварщик, я на С++ пишу :) Просто зашёл почитать про архитектуру, мало ли какой-нибудь прикольный архитектурный принцип завезли, можно будет подумать над тем, имеет ли предлагаемая архитектура право на жизнь в других языках, а тут просто обзор двух фреймворков. Сейчас название получше)