Простейший пример «Привет мир!»
Требуемые знания и навыки
Здесь и далее подразумевается, что Вы уже умеете/знаете:
- Создавать Node.js-проект и устанавливать npm-зависимости (если нет, то Вам сначала следует изучить основы Node.js).
- Настраивать компилятор языка TypeScript (фреймворк Yamato Daiwa Backend (далее: YDB) предполагает использование языка TypeScript, причём без типа
any
). - Основы Объектно-Ориентированного Программирования (ООП) — предлагаемые Yamato Daiwa Backend подходы предполагают широкое использование ООП. Хотя YDB не навязывает конкретной архитектуры, использование ООП желательно, а потому будет в следующих уроках.
Развёртывание Node.js-проекта
Создайте новый Node.js-проект и установите приведённые ниже npm-зависимости. Во избежание проблем, связанных с меняющимся программным интерфейсом по мере выхода новых версией этих зависимостей, в рамках данного урока настоятельно рекомендуется устанавливать версии, указанные в квадратных скобках.
- @yamato-daiwa/backend [0.4.0]
- Главный npm-пакет данного фреймворка
- typescript [5.8.3]
- Основной npm-пакет языка TypeScript.
- tsx [4.20.3]
- Инструмент для выполнения кода, написанного на языке TypeScript без записи на диск выходных JavaScript-файлов.
npm i @yamato-daiwa/backend@0.4.0
npm i typescript@5.8.3 tsx@4.20.3 -D
Код
Это примитивное серверное приложение отправит HTML-код, состоящий из заголовка h1
с текстовым содержимым «Hello, world!»
в ответ на HTTP-запрос типа GET по адресу http://127.0.0.1:80/
. Вообще, такой код не является валидной HTML-страницей, но в тестовых целях отправка одного только тела HTML-документа возможна — современные браузеры его отобразят. Но перед тем, как отправить запрос, разберём код данного простейшего примера.
import {
Server,
Request,
Response,
ProtocolDependentDefaultPorts,
HTTP_Methods
} from "@yamato-daiwa/backend";
Server.initializeAndStart({
initializeAndStart
класса Server
принимает объект конфигурации в качестве первого и единственного параметра. Перевод имени метода с английского языка «инициализировать и запустить» вкупе с контекстом Server
(«сервер») не должен оставлять вопросов о том, что этот метод делает. IP_Address: "127.0.0.1",
IP_Address
сделано обязательным не случайно, потому что Вы как инженер обязаны понимать, по какому IP-адресу хотите сделать доступным своё серверное приложение. Если говорить о реальном проекте, то IP-адрес будет зависеть от среды (локальная разработка, инсценирование, продакшен). В будущих уроках мы обсудим, как эту опцию сделать зависимой от среды, а сейчас установим значение 127.0.0.1 — первый адрес в диапазоне частных IP-адресов. HTTP: { port: ProtocolDependentDefaultPorts.HTTP },
ProtocolDependentDefaultPorts.HTTP
— это номер порта по умолчанию для протокола HTTP. Указывать его явно нужно по той же причине, что и IP_Address
, однако поскольку полагаться на человеческую память опасно, номера портов по умолчанию для разных протоколов были помещены в перечисление ProtocolDependentDefaultPorts
, а от Вас лишь требуется осознавать, на порту по умолчанию будет доступно приложение или же на каком-то другом. routing: [
{
HTTP_Method: HTTP_Methods.get,
pathTemplate: "/",
http://127.0.0.1:80/
. Через pathTemplate
необходимо указать часть URI, которая идёт после IP-адреса и порта — в нашем случае это просто /
(«корень»). Вообще говоря, сейчас речь идёт о пути, но URI может иметь и другие части после пути — сейчас мы в этом вдаваться не будем, чтобы не перегружать урок. На данный момент важно, что если мы отправим запрос по другому пути, например /foo
(соответствует URI http://127.0.0.1:80/foo
) или /bar/baz
(соответствует URI http://127.0.0.1:80/bar/baz
), сервер отправит ответ с ошибкой «не найдено». async handler(request: Request, response: Response): Promise<void> {
handler — обработчик запроса — определяет, какие действия нужно выполнить, когда отправленный запрос удовлетворяет указанной комбинации HTTP-метода и пути. Эта функция априори асинхронная (явно или неявно возвращает экземпляр Promise
, хотя с точки зрения ECMAScript
это не совсем точное определение, так как функции, получающие через параметры коллбэки — функции для их вызова по завершению какого-либо процесса — тоже называют «асинхронными»), так как отправка ответа, которую надо в итоге сделать, является асинхронной по своей природе. К тому же, в обработчике запроса часто выполняются и другие асинхронные операции, например обращение к базе данных или работа с файлами.
Обычно ответ формируется в зависимости от запроса, экземпляр которого представлен первым параметром. Например, в случае GET-запроса нам могут понадобиться query parameters (дословно это означает «параметры запроса», но без контекста — URI — этот перевод может сильно запутать), а в случае POST- или PUT-запросов — доступ к данным, содержащимся с теле запроса. Однако работу с экземпляром класса Response мы отложим на следующие, более сложные уроки, а в этом простейшем примере будем всегда отправлять один и тот же ответ, потому первый параметр не будет использован.
return response.submitWithSuccess({ HTML_Content: "<h1>Hello, world!</h1>" });
HTTP-ответ имеет статус, идентифицируемый трёхзначным шифром. Сейчас важно, что эти статусы разделяются на 5 групп:
- Информационные
- Успешные
- Перенаправления
- Клиентские ошибки
- Серверные ошибки
Метод submitWithSuccess
класса Response
отправляет ответ с кодом из «успешного» диапазона (по умолчанию — 200
). Указывая свойство HTML_Content
первого и единственного параметра метода submitWithSuccess
, мы указываем фреймворку, что хотим отправить HTML-код, в связи с чем фреймворк автоматически установит нужные HTTP-заголовки.
}
}
]
});
Тестирование
Запустим приложение с помощью tsx.
tsx EntryPoint.ts
Если в Вас в консоли появилось сообщение о том, что утилита tsx не найдена, то помимо глобальной установки этой утилиты можно в package.json добавить скрипт наподобие "start": "tsx EntryPoint.ts"
и запустить его:
npm run start
Если в коде нет ошибок, то Вы будете проинформированы об успешном запуске приложения следующим выводом в терминал:

