---
title: "Supabase RLS policy piemēri, pārbaudīti caur API"
description: "Kopējami Supabase RLS policy piemēri: savas rindas, publiski profili, komandas un MFA, palaisti kā viesis, īpašnieks un cits lietotājs, kopā ar rezultātiem."
url: "https://madsalt.tech/lv/blog/supabase-rls-policy-examples/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/lv/about/"
language: "lv"
published: 2026-10-27
---

# Supabase RLS policy piemēri, pārbaudīti caur API

Īsumā

Zemāk ir RLS politikas, kas vajadzīgas lielākajai daļai Supabase lietotņu, un katra palaista caur REST API kā viesis, kā rindas īpašnieks un kā cits pierakstījies lietotājs. Pirms kaut ko kopējat, vērts zināt trīs rezultātus: cita lietotāja rindas update atgriež `200` ar tukšu sarakstu, nevis kļūdu; nevainīgi izskatīga insert politika dalības tabulā ļāva lietotājam pievienoties citam uzņēmumam un lasīt tā projektus; un, rakstot `(select auth.uid())` `auth.uid()` vietā, viens vaicājums 200 000 rindu tabulā kļuva ātrāks no 156 ms uz 7,4 ms.

Esmu Denys, full-stack un mākslīgā intelekta inženieris, un lielākā daļa lietotņu, ko veidoju vai labāju, darbojas uz Supabase. Politiku fragmentu ir visur; ko tie dara, kad tiem trāpa īsts pieprasījums, reti parāda. Tāpēc es tos palaidu. Šie ir Supabase RLS policy piemēri, ar kuriem pats sāku, kopā ar katra rezultātu.

**Kā testēju.** Lielākā daļa pieprasījumu gāja caur lokālu Supabase stekku (CLI 2.109.1, Postgres 17.6, PostgREST 14.14, Auth 2.192.0) ar publisko atslēgu un diviem īstiem pierakstītiem lietotājiem, A un B. Gadījumus, kam HTTP nav vajadzīgs, piemēram, sintakses kļūdas un laika mērījumus, palaidu parastā lokālā Postgres 17.10 ar Supabase lomām `anon` un `authenticated` un no jauna izveidotu `auth.uid()`. Viss 2026. gada 24. oktobrī, ar izdomātām rindām.

## Pirms jebkuras politikas: vai tabulai ir grant?

Politika nosaka, kādas rindas loma redz. Grant nosaka, vai loma vispār tiek pie tabulas, un pašreizējos Supabase projektos jaunas tabulas Data API grantus vairs automātiski nesaņem (visos projektos no 2026. gada 30. oktobra). Manā stekā tabula ar ieslēgtu RLS un bez granta atbildēja šādi:

| Pieprasījums                | Statuss | Saturs                                                                       |
| --------------------------- | ------- | ---------------------------------------------------------------------------- |
| Viesis `GET /invoices`      | `401`   | `permission denied for table invoices`, ar norādi `GRANT SELECT ... TO anon` |
| Lietotājs A `GET /invoices` | `403`   | tas pats, ar norādi lomai `authenticated`                                    |

Tātad katrai tabulai, ko lietotne lasa no pārlūka, pirmais solis ir:

```sql
alter table public.todos enable row level security;
grant select, insert, update, delete on public.todos to authenticated;
```

Piešķiriet tikai tās komandas, kas pārlūkam vajadzīgas, un tikai lomai `authenticated`, ja vien viesiem tabula nav jālasa.

## Kā panākt, lai lietotāji redz tikai savas rindas?

Viena politika katrai komandai, katra ierobežota ar pierakstītiem lietotājiem:

```sql
create policy "read own" on public.todos
  for select to authenticated using ((select auth.uid()) = user_id);
create policy "add own" on public.todos
  for insert to authenticated with check ((select auth.uid()) = user_id);
create policy "edit own" on public.todos
  for update to authenticated
  using ((select auth.uid()) = user_id) with check ((select auth.uid()) = user_id);
create policy "delete own" on public.todos
  for delete to authenticated using ((select auth.uid()) = user_id);
```

A pieder divas rindas, B — viena. Kas atgriezās:

