Предыдущая статья: Конвертация даты по временной зоне пользователя в "NestJS", а также ввод и отображение даты в "Angular"
Я разработал небольшое фулстек-приложение в качестве примера 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
-архитектурыVercel
WebSockets
не работают, поэтому соответствующие тесты отключены.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
6.2. Подготовка
Создать организацию и проект на Supabase
Создать
bucket
с названием "images" в хранилище (пример ссылки: https://supabase.com/dashboard/project/XXX/storage/buckets)Создайте новые "S3 Access Keys" с "Access key ID" и "Secret access key" (пример ссылки: https://supabase.com/dashboard/project/gustcjgbrmmipkizqzso/settings/storage)
-
Откройте
.env
и заполните пустые значения# https://supabase.com/dashboard/project/XXX/settings/api - API Settings - Project URL - URL SUPABASE_URL=empty_value # https://supabase.com/dashboard/project/XXX/settings/database?showConnect=true - Connection String - Direct connection POSTGRES_URL=empty_value # https://supabase.com/dashboard/project/XXX/settings/api - API Settings - Project API Keys - anon public SUPABASE_ANON_KEY=empty_value # https://supabase.com/dashboard/project/gustcjgbrmmipkizqzso/settings/storage - S3 Access Keys - New access key - Access key ID SERVER_MINIO_ACCESS_KEY=empty_value # https://supabase.com/dashboard/project/gustcjgbrmmipkizqzso/settings/storage - S3 Access Keys - New access key - Secret access key SERVER_MINIO_SECRET_KEY=empty_value
-
Создайте и заполните все необходимые новые ключи окружения
npx --yes tsx update-files-for-vercel.ts
6.3. Запуск
npm run pm2-supabase-full:dev:start
6.4. Откройте браузер
6.5. Тестирование
npm run pm2-supabase-full:dev:test:e2e
6.6. Остановка
npm run pm2-supabase-full:dev:stop
Заключение
Проектирование и разработка кода для этой статьи заняла у меня почти два месяца частичной занятости. Так что можно задуматься о необходимости поддержки нескольких окружений для запуска приложения.
Примеры кода не буду тут приводить, так как изменений было очень много.
Поддержка обратной совместимости решается с помощью подмены необходимой имплементацией конфигурации и разными имплементациями сервисов, как в бэкенде так и на фронтенде.
Бесплатная версия облачной инфраструктуры от Supabase
ограничена по мощностям и долго отвечает, деплой в такое окружение можно использовать только во время разработки MVP
версии.
Так как задеплоенный в Vercel
вариант работает как serverless
, то у нас нет возможности использовать некие внутренние EventEmitter
-ы или RxJS Subject
-ы которые мы могли емитить с помощью фоновых глобальных setInterval
в коде, такие логики нужно решать иначе, используя Supabase Cron
, Supabase Queues
, Supabase realtime
.
Внедрение поддержки нескольких вариантов деплоя и запуска приложения — это очень трудозатратная штука и лучше всегда выбирать только один путь деплоя приложения.
Планы
Так как мне по работе нужно будет написать кастомный сервис авторизации, который можно будет расширять по мере необходимости, то в следующем посте я и опишу создание базовой простой версии...
Ссылки
https://nestjs.com - официальный сайт фреймворка
https://nestjs-mod.com - официальный сайт дополнительных утилит
https://fullstack.nestjs-mod.com - сайт из поста
https://nestjs-mod-fullstack.vercel.app - сайт из поста на Vercel
https://github.com/nestjs-mod/nestjs-mod-fullstack - проект из поста
https://github.com/nestjs-mod/nestjs-mod-fullstack/actions/runs/13308995633/artifacts/2585972924 - видео с E2E-тестов фронтенда
SatCat
Гуд! А не пробовали nestjs сразу на supabase бахнуть -- там вроде edge functions позволяют такое. Или нет?
kaufmanendy Автор
Неа, так не пробовал, мне интересен был Vercel изначально и к нему я уже начал искать онлайн бд, позже может чекнут функции supabase, пока не критично