Midmare — свободный от HTTP-Layer модуль Node.js

—

от автора

Midmare — это минималистичная open-source библиотека. Я написал её в достаточно сжатые сроки с целью оптимизировать конкретные процессы в своей компании. Но в результате удалось прийти к решению, потенциал которого значительно превышает изначальную задумку.

В этой статье я расскажу об особенностях и возможностях Midmare, а также поделюсь своим опытом в создании собственной библиотеки.

Предыстория

Продукт, с которым я работаю, сейчас переживает процесс миграции. Пока он не окончен, мы параллельно существуем на двух платформах и отходим от legacy. Я занимаюсь серверной частью веб-приложения для новой платформы. Некоторые процессы приходится постоянно унифицировать с другими командами.

С учетом этих особенностей необходимо было найти гибкое и универсальное решение, чтобы переписать существующий backend. Таким решением стала библиотека Midmare.

Вводные задачи

У меня была задача сделать так, чтобы библиотеку можно было использовать и с TypeScript, и с JavaScript. При этом команда целилась на то, чтобы полностью переписать код с TS на JS для ускорения разработки.

JS не типизирован, поэтому с ним разработка идет значительно быстрее, пусть и менее стабильно. Мы были готовы пойти на такие риски, так как планировали обезопасить себя, написав достаточно тестов.

Сначала неделю-полторы я думал над концепцией, перебирал разные варианты. Пока не пришел к идее, что было бы классно использовать функциональный подход так, чтобы была возможность в любой цепочке исполнения вставить любое звено без последствий.

Я пришел к идее использовать middleware

А именно — сделать цепочку функций промежуточной обработки, которые выполняются друг за другом и реагируют на ошибки друг друга (ошибки, проблемы, изменения данных и тому подобное).

Так у меня получился стек middleware, который по своему принципу похож на Коа. Такая схожесть гарантирует, что остальным библиотеку будет легко использовать. Но в Midmare методов значительно меньше, всего 4. Библиотека Midmare сама по себе минималистичная.

Начало работы

Перед установкой Midmare необходимо скачать и установить Node.js версии 10 и выше. При создании нового проекта нужно также сперва создать package.json с командой npm init.

Установка Midmare выполняется через команду npm install midmare.

Ниже приведен образец инициализации приложения.


Общая инициализация приложения на Midmare


Использование роутеров (промежуточных обработчиков уровня маршрутизатора)

Возможности Midmare

В первую очередь, мы говорим о впечатляющих возможностях декомпозиции кода. Промежуточные функции (middleware) могут не знать друг о друге ничего. Они просто берут данные, обрабатывают и отпускают дальше.

Библиотека Midmare идеально подходит в тех случаях, когда нужно использовать одну систему маршрутизации для разных API — source и destination. То есть когда нужно создать некий общий узел обработки. Но так же можно создавать и более простые приложения.

Приведу несколько примеров, чтобы подчеркнуть возможности Midmare.


Отлов ошибок с помощью промежуточной функции. Он же пример декомпозиции кода


Простая промежуточная функция, закрывающая HTTP-сессию в случае несовпадения маршрутов


Пример обработки какой-либо команды, в данном случае — отправка данных в Redis

Маленькая библиотека делает свою маленькую работу, но при этом ни в чем не ограничивает доступные тебе опции. В Midmare можно подключить по сути всё, что угодно — не ломая голову, как строить архитектуру.

Ключевое преимущество

Ключевое преимущество этой библиотеки — полная отвязка от каких-либо лейеров (HTTP, WS, etc.). Если Koa рассчитан на HTTP-сервер, то Midmare в этом плане не создает ограничений.

Можно даже просто написать терминальную утилиту. С Midmare это удобно, так как мы можем использовать параметры в «. Как в примере ниже:

Используя Midmare, можно вернуться всё к тому же HTTP-серверу. Подключить его не будет сложностью. Уже есть готовый роутер для HTTP.