| Pieprasītājs | Ko                                 | Statuss | Saturs                                       |
| ------------ | ---------------------------------- | ------- | -------------------------------------------- |
| Viesis       | lasīt visu                         | `401`   | permission denied (`anon` nav granta)        |
| A            | lasīt visu                         | `200`   | A divas rindas                               |
| A            | insert B piederošu rindu           | `403`   | `new row violates row-level security policy` |
| A            | update B rindai                    | `200`   | `[]`                                         |
| A            | delete B rindai                    | `200`   | `[]`                                         |
| A            | mainīt savas rindas īpašnieku uz B | `403`   | `new row violates row-level security policy` |

Divas `200 []` rindas ir tās, kas jāatceras. RLS neatsaka update vai delete rindām, ko jūs neredzat; tas tās izfiltrē, un nulle mainītu rindu ir veiksme. Ja jūsu lietotne uzskata "nav kļūdas" par "saglabāts", tā pateiks lietotājam, ka labojums izdevies, kaut gan nekas nenotika. Pieprasiet mainītās rindas atpakaļ (`.select()` supabase-js, `Prefer: return=representation` caur HTTP) un pārbaudiet, vai atgriezās viena.

## Kāpēc update politikai vajadzīgs WITH CHECK?

`using` izvēlas, kurām esošajām rindām lietotājs drīkst pieskarties; `with check` nosaka, kādām šīm rindām pēc tam jāizskatās. Salīdzināju trīs vienas update politikas versijas:

| Update politika                                    | A atdod savu rindu B                             |
| -------------------------------------------------- | ------------------------------------------------ |
| `using (uid = user_id) with check (uid = user_id)` | `403`, atteikts                                  |
| `using (uid = user_id)`, bez `with check`          | atteikts (Postgres izmanto `using` arī kā check) |
| `using (uid = user_id) with check (true)`          | `200`, rinda tagad pieder B                      |

`with check` izlaišana ir drošāka, nekā šķiet, jo Postgres atgriežas pie `using` izteiksmes. Lietotājam ļauj pārcelt rindu uz svešu kontu `with check (true)`, ko daži ģeneratori raksta, lai "atļautu update". Uzrakstiet check pilnībā.

## Kā padarīt profilus publiskus, bet rediģējamus tikai īpašniekam?

```sql
create policy "public read" on public.profiles
  for select to anon, authenticated using (true);
create policy "own edit" on public.profiles
  for update to authenticated
  using ((select auth.uid()) = id) with check ((select auth.uid()) = id);
```

Parastā Postgres palaišanā: viesis izlasīja abus profilus, viesa update izmainīja 0 rindu, un A `update profiles set display_name = 'x'` bez filtra izmainīja tieši 1 rindu, paša A rindu. `using (true)` der select datiem, kam jābūt publiskiem. Jebkurai citai tabulai tas ir tas pats, kas bez RLS, un politika, kas uzrakstīta bez `to` klauzulas, attiecas arī uz viesiem: mans viesis izlasīja "privāto" rindu caur `using (true)` politiku bez `to`.

## Kā koplietot rindas komandas ietvaros?

Parastā forma: tabula `memberships` un datu politika, kas tajā meklē lietotāja komandas.

```sql
create policy "members read" on public.projects
  for select to authenticated
  using (org_id in (select org_id from public.memberships
                    where user_id = (select auth.uid())));
```

Ar šo politiku viss kārtībā. Noplūde bija blakus. Tabulai `memberships` uzliku politiku, ko iesaka daudzas pamācības, "lietotāji var ievietot savu dalību":

```sql
create policy "join any org" on public.memberships
  for insert to authenticated with check (user_id = (select auth.uid()));
```

Tā pārbauda, kam rinda paredzēta, nevis kurai komandai. A, uzņēmuma 1 dalībnieks, nosūtīja vienu pieprasījumu:

