Налаштування Supabase для веб-додатку
Supabase — open source альтернатива Firebase на основі PostgreSQL. Включає базу даних, аутентифікацію, realtime-підписки, сховище файлів та Edge Functions. Добре підходить для швидкого старту та проектів, де важлива швидкість розробки.
Створення проекту
Через dashboard.supabase.com — вибрати регіон, задати пароль бази. Для self-hosted:
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker
cp .env.example .env
# Відредагувати .env: POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY
docker compose up -d
Підключення клієнта
import { createClient } from '@supabase/supabase-js'
import type { Database } from './database.types'
export const supabase = createClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
// Для серверних операцій — service role key
export const supabaseAdmin = createClient<Database>(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
)
Генерація типів зі схеми
npx supabase gen types typescript --project-id your-project-id > src/types/database.types.ts
Це дає автодоповнення та перевірку типів при роботі з таблицями.
Схема та Row Level Security
-- Таблиця профілів (розширення auth.users)
CREATE TABLE profiles (
id uuid PRIMARY KEY REFERENCES auth.users ON DELETE CASCADE,
username text UNIQUE,
avatar_url text,
bio text,
updated_at timestamptz DEFAULT now()
);
-- RLS — кожен бачить лише своє
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "profiles_select_own" ON profiles
FOR SELECT USING (auth.uid() = id);
CREATE POLICY "profiles_update_own" ON profiles
FOR UPDATE USING (auth.uid() = id);
-- Публічні профілі — читають всі
CREATE POLICY "profiles_select_public" ON profiles
FOR SELECT USING (true);
-- Таблиця постів з RLS
CREATE TABLE posts (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid REFERENCES auth.users NOT NULL,
title text NOT NULL,
content text,
is_public boolean DEFAULT false,
created_at timestamptz DEFAULT now()
);
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
CREATE POLICY "posts_select_public_or_own" ON posts
FOR SELECT USING (
is_public = true OR auth.uid() = user_id
);
CREATE POLICY "posts_insert_own" ON posts
FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "posts_update_own" ON posts
FOR UPDATE USING (auth.uid() = user_id);
CREATE POLICY "posts_delete_own" ON posts
FOR DELETE USING (auth.uid() = user_id);
Аутентифікація
// Реєстрація
const { data, error } = await supabase.auth.signUp({
email: '[email protected]',
password: 'password',
options: {
data: { username: 'john_doe' } // додаткові дані в user_metadata
}
})
// OAuth (Google, GitHub, тощо)
await supabase.auth.signInWithOAuth({
provider: 'google',
options: { redirectTo: `${window.location.origin}/auth/callback` }
})
// Отримати поточного користувача
const { data: { user } } = await supabase.auth.getUser()
// Підписка на зміни сесії
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_IN') router.push('/dashboard')
if (event === 'SIGNED_OUT') router.push('/login')
})
CRUD операції
// Вставка з поверненням
const { data: post, error } = await supabase
.from('posts')
.insert({ title, content, user_id: user.id })
.select()
.single()
// Запит з фільтрами та JOIN
const { data: posts } = await supabase
.from('posts')
.select(`
id, title, created_at,
profiles ( username, avatar_url ),
tags ( name )
`)
.eq('is_public', true)
.order('created_at', { ascending: false })
.range(0, 23) // розбиття на сторінки
// Повнотекстовий пошук (PostgreSQL tsquery)
const { data } = await supabase
.from('posts')
.select('*')
.textSearch('content', query, { config: 'russian', type: 'websearch' })
Realtime підписки
// Підписка на нові записи
const channel = supabase
.channel('posts-feed')
.on('postgres_changes', {
event: 'INSERT',
schema: 'public',
table: 'posts',
filter: 'is_public=eq.true'
}, (payload) => {
setPosts(prev => [payload.new as Post, ...prev])
})
.subscribe()
// Відписка при розмонтуванні
return () => supabase.removeChannel(channel)
Edge Functions
// supabase/functions/send-notification/index.ts
import { serve } from 'https://deno.land/[email protected]/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
serve(async (req) => {
const { userId, message } = await req.json()
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
await supabase
.from('notifications')
.insert({ user_id: userId, message })
return new Response(JSON.stringify({ ok: true }), {
headers: { 'Content-Type': 'application/json' }
})
})
# Деплой функції
supabase functions deploy send-notification
Сховище файлів
// Завантажити аватар
const { data, error } = await supabase.storage
.from('avatars')
.upload(`${user.id}/avatar.jpg`, file, {
cacheControl: '3600',
upsert: true
})
// Отримати публічну ссилку
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`${user.id}/avatar.jpg`)
Терміни
Налаштування Supabase-проекту з аутентифікацією, RLS-політиками та базовим CRUD: 1–2 дні. Додавання realtime, Edge Functions, сховища та інтеграції з frontend: ще 2–3 дні. Self-hosted деплой з налаштуванням бэкапів та моніторингу: ще 1–2 дні.