Откройте указанный URI в браузере. Если Ваш терминал распознаёт ссылки, то Вы можете по ней перейти кликом мыши. В терминале отобразится как минимум один лог о новом запросе по корневому маршруту:

Возможно, будет логирован ещё один запрос по маршруту /favicon.ico
:

Если такой лог присутствует, значит соответствующий запрос был отправлен браузером автоматически, чтобы отобразить иконку на вкладке браузера. Если бы фреймворк не был готов к такому запросу, то отправил ответ с ошибкой «не найдено», однако фреймворк YDB по умолчанию отправляет свою иконку. Разумеется, её можно заменить на другую, что мы и сделаем в одном из будущих уроков.
Поскольку localhost является «местоимением» для IP-адреса 127.0.0.1
, а 80 — порт по умолчанию для протокола HTTP, то помимо http://127.0.0.1:80/
мы можем отправлять запросы на http://localhost
, http://localhost:80
или http://127.0.0.1:80/
.
Наконец, посмотрим как фреймворк поведёт себя, когда будет отправлен запрос, обработка которого не прописана в routing
. Например, для запроса http://127.0.0.1:80/foo
лог будет таким:

Фреймворк сообщил, что запрашиваемый ресурс не найден. Аналогичное сообщение должен показать браузер — на основании отправленного статуса ответа 404 Not found.