Руководство по веб-разработке серверной части с помощью Node.js
Дата публикации: 2019-02-04
От автора: большую часть своей веб-карьеры я работал исключительно на стороне клиента. Проектирование адаптивных макетов, создание визуализаций из больших объемов данных, создание панелей управления приложений и т. д. Но мне никогда не приходилось иметь дело с маршрутизацией или HTTP-запросами напрямую. До не давнего времени.
В этом посте приводится описание того, в чем заключается разработка серверной части веб с помощью Node.js, и краткое сравнение написания простого HTTP-сервера с использованием 3 различных фреймворков: Экспресс, Koa.js и Hapi.js.
Некоторые базовые принципы
Здесь я только хочу привести краткий обзор вещей для контекста. HTTP (Hypertext Transfer Protocol) — это протокол связи, используемый в компьютерных сетях. В Интернете их много, такие как SMTP (Simple Mail Transfer Protocol), FTP (File Transfer Protocol), POP3 (Post Office Protocol 3) и так далее.
Эти протоколы позволяют устройствам с совершенно разным аппаратным / программным обеспечением связываться друг с другом, поскольку они предоставляют четко определенные форматы сообщений, правила, синтаксис и семантику и т. д. Это означает, что пока устройство поддерживает определенный протокол, оно может связываться с любым другим устройством. в сети.
Бесплатный курс «NodeJS. Быстрый старт»
Изучите курс и узнайте, как создать веб-приложение с нуля на JavaScript с NodeJS
Операционные системы обычно поставляются с поддержкой сетевых протоколов, таких как HTTP, из коробки, что объясняет, почему нам не нужно отдельно устанавливать какое-либо дополнительное программное обеспечение для доступа в Интернет. Большинство сетевых протоколов поддерживают открытое соединение между двумя устройствами, что позволяет им передавать данные туда и обратно.
HTTP, на котором работает сеть, отличается. Он известен как протокол без установления соединения, потому что он основан на режиме работы запрос / ответ. Веб-браузеры отправляют на сервер запросы на изображения, шрифты, контент и т. д., но после выполнения запроса соединение между браузером и сервером разрывается.
Серверы и клиенты
Термин «сервер» может немного сбивать с толку тех, кто плохо знаком с отраслью, поскольку он может относиться как к аппаратному обеспечению (физические компьютеры, на которых размещены все файлы и программное обеспечение, требуемое веб-сайтами), так и к программному обеспечению (программе, которая позволяет пользователям получать доступ к этим файлам в Интернете).
Сегодня мы поговорим о программной части. Но сначала несколько определений. URL обозначает Universal Resource Locator и состоит из 3 частей: протокола, сервера и запрашиваемого файла.
Протокол HTTP определяет несколько методов, которые браузер может использовать, чтобы попросить сервер выполнить кучу различных действий, наиболее распространенными из которых являются GET и POST. Когда пользователь кликает ссылку или вводит URL-адрес в адресную строку, браузер отправляет серверу GET-запрос на получение ресурса, определенного в URL-адресе.
Сервер должен знать, как обрабатывать этот HTTP-запрос, чтобы получить правильный файл, а затем отправить его обратно браузеру, который его запросил. Наиболее популярное программное обеспечение веб-сервера, которое обрабатывает это Apache и NGINX.
Оба представляют собой полный пакет программ с открытым исходным кодом, которые включают в себя такие функции, как схемы аутентификации, перезапись URL-адресов, ведение журнала и проксирование, и это лишь некоторые из них. Apache и NGINX написаны на C. Технически, вы можете написать веб-сервер на любом языке. Python, Go, Ruby, этот список можно продолжаться довольно долго. Просто некоторые языки лучше делают определенные вещи, чем другие.
Пишем масштабируемые и поддерживаемые сервера на Node.js и TypeScript
Последние три года я занимаюсь разработкой серверов на Node.js и в процессе работы у меня накопилась некоторая кодовая база, которую я решил оформить в виде фреймворка и выложил в open-source.
Основными особенностями фреймворка можно назвать:
В данной статье мы поговорим немного о Node.js и рассмотрим данный фреймворк
Всем кому интересно – прошу под кат
В чем же проблема и зачем писать очередной велосипед?
Основная проблема серверной разработки на Node.js заключается в двух вещах:
Что же с низким уровнем вхождения? Тут основная проблема в том, что с каждым оборотом колеса хайпа и приходом новых библиотек/фреймворков, все пытаются упростить. Казалось бы, проще — лучше, но т.к. пользуются этими решениями в итоге не всегда опытные разработчики — возникает культ поклонения библиотекам и фреймворкам. Наверное самый очевидный пример — это express.
Что не так с express? Да ничего! Нет, безусловно в нем можно найти недостатки, но express — это инструмент, инструмент которым нужно уметь пользоваться, проблемы начинаются когда express, или любой другой фреймворк, занимает главную роль в вашем проекте, когда вы слишком завязаны на нем.
Исходя из вышеперечисленного, начиная разрабатывать новый проект, я начал с написания некоторого «ядра» сервера, позже оно переносилось в другие проекты, дорабатывалось и в конечно итоге стало фреймврком Airship.
Основная концепция
Начиная продумывать архитектуру я задал себе вопрос: «Что делает сервер?». На самом высоком уровне абстракции сервер делает три вещи:
Все же просто, зачем усложнять себе жизнь? Я решил, что архитектура должна быть примерно следующей: у каждого запроса, поддерживаемого нашим севером, есть модель, модели запросов поступают в обработчики запросов, обработчики в свою очередь отдают модели ответов.
Важно заметить, что наш код ничего не знает про сеть, он работает только с инстансами обычных классов запросов и ответов. Этот факт позволяет нам не только доиться более гибкой архитектуры, но и даже сменить транспортный слой. Например мы можем пересесть с HTTP на TCP без изменения нашего кода. Безусловно это очень редкий случай, но такая возможность показывает нам гибкость архитектуры.
Модели
Начнем с моделей, что от них нужно? В первую очередь нам нужен простой способ сериализации и десериализации моделей, еще не помешает валидация типов при десериализации, т.к. пользовательские запросы — это тоже модели.
В дальнейшем описании я опущу некоторые детали, которые можно прочитать в документации, чтобы не раздувать статью.
Вот как это работает:
Если мы передадим данные неверного типа, то сериализатор выбросит исключение:
Модели запросов
Запросы — это те же модели, разница в том, что все запросы наследуются от ASRequest и используют декоратор @queryPath для указания пути запроса:
Модели ответов
Модели ответов тоже пишутся как обычно, но наследуются от ASResponse :
Обработчики запросов
Обработчики запросов наследуются от BaseRequestHandler и реализуют два метода:
Получение запросов
Соединяем все вместе
Генерация схемы API
В первую очередь нужно создать конфиг, который укажет все наши запросы и ответы:
После этого мы можем запустить утилиту, указав путь до конфига и до папки, в которую будет записана схема:
Генерация клиентского SDK
Зачем же нам нужна схема? Например мы можем сгенерировать полностью типизированное SDK для фронтенда на TypeScript. SDK состоит из четырех файлов:
Приведу кусок API.ts из рабочего проекта:
Весь этот код написан автоматически, это позволяет не отвлекаться на написание клиента и бесплатно получить подсказки названий полей и их типов, если ваша IDE это умеет.
Сгенерировать SDK тоже просто, нужно запустить утилиту asdkgen и передать ей путь до схем и путь, где будут лежать SDK:
Генерация документации
На генерации SDK я не остановился и написал генерацию документации. Документация достаточно простая, это обычный HTML с описанием запросов, моделей, ответов. Из интересного: для каждой модели есть сгенерированный код для JS, TS и Swift:
Заключение
Данное решение уже долгое время используется в production, помогает поддерживать старый код, писать новый и не писать код для клиента.
Многим и статья и сам фреймворк может показаться очень очевидным, я это понимаю, другие могут сказать, что такие решения уже есть и даже ткнуть меня носом в ссылку на такой проект. В свою защиту я могу сказать только две вещи:
Если кому-то все вышеперечисленное понравилось — добро пожаловать в GitHub.
Глава 5
Основы Серверного JavaScript
В этой главе даются основы серверной функциональности и различия между клиентским и серверным JavaScript. Здесь показано, как внедрять серверный JavaScript в HTML-файлы. Обсуждается, что происходит во время прогона программы на клиенте и на сервере, чтобы Вы могли разобраться во всём этом. В главе описано, как использовать JavaScript для изменения HTML-страницы, отправляемой клиенту, и, наконец, как распределить информацию между серверными и клиентскими процессами.
В главе имеются следующие разделы:
Серверный JavaScript имеет то же ядро языка, что и клиентский JavaScript, с которым Вы, возможно, уже знакомы. Задачи, выполняемые Вами при запуске JavaScript на сервере, несколько отличаются от задач, выполняемых при работе JavaScript на клиенте. Разные окружения и задачи обращаются к различным объектам.
Что Делать и Где
Клиентская среда (браузер) является передним краем работы приложения. В этой среде, к примеру, Вы отображаете HTML-страницы в окне и обслуживаете истории сессий HTML-страниц, отображаемых в браузере в течение сессии. Объекты этой среды, следовательно, обязаны иметь возможность манипулировать страницами, окнами и историей.
По контрасту, в серверной среде Вы работаете с ресурсами сервера. Например, Вы можете установить соединение с реляционной базой данных, распределить информацию между пользователями приложения или манипулировать файловой системой сервера. Объекты этой среды обязаны иметь возможность манипулировать реляционной БД и файловой системой сервера.
Кроме того, HTML-страница не отображается на сервере. Она запрашивается на сервере для отображения на клиенте. Запрошенная страница может содержать клиентский JavaScript. Если запрошенная страница является частью приложения JavaScript, сервер может генерировать эту страницу «на лету».
При разработке приложения JavaScript помните о разнице между клиентской и серверной платформами. Различия показаны в следующей таблице.
Таблица 5.1 Сравнение Клиента и Сервера
Серверы обычно (хотя и не всегда) являются высокопроизводительными рабочими станциями с быстрыми процессорами и возможностью хранения больших объемов информации.
Клиенты часто (хотя и не всегда) являются настольными системами с маломощными процессорами и относительно небольшим объемом хранимых данных.
Серверы могут быть перегружены при одновременном доступе тысяч клиентов.
Предварительная обработка данных на клиенте также может уменьшить требования к пропускной способности сети, если клиентское приложение может компоновать данные.
Обычно имеются разные пути распределения приложения между сервером и клиентом. Некоторые задачи могут выполняться только на клиенте или только на сервере; другие могут выполняться на любом из них. Хотя нет какого-то определённого способа определить, что и где делать, Вы может следовать следующим общим правилам:
Использовать серверный процессинг (тэг SERVER ) для следующих задач:
Служба JavaScript Session Management Service предоставляет объекты для сохранения информации, а клиентский JavaScript преходящ. Клиентские объекты существуют, пока пользователь имеет доступ к странице. Серверы могут объединять информацию от многих клиентов и многих приложений и могут сохранять большие объёмы данных в базе данных. Важно помнить обо всех этих характеристиках при распределении функциональности между клиентом и сервером.
Обзор Процессов Времени Прогона (Выполнения)
После того как Вы установили и стартовали приложение JavaScript, пользователь может получить к нему доступ.
Базовые процедуры таковы:
Рисунок 5.1 иллюстрирует это процесс.
Рисунок 5.1 Процессинг запроса JavaScript-страницы
Конечно, пользователь обязан иметь Netscape Navigator (или иной браузер с возможностью выполнения JavaScript), чтобы клиент мог интерпретировать операторы клиентского JavaScript. Аналогично, если Вы создаёте страницу, содержащую серверный JavaScript, он должен быть установлен на Netscape-сервере, чтобы нормально функционировать.
Например, предположим, клиент запрашивает страницу с таким исходным кодом:
Add a New Customer
Note: All fields are required for the new customer