JS: REST API (Fastify)
Теория: Code First vs API First
Существует два основных подхода к разработке API: Code First и API First. В этом уроке мы рассмотрим особенности каждого из них, их преимущества и недостатки, а также ситуации, в которых каждый подход наиболее уместен.
Что такое Code First?
Подход Code First предполагает, что разработка начинается с написания кода сервера или приложения. То есть, разработчики сначала пишут код, который реализует бизнес-логику, и только потом, на основе этого кода, формируют описание API, например, документацию или спецификацию OpenAPI.
В Fastify для этого есть официальный плагин. С его помощью, при правильном описании маршрутов в Fastify получить нужную документацию для клиентов. Для визуализации этой документации подключается другой, но тоже официальный плагин. Выглядит это так:

Основным преимуществом подхода Code First является возможность быстро приступить к разработке без необходимости тратить время на предварительное планирование API. Этот подход хорошо работает, когда API делается для себя и своей команды.
Когда людей становится больше и добавляются отдельные команды фронтенда или мобильного приложения, появляется необходимость интегрироваться с внешними клиентами и системами. В таких ситуациях может оказаться что API спроектирован плохо, а командам надо ждать его разработчиков, пока они реализуют конкретные эндпоинты и предоставят документацию по ним. В этом случае подход Code First начинает тормозить развитие.
Что такое API First?
Подход API First ставит API в центр процесса разработки. Перед началом реализации пишется спецификация API (в формате OpenAPI для REST API), которая описывает эндпоинты, методы, параметры и структуры данных. Эта спецификация служит основой для разработки как серверной, так и клиентской части приложения. Таким образом, API становится своего рода контрактом между различными частями системы и внешними клиентами.
API-First подход не требует от разработчиков полного описания всего API до старта разработки. Спецификация может заполняться по мере реализации новых API, но до того, как начнет писаться код.
При таком подходе, необходимое API можно спроектировать и заложить в систему очень быстро, не дожидаясь пока бекенд его реализует. А разнообразные решения вокруг OpenAPI помогут на базе этого API сгенерировать нужную SDK и даже мок-сервер, отвечающий по этому API, что удобно для разработки и тестирования.
Когда какой подход использовать?
Code First подходит на этапе прототипирования и для приложений, где нет внешних команд и сервисов. Например, если вы разрабатываете API для своего собственного фронтенда, которым же и занимаетесь сами. С ростом приложения, добавлением людей и появлением внешних потребителей API, переход на API-First подход необходим.
В этом курсе мы будем практиковаться в API-First подходе, попутно изучая лучше практики и инструменты для генерации.
Typespec
Создание OpenAPI спецификации напрямую, довольно утомительное занятие. Придется много дублировать и поддерживать файлы, которые растут в размерах с огромной скоростью. По этой причине, разработчики стараются описывать API каким-то более простым способом, с помощью которого можно генерировать OpenAPI спецификацию. Одним из таких способов является инструмент TypeSpec, созданный Microsoft.

TypeSpec это специализированный язык (DSL), для описания API (не только REST). Он похож на TypeScript, но это лишь визуальное сходство, TypeSpec не полноценный язык программирования. Это ограниченное описание, которым вы научитесь пользоваться буквально пройдя только этот урок и немного почитав документацию.
Даже по скриншоту выше видно насколько описание на TypeSpec более компактное, чем OpenAPI спецификация, не поместившаяся на экран. Чем больше спецификация, тем больше становится эта разница.
Станет ли TypeSpec массовым инструментом покажет время, но мы в него поверили по нескольким причинам:
- Его создал Microsoft, который сделал много полезного в области общих открытых решений. Например, LSP или TypeScript.
- Сам подход достаточно удачный. Текстовое описание, которое может генерировать не только OpenAPI схему, но многое другое, например, JSON Schema и Protobuf.
- Так как это отдельный язык, то в него легче внедрить правила, проверяющие согласованность разных элементов, чем если бы это было реализовано в виде библиотеки на TypeScript (или любом другом языке)
Попробуйте поиграть с этим инструментом, до того как мы начнем с ним работать.
Установка и настройка
Установить TypeSpec можно с помощью npm. Выполните следующую команду внутри нашего проекта:
Затем инициализируйте проект:
Это создаст базовую структуру файлов проекта TypeSpec, включающую конфигурационный файл tspconfig.yaml, где вы сможете настроить необходимые параметры, такие как пути к моделям и конфигурации для генерации OpenAPI спецификации. Описание нашего API, по умолчанию, выполняется в файле main.tsp.
main.tsp
tspconfig.yaml
package.json
node_modules/
tsp-output/
@typespec/
openapi3/
openapi.yaml
По умолчанию tsp генерирует yaml, но нам для будущих уроков понадобится json формат. Поэтому мы поменяем оригинальный конфигурационный файл на такой:
Пример
Описание кода:
- Импорт позволяет добавить необходимые элементы для работы: модели и декораторы
- Декоратор
@serviceу неймспейсаFastifyRestApiExampleговорит о том, что дальше будет идти описание маршрутов REST API с декоратором@route. - Модель определяет структуру данных, которую потом мы будем использовать в качестве типа данных для входящего запроса и возвращаемого ответа.
- Внутри неймспейса
usersопределяются маршруты, для части /users. Каждая эндпоинт описывается как конструкцияop, где декоратор определяет метод, а входные и выходные данные указываются в конструкции похожей на определение метода. Внутри скобок то что приходит снаружи. Внутри тела то, что отдается наружу. Тут можно указывать любые элементы HTTP. Постепенно мы познакомимся со всеми основными элементами.
Преобразование описания в спецификацию OpenAPI выполняется следующей командой:
Сравните получившийся файл с описанием выше, тогда станет лучше понятно как эти представления связаны:
Prism (Мок-сервер)
Запустим мок-сервер, установленный выше и сделаем пару запросов к нему. Посмотрим на спецификацию в действии.
Prism не просто запустил ее, но и добавил немного данных для удобства. Выполним запрос на каждый из урлов:
Обратите внимание на упаковку коллекции в объект с ключом data при запросе списка. Это делать не обязательно, но подобная структура помогает расширять ответ метаданными, например, количеством страниц или общим числом найденных записей. Иначе такие данные можно будет передать только в заголовках.
Модели
Модели в TypeSpec представляют собой описание структуры данных, которые будут использоваться в вашем API. Эти модели могут включать различные типы данных, ограничения на поля и связи между разными объектами. Поэтому одна и та же модель может использоваться для генерации API спецификаций и других схем, таких как JSON Schema или Protobuf.
Не обязательность VS null
Как и в TypeScript, TypeSpec поддерживает необязательные данные. Для этого к ним в конце добавляется вопросительный знак:
В этом примере fullName может отсутствовать во входном JSON. Но это не тоже самое, что оно равно null. Модель выше не допускает наличие null. Чтобы это сделать, нужно добавить тип через | (или).
В таком случае свойство должно присутствовать, но оно может быть null. Оба этих подхода можно комбинировать. Конкретная реализация зависит от требований конкретного эндпоинта.
Валидация
Для примера добавим валидацию в модель User
- Поле
fullNameтеперь должно содержать не менее 2 и не более 100 символов. - Поле
emailдолжно соответствовать формату электронной почты.
После очередной компиляции, спецификация примет такой вид:
Сокращения дублирования
Когда моделей становится много, то возникает дублирование полей, которые имеют постоянный формат, например даты создания или обновления. TypeSpec как язык, содержит синтаксический сахар, для удобного повторного использования кода. Например, так:
Ошибки
Модель в TypeSpec это универсальная конструкция, описывающая любые возвращаемые и принимаемые данные. С ее помощью описываются, например, ошибки. В таком случае к моделям добавляется декоратор @error.
И чтобы не дублировать, мы можем указать код ответа прямо внутри модели через декоратор @statusCode. Пример того, как ошибки могут использоваться:
Здесь через или (|) мы говорим что, либо возвращается тело с данными User, либо ошибка NotFoundError, которая тоже может содержать свои данные.
CRUD
Для полной картины, соберем полный CRUD пользователя
Из интересного посмотрите на подвиды модели User. В каждой операции (эндпоинте) используется свое подмножество данных со своими правилами. Унифицировать такую структуру практически невозможно и не нужно.
Рекомендуемые программы
Завершено
0 / 15
