Docs

Database

Your project's Postgres database: create tables in the console, query them safely from your app, connect tools directly, and keep the schema under version control.

Updated Oct 13, 2026Open in console

Every project has its own dedicated Postgres database in the region you chose. You can shape and browse it in the console, read and write it from your app through the SDKs, script it with the sov CLI, and connect any Postgres tool to it directly.

ToolUse it for
Table EditorCreate tables and columns, browse, filter, sort and edit rows, set row access with one click.
SQL EditorAny SQL: queries, views, functions, policies, multi-statement scripts. Saved and shared queries.
SchemaTables, relationships (visualizer), functions, triggers, webhooks, indexes and extensions.
MigrationsThe history of every schema change, with rollback (Migrations).
SDK data.sql()Queries from your app, as the signed-in user, with bound parameters.
sov dbScripting: run SQL, list tables, pull the schema, push migrations, seed data.
Direct connectionpsql, ORMs, BI and migration tools (Connect → Database).

Create and edit tables in the console

Open Table Editor. The left column lists the tables of the selected schema (public by default); pick one to browse its rows. Use Filter rows and Sort to narrow them down, Insert to add a row, and click a cell to edit it.

Table Editor showing the products tableTable Editor showing the products table
A table open in the Table Editor: column names with their types, paging, filtering, sorting and Insert.

New table

New table opens a panel where you set the name, an optional description, the columns and who may access the rows:

Create a new table panelCreate a new table panel
Each new table starts with id (primary key) and created_at. Pick a Row access preset so your app can use the table.
  • Columns — name, type, default value, primary key, nullable and unique. Types include numbers (int2/int4/int8, numeric, float4/float8, serials), text (text, varchar), dates and times (date, timestamptz, interval, …), bool, uuid, json/jsonb, bytea, network and geometric types.
  • Foreign keys — link a column to another table's column.
  • Row access — presets that create access policies for this table (below). Without one, app users can't use the table.
Row access presetEffect
Guest readAnyone, signed in or not, can SELECT.
Guest insertAnyone can INSERT (public forms, waitlists).
Signed-in readSigned-in app users can SELECT.
Signed-in CRUDSigned-in app users can select, insert, update and delete.
Owner onlySigned-in users only see and change rows whose owner_id is their user id (enforced by row-level security in the database).
Block guestsRejects all requests from people who are not signed in.

SQL Editor

SQL EditorSQL Editor
Queries are organised in Favorites, Shared with team, Private, Templates and a browsable Database tree.
  • Run with ⌘/Ctrl + Enter. Runs the whole script, or only the selected text. All statements of a script run in one transaction: if one fails, nothing is kept.
  • Mode: Run executes; Explain shows the query plan without running it; Dry run runs the script and rolls it back, so you can check a change safely.
  • Auto LIMIT adds LIMIT 100 (configurable) to queries without one, so a big table can't flood your browser.
  • Save keeps a query privately or shares it with your team; Format tidies the SQL.
  • Schema changes made here (create, alter, drop) are recorded as Migrations automatically.
  • Owners and admins can change the schema; other roles can run queries but not schema changes.

Query from your app

data.sql() sends one statement with its values. Placeholders $1, $2, … are bound by the database: a value can never become part of the SQL, which is what protects you from SQL injection. Never build SQL by concatenating user input.

// Read
const { columns, rows } = await sovaria.data.sql(
  'select id, name, price_cents from products where price_cents <= $1 order by name limit $2',
  [5000, 20],
)
// rows: [[42, 'Notebook', 1299], ...] in the order of `columns`
const products = rows.map((r) => Object.fromEntries(columns.map((c, i) => [c, r[i]])))

// Write — "returning" gives you the new row back
const { rows: [[id]] } = await sovaria.data.sql(
  'insert into products (name, price_cents) values ($1, $2) returning id',
  ['Notebook', 1299],
)

Response

{
  "columns": ["id", "name", "price_cents"],
  "rows": [[42, "Notebook", 1299]],
  "row_count": 1,
  "txid": 0
}

What apps can run

  • SELECT, INSERT, UPDATE, DELETE, EXPLAIN and read-only WITH queries — one statement per request.
  • Schema changes (create, alter, drop) are not allowed from app code: make them in the console, with migrations or the CLI.
  • Writes can't be hidden inside WITH, EXPLAIN ANALYZE is refused (it would execute the statement), and app users can't read system catalogs.
  • Up to 100 parameters and 64 KB of SQL per request. Values may be strings, numbers, booleans, null, or JSON objects/arrays (sent as jsonb). When Postgres can't infer a type (e.g. select $1), add a cast: $1::int.

Who can read and write which rows

App users and the anon key have no access to a table until you allow it. Two layers decide:

  1. Access policies (Policies page, or a Row access preset) decide whether a caller may run SELECT/INSERT/UPDATE/DELETE on the table at all.
  2. Row-level security in Postgres decides which rows: with RLS enabled and forced on a table, your create policy rules filter every query, and Sovaria lets them decide when no access policy does.

Console members (in the SQL editor and Table Editor) and the service role key are not limited. Details and patterns: Row-level security and access.

Schema, relationships and indexes

Schema pageSchema page
Schema lists tables with column and foreign-key counts. Tabs show the Visualizer (relationship diagram), database Functions, Triggers, Webhooks, Indexes and Extensions.
  • Add indexes for columns you filter or join on, and for columns used in row-level security policies.
  • Webhooks call a URL when rows are inserted, updated or deleted.
  • Extensions lists the Postgres extensions you can enable (for example pgcrypto, pg_trgm).

Connect directly with Postgres tools

Connect panel, Database tabConnect panel, Database tab
Connect → Database: the direct and pooled connection strings and a ready psql command. Get or reset the password under Project settings → Database.
ConnectionUse for
Direct (<ref>.db.sovaria.in:5432)Migrations, admin tools, long-lived servers.
Pooled (transaction mode)Serverless functions and apps with many short connections. Session features (prepared statements across transactions, LISTEN) are not available in transaction mode.

Database from the CLI

sov db tables                                   # tables in the linked project
sov db sql "select count(*) from orders"         # run SQL
sov db create-table reviews '[{"name":"id","type":"SERIAL","primary_key":true},{"name":"body","type":"TEXT"}]'
sov db pull                                      # write the current schema as a migration snapshot
sov db diff                                      # compare the live schema with that snapshot
sov db seed                                      # run your seed SQL
sov db migrations new add_reviews                # new timestamped migration file
sov db migrations push                           # apply pending migrations

Every command and option: CLI reference.

React to changes

Turn on Realtime → Database change data capture for a table to push its inserts, updates and deletes to subscribed clients — they only receive rows they may read. See Realtime.

Errors

StatusMeaningWhat to do
400Invalid SQL, a failed constraint, more than one statement, a schema change from app code, or a bad parameter.Read detail; fix the statement.
401Missing or expired session or key.Sign in again (the SDK refreshes sessions automatically).
403 … NO_POLICY_DEFINEDNo access policy and no forced RLS covers this table for this caller.Add row access for the table — Row-level security and access.
403 … POLICY_DENYAn access policy explicitly denied it.Check Policies, or test the request in Simulation.
408 / timeoutThe statement ran longer than your plan allows.Add an index, add a limit, or move heavy work to a migration.
Something unclear or missing on this page?Tell us