Type-Safe Fastify¶
Добро пожаловать в заключительную главу этой книги! В этой главе мы рассмотрим, как встроенная в Fastify поддержка TypeScript может помочь нам писать более надежные и поддерживаемые приложения.
Безопасность типов — важнейший аспект современной разработки программного обеспечения. Она позволяет разработчикам выявлять ошибки и недочеты на ранних этапах процесса разработки, сокращая время и затраты на отладку и тестирование. Используя TypeScript с Fastify, вы можете воспользоваться преимуществами безопасности типов во время компиляции и избежать непредвиденных ошибок во время выполнения, что приведет к созданию более стабильных и надежных приложений.
Использование TypeScript с Fastify также улучшает работу разработчиков, обеспечивая лучшее завершение кода, вывод типов и документирование. Кроме того, Fastify обладает первоклассной поддержкой TypeScript, предоставляя все необходимое для создания надежных и масштабируемых приложений, включая интерфейсы и дженерики.
Более того, использование TypeScript делает развертывание более безопасным, поскольку позволяет отлавливать потенциальные ошибки и баги еще до запуска. Это обеспечивает дополнительный уровень защиты нашего кода, что дает нам больше уверенности при развертывании.
К концу главы мы научимся делать следующее:
- Создать простой проект Fastify на TypeScript
- Добавлять поддержку автоматического вывода типов с помощью так называемых провайдеров типов
- Автоматически генерировать сайт документации для наших API
Технические требования
Чтобы следовать этой главе, вам понадобится следующее:
- Рабочая установка Node.js 18
- VS Code IDE
- Git-репозиторий — рекомендуется, но не обязательно.
- Терминальное приложение
Код проекта можно найти на GitHub.
В следующем разделе мы подробно рассмотрим настройку проекта Fastify на TypeScript, добавив все зависимости, необходимые для его работы.
Создание проекта¶
Создание нового проекта Fastify с поддержкой TypeScript несложно, но требует добавления некоторых дополнительных зависимостей. В этом разделе мы рассмотрим процесс ручной настройки нового проекта Fastify с TypeScript и установки необходимых зависимостей.
Начнем с файла package.json
, конфигурационного файла для проекта Node.js. Он содержит информацию о зависимостях, точке входа и скриптах. Ниже приведен лишь частичный фрагмент, поскольку мы будем развивать его в ходе изучения разделов этой главы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Файл package.json
, указанный в предыдущем блоке кода, включает в себя базовые зависимости, необходимые для проекта, и два скрипта, которые улучшат опыт разработки. Как мы уже говорили, чтобы добавить поддержку TypeScript, нам нужно добавить в проект несколько дополнительных зависимостей для разработки, помимо Fastify.
Вот разбивка зависимостей для разработки:
- Поле
main
([1]
) определяет точку входа приложения, когда оно запускается командойnode .
из корня проекта. @types/node
([2]
) — это зависимость разработки, которая предоставляет определения типов TypeScript для API Node.js. Она нужна нам для использования глобальных переменных, поставляемых в среде выполнения Node.js.fastify-tsconfig
([3]
) предоставляет предварительно настроенную конфигурацию TypeScript для использования с фреймворком Fastify. Мы можем расширить нашу конфигурацию из нее и иметь удобные настройки по умолчанию уже из коробки.tsx
([4]
) добавляет инструмент выполнения TypeScript для наблюдения и повторного запуска сервера при изменении файлов. Он построен на базе Node.js и имеет политику нулевой конфигурации.- Наконец, зависимость разработки
typescript
([5]
) добавляет компилятор TypeScript для проверки определений типов и компиляции проекта в JavaScript. Мы добавим файлtsconfig.json
в корень проекта, чтобы он работал правильно.
Перейдя в секцию scripts
файла package.json
, мы получим следующее:
build
([6]
) — это скрипт, который удаляет существующую папкуdist
и вызывает компилятор TypeScript (tsc
).- Сценарий
dev
([7]
) запускает инструмент командной строкиtsx
для повторного запуска приложения при внесении изменений в файлы проекта. Запуск файлов TypeScript напрямую удобен во время разработки, поскольку позволяет ускорить цикл разработки.
Мы готовы создать файл tsconfig.json
в корневой папке проекта. Этот файл конфигурации превратит наш Node.js-проект в TypeScript-проект.
Добавление файла tsconfig.json
¶
Файл tsconfig.json
является конфигурационным файлом для компилятора TypeScript, и он предоставляет возможность указать опции и настройки, которые управляют тем, как код компилируется в JavaScript. По этой причине, как мы видели в предыдущем разделе, команда Fastify поддерживает пакет fastify-tsconfig
npm с рекомендуемой конфигурацией для плагинов и приложений Fastify, написанных на TypeScript.
В фрагменте кода tsconfig.json
мы можем увидеть, как его использовать:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Давайте проанализируем конфигурацию:
- Во-первых, мы используем свойство
extends
([1]
) для расширения изfastify-tsconfig
. Этот пакет предоставляет рекомендуемую конфигурацию для веб-приложений Fastify, построенных на TypeScript. compilerOptions
([2]
) настраивает компилятор TypeScript на размещение скомпилированных JavaScript-файлов в директорииdist
. По этой причине ранее мы настроили точку входа приложения наdist/server.js
, используя поле main в файлеpackage.json
.- Поскольку мы разрабатываем приложение, наш код будет выполняться, а не потребляться как библиотека. Поэтому мы устанавливаем опцию
declaration
вfalse
([3]
), поскольку нам не нужно, чтобы компилятор генерировал файлы определения типов (*.d.ts
). - С другой стороны, мы хотим, чтобы компилятор генерировал файлы карт исходного кода (
*.map
), которые сопоставляют скомпилированный JavaScript-код с исходным кодом TypeScript ([4]
). Это полезно для понимания ошибок во время выполнения и отладки, поскольку позволяет нам устанавливать точки останова и переходить к исходному коду TypeScript. - Наконец, при компиляции исходного кода мы хотим включить все файлы с расширением
.ts
в папкуsrc
и ее подпапки ([5]
).
Используя файл tsconfig.json
, разработчики могут гарантировать, что все члены команды используют одни и те же параметры конфигурации, обеспечивая стандартизированный способ настройки компилятора TypeScript на разных машинах.
Параметры компилятора TypeScript
TypeScript предлагает широкий спектр опций компилятора, которые можно указать в файле tsconfig.json
для управления поведением компилятора TypeScript. Эти опции включают в себя такие параметры, как версия целевого вывода, стратегия разрешения модулей, генерация карты исходников и генерация кода. Команда Fastify предлагает конфигурацию, которая подходит для большинства проектов. Более подробную информацию обо всех опциях можно найти в официальной TypeScript documentation.
Добавление точки входа приложения¶
Сначала нам нужно написать точку входа для нашего приложения. Обычный сервер Fastify с плагином автозагрузки загрузит наши маршруты и выполнит эту работу. Код прост, и мы можем посмотреть на него в следующем фрагменте:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Прежде чем перейти к коду, не забудьте запустить npm i
в корне проекта.
Теперь давайте проанализируем предыдущий фрагмент:
- Этот код создает сервер Fastify с включенным логом (
[1]
). Поскольку мы находимся внутри файла TypeScript, система типов включена. Например, если мы наведем курсор на переменнуюfastify
в редакторе VS Code, то увидим, что она имеет типFastifyInstance
. Более того, благодаря первоклассной поддержке языка TypeScript в Fastify, все полностью типизировано из коробки. - Далее регистрируется плагин, использующий
AutoLoad
для динамической загрузки маршрутов из каталогаroutes
. Методregister
возвращает объектPromise
, но нас не интересует его возвращаемое значение. Используяvoid
, мы явно указываем, что не хотим перехватывать или использовать возвращаемое значение объектаPromise
, и запускаем метод только для получения побочных эффектов ([2]
). - Затем запускается сервер на порту
3000
и прослушивается на предмет входящих запросов. Если при загрузке сервера возникает ошибка, он записывает ее в лог и завершает процесс с кодом ошибки ([3]
).
Теперь, когда мы определили точку входа, мы можем позаботиться о плагине root
.
Использование провайдеров типов Fastify¶
Провайдер типов Fastify — это пакет только для TypeScript, который упрощает определение JSON-схем, предоставляя аннотации типов и дженерики. Его использование позволяет Fastify выводить информацию о типах непосредственно из определений схем. Провайдеры типов позволяют разработчикам легко определять ожидаемые входные и выходные данные конечных точек API, автоматически проверять корректность типов во время компиляции и подтверждать данные во время выполнения.
Fastify поддерживает несколько провайдеров типов, таких как json-schema-to-ts
и TypeBox
. В проектах на TypeScript использование провайдера типов позволяет сократить объем кода, необходимого для проверки ввода, и снизить вероятность возникновения ошибок, связанных с некорректными типами данных. В конечном итоге это сделает ваш код более надежным, поддерживаемым и масштабируемым.
Для краткости в следующем примере мы рассмотрим только провайдер типов TypeBox
. Однако, поскольку выбор провайдера типов зависит от ваших личных предпочтений, мы рекомендуем вам попробовать другие провайдеры типов, чтобы найти наиболее подходящий:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
|
Сниппет кода показывает плагин Fastify, который использует @fastify/type-provider-typebox
для определения и проверки формы объектов запроса и ответа маршрутов.
Ниже приводится описание того, что делает этот код:
- Во-первых, мы импортируем
FastifyPluginAsyncTypebox
иType
из модуля@fastify/type-provider-typebox
([1]
).FastifyPluginAsyncTypebox
— это псевдоним типа дляFastifyPluginAsync
, который внедряет поддержку определений схем@sinclair/typebox
. - Плагин определяется как
async
функция, принимающая два аргумента:fastify
и_opts
. Благодаря явной аннотации типаFastifyPluginAsyncTypebox
([2]
), этот экземплярfastify
будет автоматически определять типы схем маршрутов. - Метод
fastify.get()
определяет маршрутGET
по корневому URL (/
). Мы используем импортированный ранее объектType
для создания объектаquerystring
, определяющего свойство name типаstring
, содержащее«world»
в качестве значения по умолчанию ([3]
). Кроме того, мы снова используем его, чтобы задать ответ в виде объекта с единственным свойствомhello
типаstring
([4]
). Для обоих типов автоматически будут определены типы TypeScript внутри обработчика маршрута. - При наведении курсора на переменную
name
([5]
) в VS Code будет показан типstring
. Такое поведение происходит благодаря провайдеру типов. - Обработчик маршрута возвращает объект с единственным свойством
hello
, установленным на значение свойстваname
, извлеченного из объектаquerystring
([6]
). Тип возвращаемой функции также определяется благодаряTypeBox
. В качестве упражнения мы можем попробовать изменить возвращаемый объект на{ hello: 2 }
, и компилятор TypeScript выдаст ошибку, поскольку мы присвоили число вместо строки. - Метод
fastify.post()
вызывается для определения POST-маршрута по корневому URL (/
). Схема маршрута включает объект body, который определяет необязательное свойство name типаstring
([7]
). Благодаря этому объявлению объектrequest.body
в обработчике маршрута снова является полностью типизированным ([8]
). На этот раз мы объявили свойствоrequest.body.name
необязательным. Прежде чем использовать его в возвращаемом объекте, нам нужно проверить, не является ли оноundefined
, и в противном случае установить его в строкуworld
([9]
). Как мы видели для другого обработчика маршрутов, возврат значения, несовместимого с объявлением схемы, приведет к ошибке компиляции.
На этом мы завершаем этот раздел. Благодаря провайдерам типов мы можем быстро достичь безопасности типов во всей кодовой базе, следуя этим указателям и не прибегая к явному объявлению типов:
- Во время выполнения схема JSON будет дезинфицировать входные данные и сериализовать выходные, делая наши API более надежными, безопасными и быстрыми.
- Во время компиляции наша кодовая база полностью проверяется на типы. Кроме того, каждая переменная из объявлений схемы имеет выведенные типы, что позволяет нам найти больше ошибок до развертывания приложения.
В следующем разделе мы рассмотрим, как можно автоматически сгенерировать сайт документации, соответствующий спецификации Swagger/OpenAPI.
Генерация документации API¶
Спецификация OpenAPI — это широко распространенный и открытый стандарт для документирования RESTful API. Она предоставляет формат для описания структуры и операций API в машиночитаемом формате, что позволяет разработчикам быстро понять API и взаимодействовать с ним.
Спецификация определяет набор файлов JSON или YAML, которые описывают конечные точки API, параметры, ответы и другие детали. Эта информация может быть использована для создания документации по API, клиентских библиотек и других инструментов, облегчающих работу с API.
Спецификации Swagger и OpenAPI
Swagger и OpenAPI — это две родственные спецификации, причем OpenAPI является более новой версией Swagger. Изначально Swagger был проектом с открытым исходным кодом, но позже спецификация была приобретена компанией SmartBear и переименована в OpenAPI. Сегодня спецификация поддерживается инициативой OpenAPI, консорциумом лидеров индустрии. Swagger также известен как OpenAPI v2, в то время как под OpenAPI обычно подразумевается только v3.
Fastify поощряет разработчиков определять схемы JSON для каждого маршрута, который они регистрируют. Было бы здорово, если бы существовал автоматический способ преобразования этих определений в спецификацию Swagger. И, конечно же, он есть. Но сначала мы должны добавить две новые зависимости в наш проект и использовать их в точке входа приложения. Итак, давайте установим плагины @fastify/swagger
и @fastify/swagger-ui
Fastify через терминальное приложение. Для этого в корне проекта введите следующую команду:
1 |
|
Теперь мы можем зарегистрировать два новых пакета в экземпляре Fastify в файле src/server.ts
. Оба пакета поддерживают спецификации Swagger и OpenAPI v3. Мы можем выбрать, какой из них генерировать, передав определенную опцию. Следующий фрагмент настраивает плагин на генерацию спецификации Swagger (OpenAPI v2) и сайта документации:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Этот фрагмент настраивает плагины swagger
и swagger-ui
для генерации определений спецификации и сайта документации. Вот разбивка кода:
- Плагин
@fastify/swagger
зарегистрирован. Мы передаем свойствоswagger
для генерации спецификаций для OpenAPI v2 ([1]
). - Мы определяем общую информацию о нашем API внутри объекта
swagger
, такую как его название, описание и версия, передавая их в свойствоinfo
([2]
).swagger-ui
будет использовать это для создания сайта с более подробной информацией. - Мы определяем массивы
consumes
иproduces
([3]
), чтобы указать ожидаемые типы содержимого запроса и ответа. Эта информация важна для пользователей API, и она поможет при тестировании конечных точек. - Мы определяем массив
tags
для группировки конечных точек API по тематике или функциональности. В данном случае существует только один тег с именемHello World
([4]
). В следующем фрагментеsrc/routes/root.ts
мы увидим, как группировать уже определенные маршруты. - Наконец, мы регистрируем плагин
@fastify/swagger-ui
, вызываяfastify.register(SwaggerUI, {...})
. Сгенерированная документация может быть доступна через веб-браузер по URL, указанному вroutePrefix
([5]
) (в данном случае/documentation
).
Далее мы изменим исходные определения маршрутов, чтобы улучшить автогенерируемую документацию. Мы хотим сделать это для лучшей группировки маршрутов в интерфейсе и более точных описаний.
В следующем фрагменте мы опустим те части, которые не относятся к делу, но полный код вы можете найти в файле src/routes/root.ts
в репозитории GitHub:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
Несмотря на то, что мы показали дополнения к коду для обоих маршрутов, мы разберем только первый, потому что структура второго такая же:
- Свойство
tags
([1]
) указывает, что маршрут относится к тегуHello World
, который мы определили при регистрации плагина@fastify/swagger
. Это свойство позволяет группировать связанные маршруты вместе в документации Swagger/OpenAPI. - Поле
description
кратко описывает, что делает маршрут ([2]
). Оно будет отображаться в верхней части документации Swagger. - В поле
ummary
кратко описывается, что делает маршрут ([3]
). Оно будет отображаться рядом с определением маршрута в документации. - Чтобы лучше понять параметры, принимаемые конечной точкой, мы можем добавить специальное описание (
[4]
). Оно будет отображаться в документации Swagger в деталях параметров.
Чтобы проверить все, что мы добавили в этом разделе, мы можем запустить наш сервер в режиме разработки (npm run dev
) и с помощью браузера перейти по адресу http://localhost:3000/documentation
. Перед нами откроется симпатичный веб-сайт, по которому можно перейти, чтобы узнать больше о разработанном нами приложении. Более того, на странице также интегрирован клиент, который мы можем использовать для осуществления реальных вызовов нашего API.
Резюме
В этой главе мы узнали, почему безопасность типов имеет решающее значение в современной разработке программного обеспечения и как использование TypeScript с Fastify может помочь выявить ошибки и недочеты на ранних этапах разработки. В главе также рассказывалось о том, как использование TypeScript с Fastify может улучшить работу разработчиков, обеспечив лучшее завершение кода, вывод типов и возможность автоматической генерации сайта документации для наших API.
Поздравляем вас с тем, что вы добрались до заключительной главы этой книги! На протяжении всего этого путешествия мы узнали о веб-фреймворке Fastify и о том, как он может помочь нам создавать высокопроизводительные веб-приложения.