Как я развернул фулстек-приложение на "NestJS" с "Angular" в "Supabase" и "Vercel"
Я разработал небольшое фулстек-приложение в качестве примера REST + WebSockets бойлерплейта на NestJS и Angular. В приложении используется PostgreSQL для хранения данных, Redis для кэширования и Minio для работы с файлами. Изначально целевой средой для развертывания был Kubernetes, но для ускорения разработки и тестирования MVP я решил использовать бесплатные облачные сервисы: Supabase для инфраструктуры и Vercel для деплоя бэкенда и фронтенда.
1. Проблемы
- Инфраструктура для разработки: Для локальной разработки необходимо поднимать
PostgreSQL,Redis,Minioи сервер авторизации (Authorizer.dev). Это занимает время и требует ресурсов. - Сложность деплоя в
Kubernetes: Настройка сборкиDocker-образов, процесс билда и деплоя вKubernetesзанимает много времени, особенно еслиDocker-образы имеют большой размер. - Ограниченный бюджет: Для небольших проектов или тестирования
MVPнет бюджета на выделенные серверы или полноценную инфраструктуруKubernetes.
2. Решение
Я решил добавить возможность деплоя приложения не только в Kubernetes, но и в бесплатные облачные сервисы, такие как Supabase (для баз данных, хранилища и авторизации) и Vercel (для деплоя бэкенда и фронтенда). Это позволило ускорить процесс разработки и тестирования, а также снизить затраты на инфраструктуру.
3. Исходное состояние
- Бэкенд:
NestJSсREST APIиWebSocketsдля рассылки событий (например, текущее серверное время). - Фронтенд:
Angularдля взаимодействия с бэкендом черезRESTиWebSockets. - Инфраструктура:
- Локально запущенные сервисы:
Authorizer.devдля авторизации,PostgreSQLдля базы данных,Redisд ля кэширования,Minioдля хранения файлов. - Деплой в
Kubernetesс использованиемDocker-образов для бэкенда и фронтенда. PostgreSQLи миграции запускались черезDocker Compose.E2E-тесты также запускались черезDocker Compose.
- Локально запущенные сервисы:
4. Текущее состояние
- Бэкенд и фронтенд: Работают локально, но инфраструктура полностью перенесена в
Supabase - Используемые облачные сервисы:
Supabase Auth: Заменен локальныйAuthorizer.dev.Supabase Database: Заменены локальныеPostgreSQLиRedis.Supabase Storage: Заменен локальныйMinio.
- Деплой: Бэкенд и фронтенд деплоятся на
Vercel. Из-заserverless-архитектурыVercelWebSocketsне работают, поэтому соответствующие тесты отключены. CI/CD: Сборка и деплой происходят черезGitHub Actions, включая применение миграций и запус кE2E-тестов.
5. Этапы реализации
5.1. Переход на Supabase Database
- Локальная
PostgreSQLзаменена наSupabase Database. - Для миграций использован
pg-flyway(мини-версияFlywayбезJava). Я не хотел отказываться от функционалаFlyway, но при этом не хотел ставитьJavaв момент деплоя приложения. В итоге я написал миниверсиюflyway—pg-flyway. - В
Supabaseнельзя создавать несколько баз данных, поэтому миграции запускаются в одной базе с использованием разных таблиц для учета миграций. Для решения этой проблемы при запуске мигратора можно передать название таблицы, где будет сохраняться информация о миграциях. - Один пользователь используется дл я всех баз данных, так как
Supabaseне позволяет создавать новых пользователей с правами на создание баз. Это ограничение потребовало пересмотра логики работы с базами данных.
5.2. Переход на Supabase вместо Redis
Redisзаменен наKeyvс поддержкойPostgreSQL. В рамках текущего проекта нет специфичных дляRedisзадач, поэтому я принял решение подменитьRedisна некую имплементацию.- В процессе поиска я увидел, что
CacheModuleдляNestJSпереходит на использованиеKeyv, и я написал свою оберткуnestjs-mod/keyv, которая поддерживает какRedis, так иPostgreSQL. - Это не полная замена, такая замена справедлива только в простых приложениях, где мы кэшируем часть данных.
5.3. Переход на Supabase Storage
Minioзаменен наSupabase Storage.- Основные изменения касались логики формирования ссылок для загрузки файлов. В отличие от
Minio, вSupabaseбакеты и политики создаются черезGUI, что немного усложняет автоматизацию (я только такой способ смог найти). - Из проблем в коде, была проблема в том, что
FilesModuleбыл жестко связан сMinio, пришлось связь разорвать и создать конфигурацию для переопределения методов с уровня приложения через интеграционную конфигурацию.
5.4. Переход на Supabase Auth
- Локальный
Authorizer.devзаменен наSupabase Auth. - Проблемы начались сразу, так как ранее
AuthModuleбазировал свою логику полностью на логиках и коде для работы сAuthorizer. - Написан новый
NestJS-модуль для работы сSupabase Auth, совместимый с предыдущей реализацией. Этот модуль был написан не с нуля, а путем копирования кода существующегоnestjs-mod/authorizer. - Сейчас этот новый модуль лежит в этом проекте, но в дальнейшем он будет перенесен в
nestjs-mod/nestjs-mod-contrib. Просто у меня сейчас по работе возникло слишком много проблем с готовыми серверами авторизации, и нужно написать свою кастомную реализацию. Когда она будет написана и протестирована на обратную совместимость сSupabaseиAuthorizer, тогда и появится реализацияSupabaseв публичномnpm-пакете.
5.5. Деплой на Vercel
- Эта штука сожрала очень много времени, я не буду описывать все проблемы на пути, но их было очень много. Просто закину ссылку на примеры конфигураций, жалко, что я увидел их только недавно: примеры конфигураций
Vercel, а это мой конфиг дляvercel.json. - После того как вы настроите деплой на
Vercelи у вас естьe2e-тесты, часть из них будет падать с ошибками, так какVercelподнимает приложение на каждый запрос, и если приложение не оптимизировано подserverless, как мое текущее, то тесты будут падать из-за долго отвечающего сайта. Проблемы с долгим бэком я решил, просто увеличив таймаут ожидания в тестах.
5.6. Переменные окружения
- Это не такая уж и проблема, но она есть. Когда мы деплоим на собственный виртуальный сервер, то сервер — это безличная штука, которую можно снести в любой момент, и она не хранит в себе переменные окружения. Они все хранятся у нас в
CI/CD. - При использовании
VercelиSupabaseу нас появляются еще два места, где можно хранить переменные окружения, и нужно как-то так спроектировать деплой и запуск, чтобы учесть различные варианты. Над эту задачку я тоже много времени потратил.
5.7. Регистрация и авторизация в облаках
- Не стану описывать регистрацию в сервисах
SupabaseиVercel. Просто приложу небольшое видео о том, как создавать приложения наSupabaseи прописать переменные окружения вVercel.
6. Инструкция по запуску локального кода с внешней инфраструктурой на Supabase
6.1. Инициализация
git clone git@github.com:nestjs-mod/nestjs-mod-fullstack.git
cd nestjs-mod-fullstack
npm i
cp ./example-supabase.env ./.env