Supabase RLS — Məlumatlarını Bank Səviyyəsində Qoru
Supabase Row Level Security ilə hər istifadəçi yalnız öz datasını görür. Bank tətbiqi nümunəsi ilə RLS-i addım-addım öyrən.
Supabase RLS — Məlumatlarını Bank Səviyyəsində Qoru
Təsəvvür et: Bakıda bir fintech startup qurmusan. İstifadəçilər AZN balanslarını görə bilir, köçürmə edə bilir. Bir gün səhər "Kapital Bank"-ın müştərisi sənə yazır: "Qardaş, mən başqasının balansını görə bilirəm." Bütün dünya başına yıxılır.
Bu, fantaziya deyil. 2023-cü ildə dünyada data breach-lərin 82%-i yanlış konfiqurasiya edilmiş API və ya authorization boşluqlarından qaynaqlanıb (Verizon DBIR 2023). Supabase istifadə edirsənsə, sənin bu boşluğu bağlayan silahın Row Level Security (RLS)-dir.
Bu gün bunu real bank tətbiqi nümunəsi ilə ətraflı öyrənəcəyik.
RLS Nədir və Niyə Vacibdir?
Row Level Security — PostgreSQL-in daxili mexanizmidir. Hər cədvələ "siyasət" (policy) yazırsan və verilənlər bazası səviyyəsində müəyyən edirsən: kim hansı sətirləri görə bilər, dəyişə bilər, silə bilər.
Supabase bunu çox rahat şəkildə inteqrasiya edib. Amma default olaraq RLS söndürülmüş olur. Yəni cədvəl yaradıb RLS-i aktiv etməsən, anon key ilə bütün məlumatları çəkmək olur. Bəli, hətta Postman-dan belə.
Bunu Bakıdakı reallıqla izah edim: Tutaq ki, sən 28 Mal-da oturan bir startup-da işləyirsən, aylıq 1800 AZN alırsan, Supabase-də layihə qurmusan. Frontend-dən supabase.from('accounts').select('*') yazırsan — əgər RLS yoxdursa, hər kəs hər kəsin datasını çəkə bilər. Bura kart nömrəsi, balans, telefon — hər şey daxildir.
Addım 1: Cədvəlləri Yaradaq
Bank tətbiqimiz üçün iki əsas cədvəl lazımdır:
sql-- İstifadəçi hesabları CREATE TABLE accounts ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, user_id UUID REFERENCES auth.users(id) NOT NULL, full_name TEXT NOT NULL, balance NUMERIC(12, 2) DEFAULT 0.00, currency TEXT DEFAULT 'AZN', created_at TIMESTAMPTZ DEFAULT now() ); -- Köçürmə tarixçəsi CREATE TABLE transactions ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, sender_id UUID REFERENCES accounts(id) NOT NULL, receiver_id UUID REFERENCES accounts(id) NOT NULL, amount NUMERIC(12, 2) NOT NULL, note TEXT, created_at TIMESTAMPTZ DEFAULT now() );
Burada user_id Supabase Auth-dan gələn istifadəçi ID-sidir. RLS-in bütün məntiqi bunun üzərində qurulacaq.
Addım 2: RLS-i Aktiv Et
sql-- RLS-i hər iki cədvəl üçün yandırırıq ALTER TABLE accounts ENABLE ROW LEVEL SECURITY; ALTER TABLE transactions ENABLE ROW LEVEL SECURITY;
Vacib: RLS aktiv olan kimi, heç bir policy yazılmayıbsa, heç kim heç nə görə bilmir — hətta authenticated istifadəçilər belə. Bu, "default deny" prinsipidir və məhz bu, onu güclü edir.
Addım 3: Siyasətləri (Policy) Yazaq
İndi əsas hissə. Hər istifadəçi yalnız öz hesabını görsün:
sql-- İstifadəçi yalnız öz hesab məlumatını oxuya bilsin CREATE POLICY "Users can view own account" ON accounts FOR SELECT USING (auth.uid() = user_id); -- İstifadəçi yalnız öz balansını yeniləyə bilsin CREATE POLICY "Users can update own account" ON accounts FOR UPDATE USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id);
auth.uid() — Supabase-in möcüzəsi. JWT token-dən avtomatik istifadəçi ID-sini çıxarır. Heç bir middleware, heç bir əlavə kod lazım deyil.
Transaksiyalar üçün isə bir az daha maraqlıdır — istifadəçi həm göndərən, həm qəbul edən ola bilər:
sql-- İstifadəçi öz göndərdiyi və ya aldığı köçürmələri görsün CREATE POLICY "Users can view own transactions" ON transactions FOR SELECT USING ( sender_id IN ( SELECT id FROM accounts WHERE user_id = auth.uid() ) OR receiver_id IN ( SELECT id FROM accounts WHERE user_id = auth.uid() ) ); -- Yalnız öz hesabından köçürmə edə bilsin CREATE POLICY "Users can insert own transactions" ON transactions FOR INSERT WITH CHECK ( sender_id IN ( SELECT id FROM accounts WHERE user_id = auth.uid() ) );
Bu policy ilə bir istifadəçi başqasının adından köçürmə edə bilməz. sender_id mütləq onun öz hesabına aid olmalıdır.
Addım 4: Frontend-dən Test Et
İndi JavaScript tərəfdən yoxlayaq:
javascriptimport { createClient } from '@supabase/supabase-js'; const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY); // Login olmuş istifadəçi öz balansını görür const { data, error } = await supabase .from('accounts') .select('full_name, balance, currency'); console.log(data); // Nəticə: [{ full_name: "Orxan Məmmədov", balance: 2450.00, currency: "AZN" }] // Yalnız öz datası gəlir — başqalarının balansı GÖRÜNMÜR
Burada WHERE yazmağa ehtiyac yoxdur. RLS avtomatik filtrləyir. Bu, developer üçün həm rahatdır, həm də təhlükəsizdir — çünki WHERE yazmağı unuda bilərsən, amma RLS heç vaxt unutmur.
Tez-tez Edilən Səhvlər
- RLS-i aktiv edib policy yazmamaq → Heç kim heç nə görə bilmir, frontend boş array qaytarır. Supabase dashboard-da "No rows returned" görürsənsə, ilk bura bax.
- Service role key-i frontend-ə qoymaq → Bu key RLS-i bypass edir. Onu YALNIZ server-side (Edge Functions, backend) istifadə et.
.env-ə yaz, heç vaxt client bundle-a düşməsin. USINGvəWITH CHECKfərqini bilməmək →USINGoxuma və mövcud sətirləri yoxlayır.WITH CHECKyeni/dəyişdirilən sətirin doğruluğunu yoxlayır. INSERT və UPDATE üçün hər ikisi lazımdır.- Subquery-lərdə performans problemi → Çox böyük cədvəllərdə (100K+ sətir) subquery əvəzinə
JOINvə ya materialized view istifadə et. Index əlavə etməyi unutma:CREATE INDEX idx_accounts_user_id ON accounts(user_id);
Admin Panel Üçün Ayrı Policy
Çox vaxt admin paneldə bütün istifadəçilərin datasını görmək lazım olur. Bunun üçün custom claim və ya ayrı rol istifadə edə bilərsən:
sqlCREATE POLICY "Admins can view all accounts" ON accounts FOR SELECT USING ( auth.jwt() ->> 'role' = 'admin' );
Supabase-də custom claim əlavə etmək üçün auth.users cədvəlindəki raw_app_meta_data sahəsinə {"role": "admin"} yazmalısan. Bunu Dashboard-dan və ya Edge Function-dan etmək olar.
Nəticə: RLS Sənin Gecə Yuxundur
Supabase RLS ilə sən verilənlər bazası səviyyəsində qoruma qurursan. Frontend-dəki bug, unudulmuş WHERE, yanlış API endpoint — bunların heç biri data leak-ə səbəb ola bilməz, çünki PostgreSQL özü qapını bağlayıb.
Bakıda fintech, e-commerce və ya hər hansı SaaS layihə qurursan? RLS-siz production-a çıxma. Bu, kilidlənməmiş bank qapısı kimidir — içəridə heç kim olmaya bilər, amma risk həmişə var.
Azərbaycan bazarında Supabase ilə işləyən layihələr artır. Birbank, m10 kimi tətbiqlərin infrastrukturu fərqli olsa da, prinsip eynidir: hər istifadəçi yalnız öz datasını görməlidir. RLS bunu bir neçə sətir SQL ilə həll edir.
Bunu öyrən, portfoliona əlavə et, müsahibədə danış. 1800 AZN-dən 3500 AZN-ə keçidin yolu belə detallardadır. 🚀
Oxşar məqalələr
Redis ilə Caching — Azərbaycan Trafik Piklərini İdarə Etmək
Bayram günləri saytın çökür? Redis ilə caching qurub, saniyədə 100K sorğunu rahat idarə etməyi öyrən — real kod və rəqəmlərlə.
PostgreSQL vs MongoDB — Bakı Startapın üçün Hansını Seçməlisən?
Azərbaycanlı developer olaraq startap qurmaq istəyirsən? PostgreSQL və MongoDB arasında düzgün seçim etmək üçün real kod, rəqəmlər və yerli kontekst burada.
Epoint və PAYRIFF: Azərbaycan Fintech Ödəniş API İnteqrasiyası
Azərbaycanda ödəniş qəbul etmək istəyirsən? Epoint və PAYRIFF API-lərini real kod nümunələri ilə addım-addım inteqrasiya edək — sandbox-dan production-a qədər.