Auth (Google/Facebook OAuth, email), Database (PostgreSQL, RLS policies, SECURITY DEFINER), Edge Functions, Realtime subscriptions...
Kompleksowy przewodnik dla pracy z Supabase w aplikacjach Vite SPA - autentykacja, baza danych, RLS policies, Edge Functions i bezpieczeństwo.
ALTER TABLE tablename ENABLE ROW LEVEL SECURITY(SELECT auth.uid()) w policies (nie email) — subquery dla wydajnościsupabase gen types --lang typescript --local > src/types/database.tslib/supabase.tssupabase/functions/function-name/export default { fetch: withSupabase({ auth }, handler) } z npm:@supabase/server@^1 (nie Deno.serve())npm:@supabase/supabase-js@2, npm:stripe@22auth per funkcja: 'user' (JWT), 'secret' (cron/pg_net), 'publishable' (przed logowaniem), 'none' (webhook zewnętrzny)verify_jwt = false w supabase/config.toml dla trybów innych niż 'user'cors: 'disabled' dla webhooków) — bez _shared/cors.tssupabase functions servesupabase functions deploy function-nameauth.uid()) w policies, nie emailSET search_path = '' (pusty) + w pełni kwalifikowane nazwy (public.tabela) w każdej funkcji SECURITY DEFINER// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
import type { Database } from '@/types/database';
// Publishable key (sb_publishable_...) — bezpieczny do ujawnienia, podlega RLS.
// Legacy anon/service_role (JWT) będą wycofane do końca 2026 — nowe projekty
// używają publishable/secret keys (docs: guides/api/api-keys).
export const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY,
// Domyślny flowType to 'implicit' — dla OAuth/magic link ustaw PKCE jawnie
{ auth: { flowType: 'pkce' } }
);
// Helper types
export type Tables<T extends keyof Database['public']['Tables']> =
Database['public']['Tables'][T]['Row'];
export type InsertTables<T extends keyof Database['public']['Tables']> =
Database['public']['Tables'][T]['Insert'];
export type UpdateTables<T extends keyof Database['public']['Tables']> =
Database['public']['Tables'][T]['Update'];
# Z lokalnej bazy
supabase gen types --lang typescript --local > src/types/database.ts
# Z produkcji (pozycyjne `typescript` to stara forma — używaj --lang)
supabase gen types --lang typescript --project-id YOUR_PROJECT_ID > src/types/database.ts
// SELECT
const { data, error } = await supabase
.from('posts')
.select('*')
.eq('published', true)
.order('created_at', { ascending: false });
// INSERT
const { data, error } = await supabase
.from('posts')
.insert({ title, content, user_id: userId });
// UPDATE
const { data, error } = await supabase
.from('profiles')
.update({ display_name: newName })
.eq('id', userId);
// DELETE
const { data, error } = await supabase
.from('bookmarks')
.delete()
.eq('user_id', userId)
.eq('post_id', postId);
// RPC (wywołanie funkcji PostgreSQL)
const { data, error } = await supabase.rpc('ensure_user_profile');
Dostępne metody:
Kluczowe Koncepcje:
flowType w createClient to implicit — PKCE włącz jawnie: { auth: { flowType: 'pkce' } } (@supabase/ssr ma PKCE skonfigurowane); detectSessionInUrl: true domyślnie — nie wymieniaj code ręcznie w przeglądarceuseAuth() zarządza sesjąhandle_new_user() tworzy rekord w public.profilesensure_user_profile() jako fallbackgetSession() dla UI, getUser() lub getClaims() dla krytycznych operacjiPełny Przewodnik: resources/auth-patterns.md
Wzorcowe Tabele:
profiles - dane użytkowników (1:1 z auth.users)posts - treści z własnością użytkownikacomments - relacje do postów i użytkownikówbookmarks - relacja many-to-manyaudit_log - logowanie krytycznych operacji (write-only)RLS Patterns:
USING (true)USING ((SELECT auth.uid()) = user_id)USING (published = true OR (SELECT auth.uid()) = user_id)Pełny Przewodnik: resources/database-patterns.md
Typowe Zastosowania:
Wzorce 2026:
export default { fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {...}) } z npm:@supabase/server@^1 — ctx.supabase (RLS) / ctx.supabaseAdmin; Deno.serve() to legacy, które nadal działa, ale Supabase go już nie zalecanpm:@supabase/supabase-js@2 (nie jsr:/esm.sh) — tylko gdy potrzebny klient poza ctxnpm:stripe@22 (nie esm.sh)constructEventAsync dla Stripe webhooksdeno.json preferowany nad import mapsPełny Przewodnik: resources/edge-functions.md
Kluczowe Wzorce:
Pełny Przewodnik: resources/security.md
Użycie:
Bezpieczeństwo: kanały prywatne (config: { private: true }) działają dopiero po wyłączeniu
„Allow public access" w Realtime Settings (/dashboard/project/_/realtime/settings).
Dopóki jest włączone, RLS na realtime.messages nie jest wymuszane przy joinie.
Pełny Przewodnik: resources/realtime.md
| Potrzebujesz... | Przeczytaj |
|---|---|
| Autentykację OAuth/email | auth-patterns.md |
| Bazę danych i RLS | database-patterns.md |
| Edge Functions | edge-functions.md |
| Bezpieczeństwo | security.md |
| Realtime subscriptions | realtime.md |
| Supabase CLI | cli-guide.md |
auth.uid() = user_id, nigdy emailsupabase gen types po każdej migracjiSET search_path = '' (pusty) + w pełni kwalifikowane nazwy (public.tabela)logger.error() zamiast console.error()VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_... # legacy: VITE_SUPABASE_ANON_KEY (wycofywany do końca 2026)
# Wstrzykiwane automatycznie (withSupabase czyta je sam):
SUPABASE_URL=...
SUPABASE_PUBLISHABLE_KEYS={"default":"sb_publishable_..."}
SUPABASE_SECRET_KEYS={"default":"sb_secret_..."} # legacy: SUPABASE_SERVICE_ROLE_KEY; NIGDY nie commituj!
# Własne secrets (supabase secrets set):
STRIPE_SECRET_KEY=... # NIGDY nie commituj!
STRIPE_WEBHOOK_SECRET=... # NIGDY nie commituj!
// ❌ Service role na froncie
const supabase = createClient(url, SERVICE_ROLE_KEY);
// ❌ Email w RLS policy
USING (user_email = auth.email()) // Email może się zmienić!
// ❌ Brak typów
const { data } = await supabase.from('posts').select('*'); // data: any
// ❌ console.error w produkcji
console.error('DB error:', error); // Wycieka info o strukturze DB
// ❌ Stary import w Edge Functions
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';
// ❌ getSession() do autoryzacji server-side
const { data: { session } } = await supabase.auth.getSession();
if (session) { /* autoryzacja */ } // Token nie jest zweryfikowany!
// ✅ Publishable key na froncie (sb_publishable_...; legacy anon key działa do końca 2026)
const supabase = createClient(url, PUBLISHABLE_KEY);
// ✅ UUID w RLS policy
USING (auth.uid() = user_id) // UUID jest immutable
// ✅ Typed queries
const { data } = await supabase.from('posts').select('*'); // data: Tables[]
// ✅ Production-safe logger
logger.error('Błąd operacji', error);
// ✅ Nowy standard Edge Functions (Deno.serve = legacy, nadal działa)
export default {
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => { ... }),
};
// ✅ getUser() lub getClaims() do autoryzacji
const { data: { user } } = await supabase.auth.getUser();
if (user) { /* autoryzacja */ }
Status Skilla: Modułowa struktura z progressive loading dla optymalnego zarządzania kontekstem. Zaktualizowany do standardów Sierpień 2026 (withSupabase, publishable/secret keys).