![Viena insert politika tabulā memberships. Uzņēmuma 1 lietotājs A, caur REST API. Politika: user_id = auth.uid(): Lietotājs A → pievienojas uzņ. 2 → 201 Created → lasa B algas; Nav insert politikas: Lietotājs A → pievienojas uzņ. 2 → atteikts → tikai savi projekti.](https://madsalt.tech/charts/lv/supabase-rls-policy-examples-join.svg)

*1. attēls. Mans tests lokālā Supabase stekā (CLI 2.109.1), 2026. gada 24. oktobrī.*

| A pieprasījums                          | Statuss | Saturs                   |
| --------------------------------------- | ------- | ------------------------ |
| lasīt projektus                         | `200`   | `A roadmap`              |
| insert dalība `{user_id: A, org_id: 2}` | `201`   | izveidota                |
| lasīt projektus vēlreiz                 | `200`   | `A roadmap`, `B payroll` |

Risinājums ir vispār nedot tabulai `memberships` insert politiku, lai pārlūks tajā nevarētu rakstīt (parastā Postgres palaišanā tas pats insert tad tika atteikts ar `42501`), un dalībniekus pievienot ar uzaicinājuma plūsmu, kas darbojas serverī vai funkcijā, kas pārbauda uzaicinājumu. Lietotājs, kas pieder diviem uzņēmumiem, ir nākamās lamatas; par to rakstīju pie [Supabase RLS: kāpēc divu org. lietotājs redz svešus datus](https://madsalt.tech/lv/notes/supabase-rls-user-in-two-organizations/).

## Ko darīt, ja politikai jāizlasa sava tabula?

"Dalībnieki redz savus komandas biedrus" nozīmē politiku tabulā `memberships`, kas lasa tabulu `memberships`. Postgres atsakās vaicājuma izpildes laikā:

```text
ERROR 42P17: infinite recursion detected in policy for relation "members2"
```

Pārvietojiet meklēšanu uz funkciju, kas darbojas ar īpašnieka tiesībām, shēmā, ko API nepublicē, ar fiksētu `search_path`:

```sql
create schema private;
create function private.user_org_ids() returns setof int
  language sql stable security definer set search_path = ''
  as $$ select org_id from public.members2 where user_id = (select auth.uid()) $$;
revoke all on function private.user_org_ids() from public;
grant usage on schema private to authenticated;
grant execute on function private.user_org_ids() to authenticated;

create policy "see teammates" on public.members2
  for select to authenticated using (org_id in (select private.user_org_ids()));
```

Pēc tam A redzēja abus uzņēmuma 1 dalībniekus, bet B redzēja tikai sevi. Turiet funkciju ārpus `public`: viss, kas tur ir, ir izsaucams caur API.

## Kā pieprasīt MFA jutīgām rindām?

Pievienojiet ierobežojošu politiku virs parastās. Ierobežojošās politikas apvieno ar `and`, tāpēc jāiziet abām:

```sql
create policy "mfa only" on public.payouts as restrictive
  for select to authenticated using ((select auth.jwt() ->> 'aal') = 'aal2');
```

Ar to pašu lietotāju `aal1` sesija izlasīja 0 izmaksu, bet `aal2` sesija — 1.

## Kurām tabulām RLS jābūt ieslēgtam un politikas nav?

Tabulām, kam pieskaras tikai jūsu serveris. Ieslēgts RLS bez politikas liedz visu pārlūka lomām: parastā Postgres palaišanā viesis un A izlasīja 0 rindu, un A insert tika atteikts ar `42501`. RLS atstāšana izslēgtu ir pretējais. Tabula ar izslēgtu RLS un noklusējuma grantiem, lasīta ar publisko atslēgu bez pierakstīšanās:

| Viesa pieprasījums      | Statuss | Saturs                 |
| ----------------------- | ------- | ---------------------- |
| `GET /leads`            | `200`   | abas rindas            |
| `DELETE /leads?id=gt.0` | `200`   | abas rindas, izdzēstas |

Tieši par to ir Advisor brīdinājums "RLS disabled in public".

## Vai viena politika var aptvert vairākas komandas?

Ne ar sarakstu. `for update, delete` ir sintakses kļūda (`42601: syntax error at or near ","`), kaut gan tā joprojām parādās publicētos fragmentu krājumos. `for all` ir derīgs un piemēro vienu `using` un vienu `with check` visām komandām, ko reti vēlas. Arī insert politika ar `using` neizdodas: `only WITH CHECK expression allowed for INSERT`.

## Vai politikas palēnina vaicājumus?

Var palēnināt. Politikas izteiksme tiek izpildīta katrai rindai, ko Postgres izskata. Nomērīju `select * from big` kā lietotājs A 200 000 rindās, kas pieder 1 000 lietotājiem, A pieder 200 (7 palaišanu mediāna, Postgres 17.10):

![Viena īpašnieka politika, četri pieraksti. Milisekundes, lai nolasītu 200 savas rindas no 200 000. 7 palaišanu mediāna. auth.uid() = user_id, bez indeksa: 156,30; (select auth.uid()), bez indeksa: 7,40; auth.uid() = user_id, ar indeksu: 0,21; (select auth.uid()), ar indeksu: 0,05.](https://madsalt.tech/charts/lv/supabase-rls-policy-examples.svg)

*2. attēls. Mans tests lokālā Postgres 17.10 ar no jauna izveidotām Supabase lomām, 2026. gada 24. oktobrī.*

| Politika un indekss                          | Mediāna  |
| -------------------------------------------- | -------- |
| `auth.uid() = user_id`, bez indeksa          | 156,3 ms |
| `(select auth.uid()) = user_id`, bez indeksa | 7,4 ms   |
| `auth.uid() = user_id`, indekss uz `user_id` | 0,21 ms  |
| `(select auth.uid()) = user_id`, indekss     | 0,05 ms  |

Izsaukuma ietīšana `select` ļauj Postgres to aprēķināt vienreiz vaicājumā, nevis vienreiz katrai rindai; pārējo izdara indekss. Supabase [RLS veiktspējas ceļvedis](https://supabase.com/docs/guides/troubleshooting/rls-performance-and-best-practices-Z5Jjwv) savā 100 000 rindu tabulā min tādu pašu izmaiņu kārtu (no 179 ms uz 9 ms). Daudzi bloga ieraksti šos skaitļus citē kā savus; augstāk minētie ir no mana palaiduma.

## No kurienes jānāk lomām?

No `app_metadata` vai jūsu pašu tabulas, nekad no `user_metadata`. Pierakstījies lietotājs var mainīt savu `user_metadata` ar `supabase.auth.updateUser()`, tāpēc politika, kas pārbauda `auth.jwt() -> 'user_metadata' ->> 'role'`, ļauj viņam lomu piešķirt pašam sev. Reģistrācijas trigeris, kas kopē lomu no reģistrācijas metadatiem, dara to pašu; šis gadījums aprakstīts rakstā [Supabase raw_user_meta_data: kā reģistrācija kļuva par Owner](https://madsalt.tech/lv/notes/supabase-raw-user-meta-data-role/).

## Kā pārbaudīt savas politikas?

- Bezmaksas [Supabase drošības pārbaude](https://madsalt.tech/lv/tools/supabase-security-check/) nolasa jūsu projektu tikai lasīšanas režīmā un atzīmē vienmēr patiesas politikas, rakstīšanas politikas viesiem, politikas, kas uzticas lietotāja metadatiem, un tabulas ar izslēgtu RLS, katrai ar labošanas SQL.
- Neviens skeneris nezina, kuras rindas kam pieder. Tam palaidiet pieprasījumus kā divi lietotāji, kā to dara šis raksts; pilna metode lietotnei ar lomām ir rakstā [kā es testēju Supabase RLS](https://madsalt.tech/lv/blog/how-to-test-supabase-rls/).
- Ko Supabase aizsargā jūsu vietā un ko atstāj jums, ir rakstā [vai Supabase ir drošs?](https://madsalt.tech/lv/blog/is-supabase-secure/).
- Tie paši īpašnieka un komandas testi, palaisti arī uz Appwrite, ir rakstā [Appwrite vs Supabase drošība: vienādi uzbrukumi abiem](https://madsalt.tech/lv/blog/appwrite-vs-supabase/).

**Ierobežojumi.** Viens lokāls steks un viens klēpjdators, izdomāti dati, 2026. gada 24. oktobris. Laiki ir atkarīgi no aparatūras un tabulas formas; attiecības ir svarīgākas par milisekundēm. Storage politikas un Realtime šeit nav aplūkotas.

Neesat pārliecināti, vai jūsu politikas izturēs?

Labprāt kopā ar jums apskatīšu shēmu. Bez pārdošanas sarunas.

[Sazināties](https://madsalt.tech/lv/#contact)
