RPC (Postgres funksiyalar)

Postgres funksiyalarini chaqirish orqali custom biznes logikasini bajarish. RLS va autentifikatsiya bilan ishlaydi, skalyar yoki SETOF qaytaruvchi funksiyalarni qo'llaydi.

RPC asoslari

RPC (Remote Procedure Call) — bu Postgres funksiyalarini HTTP orqali chaqirish imkoniyati. storagedb SQL Editor'da yaratgan funksiyani POST /v1/<REF>/rest/v1/rpc/<fn_name> orqali chaqirish mumkin.

HTTP so'rov bajarilganda:

1. SET LOCAL ROLE <anon|authenticated|service_role> — foydalanuvchining roli o'rnatiladi (RLS qoidalari ushbu rol ostida ishlaydi).

2. set_config('request.jwt.claims', <jwt>, true) — JWT claim'lari session'ga o'rnatiladi, shuning uchun auth.uid() va boshqa auth funksiyalari ishlaydi.

3. Postgres funksiyasi bajariladi. Skalyar qaytaruvchi (bitta qiymat) to'g'ridan-to'g'ri qaytariladi; SETOF yoki table qaytaruvchi funksiya massiv qaytaradi.

Funksiyani avval SQL Editor'da CREATE FUNCTION yoki meta/query bilan yaratish kerak.

Postgres funksiyasi yaratish

storagedb'dagi SQL Editor orqali funksiya yarating. Quyida o'z schema'sida simple qoshish funksiyasi:

Bu funksiya public schema'sida bo'ladi va uning parametrlar nomasi o'zgaruvchining nomi bilan mos kelib turadi (RPC POST body'dagi key'lar).

SQL: Simple qoshish funksiyasi
CREATE FUNCTION public.qoshish(a int, b int) RETURNS int AS $$
BEGIN
  RETURN a + b;
END;
$$ LANGUAGE plpgsql;

-- Yoki immutable (pure function):
CREATE FUNCTION public.ko_payti(x int, y int) RETURNS int IMMUTABLE AS $$
  SELECT x * y;
$$ LANGUAGE sql;

RPC: curl orqali chaqirish

POST so'rovni /rest/v1/rpc/<fn_name> ga yuboring. JSON body'dagi kalitlar funksiyaning parameter nomlari bo'lishi kerak.

Header'ga apikey: <ANON_KEY> yoki apikey: <SERVICE_KEY> qo'shing.

Skalyar funksiyalar (1 qiymat qaytaruvchi) qiymatni to'g'ridan-to'g'ri qaytaradi. SETOF funksiyalar massiv qaytaradi.

curl: qoshish funksiyasini chaqirish
curl -X POST \
  https://storage.identify.uz/v1/<REF>/rest/v1/rpc/qoshish \
  -H "apikey: <ANON_KEY>" \
  -H "content-type: application/json" \
  -d '{"a": 5, "b": 7}'

# Natija (skalyar):
# 12
curl: ko_payti (ko'paytirish)
curl -X POST \
  https://storage.identify.uz/v1/<REF>/rest/v1/rpc/ko_payti \
  -H "apikey: <SERVICE_KEY>" \
  -H "content-type: application/json" \
  -d '{"x": 3, "y": 4}'

# Natija:
# 12

RPC: Client SDK bilan chaqirish

storagedb Client SDK'sida db.rpc(fnName, args) metodini ishlating. U Promise qaytaradi { data, error } strukturi bilan.

TypeScript'da generik T bilan qaytaruvchi tip belgilang.

TypeScript: SDK bilan rpc chaqirish
import { createClient } from '@storagedb/client';

const db = createClient(
  'https://storage.identify.uz/v1/<REF>',
  '<ANON_KEY>'
);

// Skalyar qaytar (number)
const { data, error } = await db.rpc<number>('qoshish', { a: 5, b: 7 });
if (error) {
  console.error('RPC xatosi:', error.message);
} else {
  console.log('Natija:', data); // 12
}
TypeScript: JSON parametr
// Postgres funksiya JSON parametrni qabul qiladi:
// CREATE FUNCTION public.save_metadata(user_id text, meta jsonb) RETURNS text ...

const { data, error } = await db.rpc<string>('save_metadata', {
  user_id: 'abc123',
  meta: { color: 'blue', size: 'large' } // JSON
});

if (!error) console.log('Saqlandi:', data);

RLC va autentifikatsiya

RPC chaqiruvi RLS (Row-Level Security) qoidalariga bo'ysinadi. Rol o'rnatiladi (anon_key uchun anon, service_key uchun service_role), JWT claim'lari session'ga o'rnatiladi.

service_key RLS ni chetlab o'tadi; barcha jadval va ustunlarga kirish mumkin.

anon_key foydalanuvchi identity'si bilan ishlaydi — JWT claim'lardagi auth.uid() mavjud bo'ladi va RLS qoidalari nazorat qiladi.

Funksiya ichida auth.uid() funksiyasini ishlating: SELECT auth.uid() — hozirgi foydalanuvchining UUID'sini qaytaradi.

SQL: RLS bilan funksiya
-- Shaxsiy ma'lumotlar jadvali (RLS bilan himoyalangan)
CREATE TABLE public.user_profile (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id uuid REFERENCES auth.users(id),
  name text,
  bio text,
  UNIQUE(user_id)
);

ALTER TABLE public.user_profile ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Foydalanuvchilar o'z profilini o'qishi" ON public.user_profile
  FOR SELECT USING (user_id = auth.uid());

-- RPC funksiyasi: foydalanuvchi o'z profilini yangilashi
CREATE FUNCTION public.update_my_profile(new_name text, new_bio text) RETURNS jsonb AS $$
DECLARE
  updated_row jsonb;
BEGIN
  UPDATE public.user_profile
  SET name = new_name, bio = new_bio
  WHERE user_id = auth.uid()
  RETURNING row_to_json(user_profile.*) INTO updated_row;
  
  RETURN updated_row;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
TypeScript: RLS bilan rpc
// anon_key ishlatib (autentifikatsiyalangan foydalanuvchi)
const { data, error } = await db.rpc<any>('update_my_profile', {
  new_name: 'Shohjahon',
  new_bio: 'O'zbek dasturchisi'
});

if (error) {
  // RLS ni polatka qilgan bo'lsa (masalan, bu profil o'zimning emas)
  console.error('Kirishga taqiq:', error.message);
} else {
  console.log('Profil yangilandi:', data);
}

SETOF va massiv qaytarish

Agar funksiya SETOF <type> yoki TABLE(...) qaytarsa, RPC natija massiv bo'ladi.

SETOF scalar tiplar (masalan SETOF text) massivga aylanadi.

SETOF composite tiplar (masalan SETOF user_profile) nesnalar massiviga aylanadi.

SQL: SETOF bilan funksiya
-- Barcha blog postlarini tag bo'yicha filtrlash
CREATE FUNCTION public.posts_by_tag(tag_name text) 
RETURNS TABLE(id uuid, title text, body text) AS $$
BEGIN
  RETURN QUERY
  SELECT p.id, p.title, p.body
  FROM public.blog_posts p
  JOIN public.post_tags pt ON p.id = pt.post_id
  WHERE pt.tag = tag_name;
END;
$$ LANGUAGE plpgsql;
TypeScript: SETOF natijasi
interface BlogPost {
  id: string;
  title: string;
  body: string;
}

const { data, error } = await db.rpc<BlogPost[]>('posts_by_tag', {
  tag_name: 'o\'zbek'
});

if (!error && Array.isArray(data)) {
  data.forEach(post => {
    console.log(post.title); // "O'zbekcha IT faqtlari", ...
  });
}