Cloud

Database

Postgres 15 database with automatically generated tables, RLS policies, and types. Full SQL, but without writing SQL.

Introduction

Every Master project has a dedicated Postgres instance in the EU region. You create tables by describing them in the chat.

Creating a table

prompt
Add a posts table:
- title (text)
- body (text)
- author_id (uuid → auth.users)
- published (boolean, default false)
- created_at (timestamp)
sql
create table public.posts (
  id uuid primary key default gen_random_uuid(),
  title text not null,
  body text not null,
  author_id uuid not null references auth.users on delete cascade,
  published boolean not null default false,
  created_at timestamptz not null default now()
);

alter table public.posts enable row level security;

Row-level security

sql
create policy "Authors can read own posts"
on public.posts for select
using (auth.uid() = author_id);

create policy "Public can read published"
on public.posts for select
using (published = true);
Never store user roles in the profiles table. Always use a separate user_roles with a security definer function has_role().

Reading and writing

ts
import { supabase } from "@/integrations/supabase/client";

const { data, error } = await supabase
  .from("posts")
  .select("id, title, body, created_at")
  .eq("published", true)
  .order("created_at", { ascending: false })
  .limit(20);

Realtime

ts
const channel = supabase
  .channel("posts-changes")
  .on("postgres_changes", { event: "*", schema: "public", table: "posts" }, (payload) => {
    console.log("Change:", payload);
  })
  .subscribe();

Migrations and versions

Every schema change creates a new migration. Migrations are versioned, deterministic, and applied in order.

Tips and best practices

  • Index foreign keys — Postgres je neindexuje automaticky.
  • Use UUID as the primary key for public tables.
  • Neselectuj * — vyjmenuj sloupce.
  • Limit query — default limit is 1000 rows.