При этом также можно спокойно подключать WebSockets. Нужно только инициализировать сам клиент WebSockets. А дальше дело за максимально простым роутингом.


Пример объединения HTTP и WebSockets с Midmare

Такая универсальность устраняет необходимость продумывать разные пути решения одной и той же проблемы в одной программе.

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

Готовых библиотек такого формата я не встречал

То есть полного аналога: без низкоуровневых привязок, будь то WebSockets, HTTP или, если обобщать, сетевая связь или программная работа в самой ОС.

Midmare может делать это всё одновременно и буквально в 5–6 строк настройки.

А идея в её основе достаточно простая — построить цепочку выполнения функций с использованием метода next/send для вызова. Если ничего дальше вызывать не нужно, программа остановится. Плюс роутинг, минус HTTP — и мы имеем Midmare.

Зачем использовать эту библиотеку?

Всегда можно написать собственное решение. Конечно, при этом у вас может хромать декомпозиция или сам код (например, может не получиться вклиниться между шагами в цепочке). Если таких огрехов много, то программа, написанная вручную, может прийти к плохому финалу через год–два. Midmare позволяет этого избежать.

Кроме того, слишком много фильтров существует в мире программистов. Кому-то хочется писать на TypeScript, кому-то на JS. Я искал решение, чтобы захватить оба варианта.


Сейчас я стараюсь активно тестировать и дорабатывать Midmare

Большой плюс маленькой библиотеки — её создатель сам же её активно поддерживает.

Свою библиотеку я тестирую каждый день и, соответственно, быстро нахожу подводные камни или недоработки. С маленькой библиотекой любую проблему можно исправить в кратчайшие сроки.

Циклические вызовы — первая из исправленных проблем

Посмотрим на пример ниже:

Допустим, нам нужно добавить в шаг .process(‘/message’) вызов ‘/send’, но в .process(‘/send’) уже содержится вызов ‘/message’. Тогда Шаг №2 будет вызывать Шаг №1. При этом в Шаге №1 будет каждый раз срабатывать вызов Шага №2 — и так по кругу.

Когда работаешь над созданием библиотеки, такие проблемы нужно вылавливать, показывать пользователям, объяснять, чем это чревато.

В данном случае у нас возникнет ошибка Maximum call stack size exceeded и JS упадет в ответ на многократный рекурсивный вызов одной и той же функции.

Решение такой проблемы в этой библиотеке: Сохранение истории путей в одном контексте. То есть каждый вызов `ctx.send` или `app.send` имеет свою историю вызовов.

Поскольку в Midmare есть обработка этого момента, библиотека выдаст ошибку в случае циклического вызова.

Может быть ситуация, в которой циклический вызов нужен. Например, две промежуточные функции обрабатывают одни и те же данные по-разному, потому что существует возможность выхода из цикла. То есть, когда в одной из функций есть if-else, который отработает в else и проигнорирует циклический вызов.

На случай, если в реализации будет требоваться цикличность, в Midmare существует опция установить игнорирование ошибки через `ignoreCycleError: true`.

Библиотека ждёт своё комьюнити

В такую pure-библиотеку в принципе нет смысла что-либо добавлять. Было бы неплохо добавить возможность подключать плагины, но с middleware это можно решить и без нового функционала.

Если в конкретном случае не хватает какой-то обработки, её можно самостоятельно дописать в условные 4 строчки. Как в примере с функцией для закрытия HTTP-сессии:

Все дополнительные обработки можно вынести в отдельный репозиторий. Или разнести роутеры по репозиториям. Подключаться и обрабатываться всё будет так же.

Что библиотеке действительно не помешало бы на сейчас, так это сообщество, которое бы создавало на основе библиотеки свои инструменты. Поэтому я приглашаю комьюнити вносить свой вклад в развитие библиотеки. Присоединяйтесь и тестируйте.

Автор: Иван Петушинский, Senior Node.js Developer

ссылка на оригинал статьи https://habr.com/ru/company/parimatch_tech/blog/518026/