Сквозная типизация для API Next.js

После того как я некоторое время писал TypeScript (TS), я понял одну вещь: «Мы пишем TS, чтобы продолжать писать JS». Раньше я сыпал аннотациями типов ради типов, но это не работает. Что я должен делать, так это писать TS в одном месте, и поместить структуру, чтобы я мог вывести тип оттуда, где он мне нужен, без необходимости управлять большим количеством типов.

Чтобы увидеть разницу, вот как я бы написал обработчик API до и после применения новой структуры:

Этот пост — моя попытка извлечь то, что я хотел бы знать, когда начинал. Знания о TS применимы везде, где вы используете TS, но я буду использовать Next.js для представления идеи.

Прежде чем продолжить, шаг 1 — 2 довольно прост, если вы знакомы с TS — так что не стесняйтесь пропустить. Процесс набора текста начинается с шага 3.

1. Настройка сцены

Давайте подготовим сцену, создав репозиторий Next.js, который использует TS из коробки:

npx create-next-app@latest --ts
Войти в полноэкранный режим Выйти из полноэкранного режима

После этого у вас будут эти файлы:

На момент написания этой статьи я использую Next.js 12.

Чтобы убедиться, что Next.js готов, запустите yarn dev и curl конечную точку api/hello, чтобы увидеть ее ответ. Когда вы остановите терминальную сессию yarn dev (используйте ctrl+c), curl больше не должен работать.

Теперь установим другие пакеты (мы объясним их применение по ходу дела):

yarn add zod http-status-code @sentry/nextjs
Вход в полноэкранный режим Выйти из полноэкранного режима

2. Использование абсолютного импорта

Откройте файл pages/api/hello.ts в vscode и добавьте этот оператор импорта, вы увидите красную загогулину:

TS пытался найти пакет @backend в node_modules, но не смог найти, отсюда и ошибка. Нам не нужно устанавливать пакет, потому что это будет папка, на которую мы будем ссылаться с помощью абсолютного импорта. Давайте установим его:

  1. Откройте ваш tsconfig.json и добавьте эти строки ниже compilerOptions:
"baseUrl": ".",
"paths": {
  "@api/*": [
    "pages/api/*"
  ],
  "@backend": [
    "backend"
  ],
  "@backend/*": [
    "backend/*"
  ]
},
Войти в полноэкранный режим Выйти из полноэкранного режима

Далее создадим новую папку backend и создадим в ней файл index.ts:

Откройте файл pages/api/hello.ts снова, и красная тильда исчезла!

Основываясь на новых добавленных baseUrl и paths в нашем tsconfig.json, TS знает, какую папку найти, когда видит "@backend". Мы называем эту настройку «абсолютным импортом». Использование абсолютного импорта проще по сравнению с относительным импортом, где нам приходится использовать ../ или ../../ для доступа к файлам в родительских папках.

1) Поскольку мы добавили "baseUrl":"." и папка backend фактически находится в корневом каталоге проекта, мы можем сделать import {} "backend" и это все равно будет работать. Мы будем придерживаться "@backend", потому что (на мой взгляд) префикс @ помогает намекнуть, что это абсолютный импорт.

2) Мы также добавили абсолютный путь для "@api/*", к которому мы перейдем позже.

3. Добавляем файлы в папку backend

Откройте этот Github gist и скопируйте содержимое в соответствующий файл в папке backend:

Ваша папка backend должна выглядеть следующим образом:

Когда все будет готово, запустим проверку типов, чтобы убедиться в отсутствии ошибок:

yarn tsc --noEmit
Вход в полноэкранный режим Выход из полноэкранного режима

Мы используем --noEmit, чтобы указать TS запустить проверку типов без фактической компиляции файлов в JS.

4. Давайте посмотрим типы!

Откройте файл pages/api/hello.ts и обратите внимание, что Next.js добавил тип Data для ответа JSON. Если вы передадите неправильную форму для параметра, TS будет жаловаться:

Попробуйте сохранить файл при наличии красной волнистой линии и запустите проверку типа (yarn tsc --noEmit):

Вы видите, что проверка типа не прошла, потому что возникла ошибка. Это один из способов использования TS для предотвращения попадания случайных ошибок в продакшн. Например, мы можем запускать проверку типов автоматически (например, с помощью Github Action) для каждого коммита и предотвращать слияние коммита в main, если проверка не прошла.

Теперь мы знаем, что Next.js добавил тип для данных ответа. Но что, если мы хотим задать тип и для запроса? Откройте этот gist и скопируйте содержимое в pages/api/greeting.ts:

Вот как мы читаем приведенные выше коды:

  • Строки 13-16: определяем функцию handler, которая является мясом нашего кода API. Мы используем ApiHandler, дженерик, который мы определили в backend/types.ts:25, для добавления типов к нашим объектам запроса и ответа на основе типа schema и response.

  • Строка 18: передайте handler нашей функции handle, которая автоматически проверит запрос на соответствие схеме. Это гарантирует, что все свойства, определенные в schema, будут доступны в handler. Например, он отменит запрос и вернет ответ об ошибке, если пользователь не предоставит name в параметрах запроса. Таким образом, нашему handler не придется заниматься ручной проверкой (например, проверять, не является ли name пустым).

Вот так! Теперь у нас есть структура для типизации нашего API. Мне нравится, как она начинается с декларативного стиля (объявление формы схемы и ответа) и продолжается императивным стилем (обработчик).

Когда у нас будет несколько файлов с похожей структурой, их будет легко просмотреть, потому что есть шаблон: форма схемы, форма ответа, обработчик. Обработчик тоже довольно тонкий, потому что ему не нужно заботиться о проверке данных.

В следующей части мы посмотрим, как мы повторно используем response, чтобы добавить типизацию в наш React-компонент. Мы также добавим структуру для тестирования бэкенда и фронтенда с помощью jest. Вот краткое описание:

Оставайтесь с нами!

Оцените статью
devanswers.ru
Добавить комментарий