TL;DR: Мы сделали автоматическое подключение Supabase через OAuth, чтобы каждый разработчик мог получить готовую базу данных под свой SaaS без ручного деплоя. Миграции выполняем через Supabase CLI, а работу с данными — через PostgREST.

Зачем всё это

Я развиваю сервис AgentCraft — платформу, которая превращает n8n‑автоматизации в готовый SaaS «из коробки».

AgentCraft подключает Stripe, Supabase, дашборды и систему пользователей — буквально за несколько минут.
Цель — минимизировать количество шагов, которые нужно сделать разработчику, чтобы развернуть свою инфраструктуру.

Примерная схема сервиса
Примерная схема сервиса

Когда мы дошли до вопроса автоматического развертывания БД, стало очевидно: нужен инструмент, который:

  • уже знаком no-code-разработчикам,

  • предоставляет API, CLI и OAuth,

  • и не требует ручной настройки PostgreSQL.

Выбор пал на Supabase — популярную open-source платформу поверх PostgreSQL.

Архитектура решения

Наша схема работы выглядит следующим образом:

  1. AgentCraft работает на классической PostgreSQL базе данных на бэкенде

  2. Разработчики регистрируются на AgentCraft и подключают свои вебхуки

  3. Разработчики подключают свою учетную запись Supabase через OAuth

  4. AgentCraft автоматически создает основные таблицы в их Supabase проекте:

    • Биллингbilling_plans, subscriptions, invoices

    • Конечные пользователиusers, user_sessions

    • Статистикаwebhook_executions, usage_tracking

    • и другие служебные таблицы

Таким образом, разработчик просто нажимает “Connect Supabase” — и получает полноценную инфраструктуру SaaS с базой данных, billing’ом и аналитикой. Как все это выглядит можно посмотреть тут - https://www.loom.com/share/e55bb4d7698547d9a31c676f2a109bb0

Как работает Supabase: инструменты взаимодействия

Supabase предоставляет несколько способов взаимодействия с базой данных:

1. Supabase CLI

CLI — основной инструмент для миграций и управления схемой базы данных.

# Логин через OAuth токен
supabase login --token $ACCESS_TOKEN

# Привязка к проекту
supabase link --project-ref $PROJECT_REF

# Создание новой миграции
supabase migration new saas_migration

# Применение миграций
supabase db push --yes

CLI идеально подходит для автоматического деплоя схемы в разных проектах Supabase — ровно то, что нам нужно.

2. PostgREST API

PostgREST — это REST API, которое Supabase автоматически генерирует для всех таблиц PostgreSQL.

GET /rest/v1/users?select=id,email&status=eq.active
POST /rest/v1/users
PATCH /rest/v1/users?id=eq.{id}
DELETE /rest/v1/users?id=eq.{id}

Этот API работает “из коробки” и позволяет AgentCraft взаимодействовать с чужими базами, не имея прямого доступа к PostgreSQL.

3. Management API

Management API используется для получения списка проектов, ключей и метаданных.
Через него мы получаем anon_key, service_key и project_ref для последующей работы.

Интерфейс создания SaaS. Пользователь прокликивает настройки подключая БД, вебхуки, биллинг. На последнем шаге запускается миграция в БД Supabase
Интерфейс создания SaaS. Пользователь прокликивает настройки подключая БД, вебхуки, биллинг. На последнем шаге запускается миграция в БД Supabase

Сравнительная таблица инструментов Supabase

Инструмент

Назначение

Ограничения

Supabase CLI

Миграции, управление схемой, деплой

Требует локальной установки, работает через OAuth

PostgREST API

CRUD операции с данными, выборка, фильтрация

Не поддерживает DDL (CREATE TABLE, ALTER TABLE и т.д.)

Management API

Управление проектами, получение ключей

Не управляет схемой БД

Прямое подключение PostgreSQL

Полный контроль над БД

Требует service_key, не всегда доступен

Создание миграций через CLI

В AgentCraft мы используем Supabase CLI для создания и применения миграций.
Вот как это реализовано на Go:

func (h *SaaSHandler) deployDatabaseWithOAuth(
    ctx context.Context,
    integration *models.ExternalIntegration,
    project *models.SupabaseOAuthProject,
    organization *models.SupabaseOAuthOrganization,
    productID uuid.UUID,
) (map[string]interface{}, error) {
    accessToken := *organization.AccessTokenEnc

    // Применяем миграции через CLI
    err := h.applyDatabaseTemplatesViaCLI(ctx, accessToken, project.Ref, productID)
    if err != nil {
        return nil, fmt.Errorf("failed to apply database templates: %w", err)
    }

    return deploymentConfig, nil
}

А вот сам процесс применения миграций:

func (h *SaaSHandler) applyDatabaseTemplatesViaCLI(
    ctx context.Context,
    accessToken, projectRef string,
    productID uuid.UUID,
) error {
    tempDir, _ := os.MkdirTemp("/app", "supabase-cli-*")
    defer os.RemoveAll(tempDir)

    // Логинимся в Supabase
    exec.CommandContext(ctx, "supabase", "login", "--token", accessToken).Run()
    exec.CommandContext(ctx, "supabase", "link", "--project-ref", projectRef).Run()

    // Создаем миграцию
    exec.CommandContext(ctx, "supabase", "migration", "new", "saas_migration").Run()

    // Записываем SQL
    os.WriteFile(tempDir+"/migration.sql", []byte(migrationSQL), 0644)

    // Применяем миграции
    exec.CommandContext(ctx, "supabase", "db", "push", "--yes").Run()
    return nil
}
Дашборд конечных пользователей (юзеров, которые регаются на SaaSы девелоперов). Данные идут из Supabase девелопера
Дашборд конечных пользователей (юзеров, которые регаются на SaaSы девелоперов). Данные идут из Supabase девелопера
А разработчик в свою очередь видит статистику по своим SaaS
А разработчик в свою очередь видит статистику по своим SaaS

Почему не удалось использовать PostgREST для миграций

PostgREST не поддерживает DDL операции — то есть нельзя делать CREATE TABLE, ALTER TABLE, CREATE FUNCTION и т.п.

Причины:

  1. Безопасность — нельзя давать возможность создавать таблицы через HTTP

  2. Назначение — PostgREST предназначен для CRUD, а не для схемы

Именно поэтому мы выбрали Supabase CLI — он безопасен, легитимен и поддерживает OAuth-доступ.

После создания SaaS в Supabase разработчика создаются все таблицы
После создания SaaS в Supabase разработчика создаются все таблицы

OAuth подключение Supabase

Наша цель - сделать максимально простое подключение БД, как у Lovable.

После того, как разработчик жмет кнопку “Connect Supabase”, происходит классический OAuth flow:

func (h *IntegrationsHandler) HandleSupabaseOAuthCallback(w http.ResponseWriter, r *http.Request) {
    tokens, _ := h.exchangeSupabaseOAuthCode(request.Code)
    organizations, _ := h.fetchSupabaseOrganizations(tokens.AccessToken)
    h.storeSupabaseOAuthData(r.Context(), userID, tokens, organizations)
}

Далее мы создаем интеграции для всех доступных проектов пользователя:

for _, org := range organizations {
    for _, project := range org.Projects {
        details, _ := h.fetchProjectDetailsFromSupabase(accessToken, project.Ref)
        h.createIntegration(userID, project.Ref, details.ServiceKey, details.AnonKey)
    }
}
Добавление БД к проекту. Помимо oauth supabase я оставил вариант прямого подключения к БД
Добавление БД к проекту. Помимо oauth supabase я оставил вариант прямого подключения к БД

Взаимодействие через PostgREST

После миграции мы используем PostgREST API для CRUD операций с таблицами клиента:

func (c *SupabaseCommon) ExecuteSupabasePostgRESTRequest(
    ctx context.Context,
    projectRef, serviceKey, method, endpoint string,
    payload interface{},
) ([]byte, error) {
    url := fmt.Sprintf("https://%s.supabase.co/rest/v1/%s", projectRef, endpoint)
    req, _ := http.NewRequestWithContext(ctx, method, url, reqBody)
    req.Header.Set("Authorization", "Bearer "+serviceKey)
    req.Header.Set("apikey", serviceKey)
    return c.httpClient.Do(req)
}

Практические советы

1. Обработка таймаутов

Supabase может “засыпать” после неактивности — добавьте retry-механику:

for attempt := 1; attempt <= 3; attempt++ {
    err := h.applyDatabaseTemplatesViaCLI(...)
    if strings.Contains(err.Error(), "connection") {
        time.Sleep(time.Duration(attempt) * 5 * time.Second)
        continue
    }
    break
}

2. Обновление OAuth токенов

Токены истекают, поэтому мы проверяем их перед каждым деплоем и обновляем при необходимости.

3. Валидация после миграции

Проверяем, что все таблицы созданы корректно:

SELECT EXISTS (
  SELECT FROM information_schema.tables 
  WHERE table_schema = 'public' 
  AND table_name = 'users'
);

Вывод

Интеграция Supabase через OAuth и CLI позволила нам:

  • автоматизировать развёртывание инфраструктуры SaaS для пользователей,

  • избавить разработчиков от ручного конфигурирования PostgreSQL,

  • использовать PostgREST как стандартный REST API поверх готовой схемы.

Комментарии (0)