Я очень долго не воспринимал JavaScript, как язык программирования общего пользования. Добавить падающих "снежинок" на корпоративную web-страничку перед Рождеством — вот, для чего на самом деле придумали JS, казалось мне поначалу. Ну что серьёзного можно было сделать на языке, который не имел в своём арсенале функционала по работе с файловой системой, не говоря уже про доступ к БД? Однако сейчас, по прошествии двух десятков лет, я считаю, что JS развился достаточно, чтобы быть основным языком программирования для создания web-приложений. Сейчас у него есть всё для этого. На самом деле, эта статья не про то, как использовать HTTP/2 сервер в web-приложениях, а про то, как можно писать приложения с современным JS. И про HTTP/2.  Когда я только начинал, в 1999-м году, web-приложения создавались из расчёта, что JS в браузере клиента может быть отключен. SSR рулил, хотя такой аббревиатуры ещё не было (она появилась позднее, когда появился рендеринг на клиенте). Тогда из ЯП в топе у web-разработчиков были Java (её пихали в клиентские апплеты и надеялись, что ещё чуть-чуть и "настоящий" ЯП вытеснит из браузера "это недоразумение"), ASP (от "корпорации добра" того времени) и PHP ("бедненько, но быстренько"). Ну, и ещё Тёма Лебедев со своим parser'ом — это уже для совсем упоротых, в число которых на некоторое время вошёл и я (к счастью, быстро вышел). Даже когда появился AJAX я не видел особых перспектив у JS. Да, на клиенте у JS не было альтернатив, но использовать JS на клиенте — вступать в конфликт с поисковиками. Они JS поначалу игнорили на все 100%. Потом были jQuery, SPA, ExtJS и GWT. С jQuery я имел дело постольку-поскольку, а с GWT довольно плотно. JS всё ещё не был тем языком, на котором мне бы хотелось писать код. Даже когда появились nodejs, TypeScript и ES2015 (ES6) я не понимал, как JS можно использовать для создания крупных модульных приложений, особенно в браузере. С 2012 я плотно сидел на Magento (написана на PHP, но в enterprise-стиле — пакетно-модульно, с namespace'ами, IoC и прочими архитектурными паттернами), но попытки работать с JS из Magento оборачивались головной болью (кто сталкивался с ui-компонентами Magento, тот в курсе).  Кратковременное переключение на python принесло разочарование — тот же PHP, только ещё и без namespace'ов и с проблемами совместимости между версиями 2 и 3. Первый angular — слишком сложно, несмотря на прекрасную документацию. React я даже не трогал — идея добавлять JS в HTML мне казалась приемлемой, а вот HTML в JS (JSX) — слишком экстравагантной. PHP, ASP, JSP — везде программный код добавлялся в код разметки, а не наоборот. Я смотрел в сторону более традиционных Ember и Vue. Поворотным моментом была Magento-конференция в Риге в 2019-м году, на которой я увидел демонстрацию Vue Storefront. Это уже было нечто похожее на то, каким должны были быть "красивые" web-приложения с моей точки зрения. Я занырнул в PWA и увидел, что новые возможности браузеров очень сильно меняют правила игры, смещая функциональный акцент с бэка на фронт — web-приложения "устанавливаются" в браузер клиента, работают в режиме "offline" и архитектурно приблизились к десктопным приложениям (появилась даже своя IndexedDB). А на фронте у JS нет альтернативы. Вернее, все альтернативы (включая Java Applets, Macromedia Flash, Microsoft Silverlight, Google Dart) так и не смогли потеснить JS. Тем не менее, процесс программирования на JS был всё ещё далёк от того, к какому я привык за время работы с Java и PHP. Для начала мне очень сильно не хватало возможности адресации любого элемента кода (класс, функция, константа). В Java/PHP package'ы и namespace'ы позволяли однозначно идентифицировать элементы ("Copy Reference" в IDEA), а JS либо выдавал просто имя метода/класса без привязки к файлу, либо имя файла и номер строки в нём. Для сопровождения разработки в той же JIRA этого явно было мало. Проблему разрешили аннотации JSDoc и имена элементов кода в стиле PHP Zend 1 (Vnd_Prj_Mod_Name). Следующим камнем преткновения был DI. К хорошему быстро привыкаешь, а DI — очень хорошая вещь. Я использовал Spring Framework в Java, а затем DI в Magento 2. В Magento 2 менеджер объектов не просто создавал объекты вместе с зависимостями, а позволял подменять одни объекты другими (plugins). Нечто похожее я хотел найти и для JS, но оказалось, что просто DI, который бы работал и в браузере, и в nodejs-приложениях, найти было проблематично. Пришлось разбираться с функцией import() и делать DI самому. Самое простое, но самое последнее — интерфейсы. В JS интерфейсов нет. Зато они есть в JSDoc. IDEA очень хорошо разбирает аннотации JSDoc и "видит" связь между интерфейсом: и имплементацией: С учётом того, что у меня есть свой собственный DI-контейнер, добавить в него инструкции по подмене интерфейсов их имплементациями — дело техники: Да, это настройка для всего контейнера, но если мне понадобится, то я могу добавить настройки и для отдельного es-модуля — ведь у меня есть namespace'ы. Итого: на данный момент я вполне удовлетворён тем, что из себя представляет JavaScript, и могу нарабатывать инструментарий для решения типовых задач в области прогрессивных web-приложений. Теперь собственно, о том, как я всё это применяю. Начну с дескриптора ./teqfw.json плагина @teqfw/http2 (про дескриптор — "Плагины"). Он не слишком велик, поэтому привожу его целиком: В дескрипторе определяется пространство имён для es-модулей плагина (TeqFw_Http2), который использует DI-контейнер. Можно по разному настраивать DI-контейнер, я взял за основу PSR-4. Привязываешь префикс пространства имён к каталогу и по логическому имени es-модуля вычисляешь путь к файлу на диске (nodejs) или сервере (браузер). Второй момент — логические имена позволяют адресовать важные с точки зрения приложения элементы кода. В дескрипторе плагина указаны модули, которые содержат код консольных команд приложения (см. @teqfw/core). Теперь о том, как инжектятся зависимости. Вот код для HTTP/2-сервера: Имея информацию из дескриптора (/di/autoload), можно по имени класса определить путь к файлу, в котором находятся исходники. Я выработал для себя именно такую (расширенную) форму записи конструктора, хотя начинал вот с такой: Вторая форма более похожа на классическую форму инъекции зависимостей через конструктор в Java: Первый, более развёрнутый, вариант позволяет добавить JSDoc-аннотации к переменным (указания для IDE) и делает более понятным, какие, собственно, данные инжектятся. Мой DI-контейнер устроен так, что один и тот же код может инжектиться в конструктор по-разному: Со временем начинаешь различать на глаз эти варианты: но такая запись помогает IDE, которая затем помогает тебе: В общем, такой подход позволяет отойти от импортов (завязанных на файловой структуре) и перейти к получению зависимостей не только на уровне элементов кода, но и на уровне экземпляров объектов приложения. Так идентификатор singleton-зависимости Vnd_Prj_Mod$ в любом конструкторе соответствует одному и тому же объекту, и без разницы, для какого конструктора этот объект создался впервые. Следствие такого подхода: все объекты должны создаваться через DI-контейнер или через отдельный тип элементов кода — фабрики объектов (рассматривались в статье про DTO). Я очень редко применяю оператор new вне фабрик в своих приложениях — никогда не знаешь, как изменятся зависимости в конструкторах. http2-плагин добавляет две команды для запуска и останова HTTP/2-сервера в добавок к командам запуска/останова HTTP/1-сервера из web-плагина: HTTP/1-сервер из web-плагина использует для обработки запросов обработчики, для которых определяет интерфейс TeqFw_Web_Back_Api_Request_IHandler, и контекст запроса (с интерфейсом TeqFw_Web_Back_Api_Request_IContext). HTTP/2-сервер просто должен быть совместим с этими интерфейсами. Основной момент, что http и http2 сервера из библиотек nodejs по разному предоставляют доступ к заголовкам и телу запроса.  Вот иерархия es-модулей для HTTP/1-сервера и его фабрика для создания контекстов запросов: Для HTTP/2-сервера вот такая иерархия модулей и инъекция фабрики: Вот и всё — на этом уровне в приложении происходит подмена одного сервера другим совершенно незаметно для кода остальных плагинов в приложении — начиная от обработчиков (handler'ов) и вдаль. Все эти "интерфейсы" и "имплементации" нужны только как сигнал разработчикам, где в коде у них предусмотрены разрывы между узлами приложения (как сцепление между двигателем и ходовой частью в автомобиле), используя которые, они могут безопасно подменять ту или иную часть (например, "двигатель"). Интерфейсы очень широко используются в статически типизированных языках, но в языках с "утиной" типизацией они пока что применяются значительно реже. Но это пока. HTTP/1-сервер доступен напрямую из браузера. Просто запускаем сервер: и открываем его в браузере: http://localhost:3000/ С HTTP/2-сервером несколько сложнее — не все браузеры напрямую используют HTTP/2, в основном — поверх TLS. Я в своих приложениях использую проксирующий сервер (apache — я давно им пользуюсь, привык), настраиваю на нём HTTPS, а затем делаю редирект на HTTP/2-сервер самого приложения: В общем, HTTP/1-сервер можно использовать локально для разработки, а HTTP/2 — для production'а. Я изменил демо-проект из предыдущей статьи (habr_teqfw_web) так, чтобы приложение можно было запускать как на HTTP/1-сервере, так и на HTTP/2. Вторая версия приложения, развёрнутая по адресу http://ws.habr.demo.teqfw.com/, работает по протоколу HTTP/1:  А третья версия, развёрнутая по адресу https://http2.habr.demo.teqfw.com/, уже по HTTP/2:  Оба nodejs-сервера стоят за проксирующим apache'м и доступны напрямую по портам 3001 (HTTP/1) и 3002 (HTTP/2) соответственно. Картинки приведены для красоты, т.к., если зацепиться на первое приложение по HTTPS, то от браузера до прокси также будет использоваться HTTP/2, а HTTP/1 будет уже от прокси к nodejs-серверу. Демо-приложение содержит всего 8 зависимостей в ./node_modules/ и тем не менее, способно поддерживать оба протокола:  Да, express делает больше, но у него и зависимостей больше (+48 к моим), а мне пока хватает и того, что есть. Это ж мой персональный инструмент, а не "универсальная таблетка для всего в интернете". JavaScript развился достаточно сильно (как и "среда обитания" web-приложений, да и вообще всё IT) и продолжает развиваться (HTTP/3 на горизонте). Используя новые возможности, можно меньшими затратами выйти на те же результаты. Или теми же затратами достичь результатов гораздо больших. Так было, так есть и так будет ещё какое-то время. Нельзя объять необъятное, как говорил Козьма Прутков, но можно копать вглубь. Выбрать себе тему — и копать, копать, копать…  Я выбрал PWA и копаю. Интересно, что там есть ещё... wiredgeese.com