Час написати бекенд на TypeScript? Що таке T3 Stack
Я — FullStack-розробник, який вже декілька років працює з React, TypeScript та Laravel. У фронтенд-розробці я часто використовую TS і я обожнюю типи. Так, Laravel мені подобається, проте думка про написання бекенду на TypeScript мене не покидає вже давно. Недавно я побачив create-t3-app (пакет, який дозволяє швидко розгорнути T3 Stack) і задумався, що час спробувати щось нове. Час написати бекенд на TS.
Я вирішив зробити простий сайт, де я міг би перевірити зручність нового стеку за вечір чи два. Стаття знизу — це узагальнення мого досвіду й реакція на інструмент, не туторіал. Сам проєкт доступний на GitHub, якщо потрібно подивитися на код. Якщо хтось задумується про перехід на T3 Stack (або бажає використати якісь інструменти з цього стеку), надіюся, мій досвід вам допоможе.
Що таке T3 Stack
T3 Stack — це набір інструментів, популяризований розробником Theo. В його філософії лежить обов’язкова типізація всіх частин сайту. Автор стеку пропонує використовувати наступні інструменти для того, щоб досягти повного type safety (безпека за допомогою типізації):
- Для фронтенду використовується Next.js (надбудова над React) разом із TypeScript.
- tRPC — типізований бекенд, який передає свої типи до React. У випадку з Laravel тобі завжди потрібно писати вручну типи для будь-якого запиту до API. З tRPC це не потрібно, тому що і те, які дані запит може отримати, і те, які дані запит може віддати, ми отримуємо автоматично з того коду, який ми написали.
- Prisma — зв’язок з базою даних. Звичайно ж, типізований. Створив модель — у тебе автоматично створюється тип цієї моделі в твоєму проєкті.
- Tailwind — легкий спосіб стилізувати ваші макети. Мені особисто інструмент подобається, проте він необов’язковий. Можна встановити те, що вам зручно (правда, уже вручну).
- NextAuth.js — бібліотека для авторизації. Теж не обов’язково, проте має допомогти з доволі важливим завданням.
Сайт
Я зробив сторінку логіну, сторінку реєстрації та сам профіль. Три сторінки, щоб використати всі наявні інструменти й дати їм якусь оцінку. Увесь код ви можете знайти в репозиторії на GitHub, а демо — ось тут. Так виглядає фінальний сайт:
Початок роботи
По аналогії з create-react-app, ми використовуємо create-t3-app, щоб швидко почати роботу. Заходимо в папку з проєктами й запускаємо команду npx create-t3-app. Я вибираю всі доступні пакети: nextAuth, prisma, tailwind, trpc.
Ця команда створює нам початкову систему папок в проєкті:
Важливо! Жоден із інструментів, наявних тут, не каже вам, як потрібно структурувати проєкт. Я звик до Laravel, який точно визначає папки, в яких ви маєте зберігати ту чи іншу частину сайту, і це я вважаю недоліком. Проте для когось, хто має інші уявлення про структуризацію проєкту, це буде перевагою.
Зараз процес набагато швидший, ніж якби я намагався встановити це вручну й поєднати між собою. Нам залишається лише створити .env файл та додати туди змінні середовища (ось посилання на те, як згенерувати NEXTAUTH_SECRET):
Висновок. Напишіть npx create-t3-app і через декілька хвилин у вас буде декілька інструментів, які вручну потрібно було би встановлювати годинами. Головне, не забудьте вказати змінні середовища.
Фронтенд
Я вже працював з TypeScript, React та Tailwind, тому вирішив почати роботу з макетів. Для цього мені потрібно створити три сторінки: логін, реєстрація, профіль.
Важливо! Для того, щоб створити сторінки, в Next.js використовується особлива система, яка створює посилання на основі папок і файлів. Якщо ви плануєте зробити сторінку за посиланням /admin/dashboard, ви створюєте папку /src/pages/admin, а в ній файл dashboard.tsx. Ця система для мене незвична, проте зручна.
У нашому випадку система папок виглядатиме так:
Робота з макетами розпочинається з визначення головних кольорів та стилів та налаштування проєкту. Ми розміщуємо головні кольори у файл tailwind.config.js. Пишемо глобальні стилі в /src/styles/globals.css. Та створюємо псевдоніми для папки src/components (@components) і src (@src), щоб до них можна було легко дістатися з будь-якої частини проєкту.
Приємний бонус від Next.js. Aliases (псевдоніми) для часто використовуваних шляхів — необхідна частина сучасної розробки. Працюючи із create-react-app мені потрібно було встановлювати @craco/craco та craco-alias, щоб псевдоніми в tsconfig.json працювали під час білду. Next.js робить це за замовчуванням. Можна добавити «paths» в tsconfig.json і вони будуть працювати як псевдоніми без встановлення додаткових пакетів.
Я не буду надсилати кожен файл, який я створив. Надішлю лише один, щоб було уявлення, як виглядає проста сторінка в такому проєкті.
У проєкті встановлено два додаткових пакета для зручності: react-hook-form та @svgr/webpack (на відміну від create-react-app, цю бібліотеку в create-t3-app треба встановлювати вручну). <Button /> — це компонент, написаний вручну. Login.protected — це спосіб захистити сторінку й перенаправляти людей, які вже авторизовані. Next.js у своїй документації вказує це як один із можливих способів вирішення цієї задачі.
NextAuth.js дає доступ до зручної функції signIn(). Увесь процес логіну стандартними способами (наприклад, через GitHub) вимагає на стороні клієнта тільки цю функцію. Сесія оновлюється самостійно і автоматично заново рендерить додаток. Саму логіку перенаправлення треба написати вручну. У моєму випадку ця логіка схована в /src/middleware/guest.ts (будь-яку авторизовану людину ми перенаправляємо до профілю).
Отже, якщо порівнювати create-react-app та create-t3-app, то зручність розробки такого маленького додатку мало чим відрізняється. Так, доведеться звикнути до нового способу налаштовувати URL (я звик до React Router, наприклад). Так, деякі пакети, до яких ти звик, треба встановлювати вручну. Але, зрештою, це — просто React із новими можливостями.
База даних
T3 Stack не каже вам, яку базу даних треба встановити. При встановленні create-t3-app встановлює SQLite. Бажаєте MySQL чи MongoDB? Потрібно встановити самому. Для зв’язку з базою даних використовується Prisma. Теоретично, можна встановити іншу ORM, проте в нашому випадку продовжимо з нею.
Якщо ми вибираємо NextAuth.js при запуску проєкта, create-t3-app автоматично створює схему бази даних, необхідних для цієї бібліотеки:
Усе, що потрібно зробити розробнику — запустити команду npx prisma db push і схема автоматично згенерується. Зверну увагу, що цю команду не варто використовувати в реальному проєкті. Для цього в Prisma є міграції.
Якщо потрібно швидко глянути на базу даних, напишіть npx prisma studio. Цей інструмент дозволяє швидко перевірити, які моделі вже існують у проєкті, і дає можливість їх редагувати.
Prisma — типізований модуль і в цьому його головна перевага. Функцію на сайті він виконує ту саму, що й Laravel Query Builder, проте синтаксис, мені здається, менш зручний. З іншого боку, підказки, які виникають від типізації, безцінні. Лише подивіться, що відбувається, коли я намагаюся оновити користувача:
IDE (у моєму випадку VSCode) знає про те, які можливі значення приймає модель User. По досвіду з React я знаю, що це може зекономити години в досить великому проєкті. Особливо це спростить роботу будь-якому новому розробнику, який намагатиметься розібратися в коді. Мені здається, що в Laravel це не буде можливим ще багато років, а тут це існує вже.
Висновок. Prisma має новий синтаксис, до якого треба звикнути після Laravel. Але типізація й автопідбір точно вартує цього.
Авторизація
NextAuth.js в цьому стеку мене, щиро кажучи, розчарував. З самого початку я планував створити сторінку логіну (емейл та пароль) та сторінку реєстрації (ім’я, емейл та пароль). Мені здавалося, що це доволі поширений спосіб створення авторизації, проте NextAuth.js так не вважає.
По-перше, NextAuth.js не має метода signUp. Якщо ти хочеш такий метод, маєш сам його написати. На думку авторів бібліотеки має бути лише один метод: signIn. І якщо користувач залогінився, а його не існує в базі даних — то відразу ж і створи його. Не маю нічого проти такого підходу, проте звичний потік це зламало. Це змусило мене залишити лише одну сторінку — логін. А замість повноцінної реєстрації — сторінка, де ти дописуєш свої дані, якщо ти вперше залогінився.
По-друге, бібліотека має дуже слабку підтримку входу за допомогою емейлу й паролю (credentials). Автоматично вона не створює запис в базі даних (при тому, що при авторизації через GitHub цей процес відбувається автоматично). Тому це все у твоїх руках. І в мене не було бажання створювати самостійно систему пошуку користувача, порівняння та хешування паролів, тому я обрав варіант авторизації через GitHub. Цей варіант вимагав від мене лише 29 рядків коду, які я просто скопіював з документації:
Висновок. Бібліотека авторизації залишає бажати кращого. Якщо плануєте систему логіну через емейл і пароль, то буде складніше, ніж у Laravel. Хоча якщо використовувати способи авторизації, які тебе дуже просять використати, цей процес стає простим.
Ще є недолік, який варто згадати. Чомусь NextAuth.js не має функції, яка може оновити сесію на клієнті за твоїм бажанням. Коли користувач змінює дані про себе, логічно показати йому оновлений профіль, проте в бібліотеці стандартними способами це неможливо зробити. Мені в проєкті довелося створити функцію reloadSession самостійно, яку я успішно запозичив із StackOverflow.
Бекенд
tRPC — це зручний інструмент, який замінює звичний REST API. Зв’язок із бекендом стає набагато простішим. По-перше, тому що є типізація на сервері. По-друге, тому що ця типізація видна фронтенду автоматично, і для даних, які ми надсилаємо, і для даних, які ми отримуємо. Якщо ви не зрозуміли, про що я, то я спробую пояснити на прикладі.
До речі, tRPC можна використовувати разом із іншими фреймворками: Express, Fastify та іншими. Мені не доводилося це робити, проте я впевнений, що це важлива інформація для будь-кого, хто вже з ними працює.
У моєму додатку є форма, яка має оновлювати дані в профілі. І функція в tRPC з ім’ям profile.update за це відповідає:
У цій функції написано, що бекенд приймає { city: string, name: string } параметри (це валідація за допомогою zod), оновлює дані в моделі й нічого не вертає. Що розробник бачить, коли намагається викликати цю функцію? Перш за все, IDE турботливо вказує всі ендпоїнти, які є на бекенді. У тому числі, потрібний нам profile.update:
Вражаюче. Якщо ми збираємося надіслати цей запит, IDE допомагає нам зрозуміти, що саме ми можемо надіслати:
При вдалому запиті tRPC також показує той тип відповіді, який передбачається функцією. У нашому випадку void:
Висновок. Типізація бекенду і доступність цих типів під час розробки фронтенду — безцінна. Працюючи над React-додатком, який отримує дані від REST API, я часто писав типи самостійно. Проблеми виникали тоді, коли тип на фронті був або неповний (тому що я людина й міг помилитися або не знати всіх можливих відповідей), або він змінювався (тому що бекенд змінюється й змінюються задачі). Із tRPC ця проблема вирішується, тому що будь-яка зміна на бекенді автоматично відображається на фронтенді. Як результат, менше багів і швидша робота з API на проєкті.
Узагальнення
T3 Stack — це чудова можливість почати розробку з TS. І так, я знаю, це не простий стек технологій (знання TypeScript є однією з найбільшою перепон). Мій перехід з PHP-бекенду на TS-бекенд не пройшов так просто, як я би хотів. Напевне, це не буде просто й для вас. Я очікував, що поширені задачі типу авторизації можна буде зробити без проблем. Проте деякі аспекти розробки на нових технологіях не настільки очевидні, щоб розібратися в них без підготовки.
Незважаючи на це, T3 Stack та інструменти в ньому пропонують можливості, які будуть недоступні на Laravel роками. Як фулстак-розробник я особливо ціную, наскільки легшою може стати розробка, де обмін типами між фронтендом і бекендом є безперервним. Якщо за TypeScript майбутнє, то ці технології або технології, схожі на ці, точно будуть з нами. Варто на них звернути увагу.
79 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів