Skip to content

Tables and schema

Every project is a Postgres database. Create tables in the dashboard (table editor or SQL editor), with migrations or through the Management API.

A new table in an exposed schema is unreadable twice over:

  1. RLS is switched on automatically for every CREATE TABLE in public (and api, if you expose it). RLS with no policy means deny all.
  2. No table grants for anon or authenticated. You grant privileges and write policies yourself. See row level security.

Functions follow the same rule: no function is executable by PUBLIC. An RPC becomes callable only after grant execute ... to authenticated (or anon).

Only public is exposed to the REST API by default. You can opt in to an api schema in the project’s Data API settings and select it in the SDK:

const base = createClient({ url, key, db: { schema: "api" } })

The schemas auth, storage, realtime, base and extensions can never be exposed.

create table public.orders (
id bigint generated by default as identity primary key,
user_id uuid not null default auth.uid(),
total numeric not null,
created_at timestamptz not null default now()
);
create index on public.orders (user_id);
grant select, insert, update, delete on public.orders to authenticated;

Keep schema changes as versioned SQL files and apply them with potalab base db push or the Management API. Pick one path per project. See the CLI.

potalab base gen types turns your schema into a Database type for createClient<Database>(). Nullable columns become | null, columns with defaults are optional on insert, and identity ALWAYS and generated columns are typed never.

The dashboard Advisors page and potalab base lint report tables without RLS, unwrapped auth.uid() calls, missing indexes and more. See row level security.