<!-- https://svgrid.com/docs/enterprise/studio/supabase/ - SvGrid documentation as markdown. Index of every page: https://svgrid.com/llms.txt -->

# Supabase

[Supabase](https://supabase.com) gives you a hosted PostgreSQL database with a
generous free tier and a browser-safe client. This guide takes you from an empty
Supabase project to a live CRUD screen, step by step. No backend to run.

![From an empty Supabase project, create a table, open access with Row-Level Security, and pass the anon key to createSupabaseDataSource to wire hosted Postgres straight into the grid.](https://svgrid.com/docs-media/studio-supabase.svg)

> **Try it first:** the [Supabase demo](https://svgrid.com/demos/194-studio-supabase/)
> is exactly this - paste your project URL + anon key and it runs against your
> real database in the browser.

> **Want it all in one page?** The
> **[Supabase CRUD grid tutorial](https://svgrid.com/docs/enterprise/studio/supabase-grid/)** is the complete,
> copy-paste walkthrough - the table, the schema, the data source, and the full
> grid page with create / edit / delete, start to finish. This page is the deeper
> reference behind it.

**Or let Studio write the whole app.** One command reads the tables you name
and writes a SvelteKit 3 app with list and edit screens for each one (see the
[Studio CLI](https://svgrid.com/docs/enterprise/studio/cli/)):

```bash
npx @svgrid/studio init --kit 3 --supabase-url <url> --supabase-key <anon key>
```

The short below runs it against the [Northwind sample](https://svgrid.com/docs/enterprise/studio/supabase-sample/).

> Video: Friday, 5:47 PM: a Supabase admin panel by Monday (https://www.youtube.com/watch?v=OsR_dvtlbbM)

**All the Supabase pages, one map.** This guide is the canonical entry point -
connect, keys, and Row-Level Security. From here:

- [Supabase CRUD grid](https://svgrid.com/docs/enterprise/studio/supabase-grid/) - the copy-paste one-page tutorial
- [Supabase Northwind sample](https://svgrid.com/docs/enterprise/studio/supabase-sample/) - a relational practice dataset to load and explore
- [Real-time](https://svgrid.com/docs/enterprise/studio/realtime/) - live grid over Supabase change streams
- [Auth & secured screens](https://svgrid.com/docs/enterprise/studio/auth/) - Supabase Auth + RLS per signed-in user
- [Databases](https://svgrid.com/docs/enterprise/studio/databases/#supabase) - the server-side connection-string route instead

There are two ways to connect, and you can use either:

| Path | When | Runs in |
| --- | --- | --- |
| **A. `supabase-js` client** | SPAs, and when you want Row-Level Security + auth enforced per user | The browser |
| **B. SvelteKit server route** | You already use SvelteKit `+server.ts` and want SQL on the server | Node / edge server |

Path A is what the demo uses and what most people want. Start there.

---

## Step 1 - Create a project

1. Sign in at [supabase.com](https://supabase.com) and click **New project**.
2. Give it a name and a database password, pick a region, and wait ~2 minutes
   for it to provision.

## Step 2 - Create and seed the table

Open **SQL Editor** in the Supabase dashboard, paste this, and click **Run**. It
creates a `customers` table and adds a few rows.

```sql
create table if not exists customers (
  id     bigint generated always as identity primary key,
  name   text not null,
  email  text not null,
  tier   text,
  mrr    integer,
  active boolean default true
);

insert into customers (name, email, tier, mrr, active) values
  ('Ada Lovelace',    'ada@analytic.io',      'enterprise', 1200, true),
  ('Alan Turing',     'alan@bletchley.uk',    'pro',         240, true),
  ('Grace Hopper',    'grace@navy.mil',       'enterprise',  980, true),
  ('Edsger Dijkstra', 'edsger@shortest.path', 'free',          0, false);
```

## Step 3 - Open access with Row-Level Security

Supabase blocks all access by default until you add a **policy**. Because the
anon key is public (it ships in the browser), RLS is what actually protects your
data - so you always keep it on and grant exactly what you intend.

For a public demo, allow the anon role to read and write:

```sql
alter table customers enable row level security;

create policy "svgrid demo access" on customers
  for all to anon using (true) with check (true);
```

> **For a real app**, do not grant blanket access to `anon`. Grant to
> `authenticated` and scope rows to the signed-in user, e.g.
> `using (auth.uid() = user_id)`. See the
> [Supabase RLS docs](https://supabase.com/docs/guides/database/postgres/row-level-security).

## Step 4 - Get your keys

In the dashboard: **Project Settings -> API**. Copy two values:

- **Project URL** - `https://xxxxxxxxxxxx.supabase.co`
- **anon public** key - a long token starting with `eyJ...`

The anon key is designed to be shipped in the browser. Never ship the
**service_role** key to a client - it bypasses RLS.

---

## In the designer (no code)

The visual designer wraps all of the below. Bind an entity to **Supabase**, open
its **Configure** builder, and:

- Paste the **Project URL** and **anon public** key once - they are stored as a
  **shared project connection**, so every Supabase entity reuses them (you only
  set them one time).
- Click **List tables** to read the tables from your project, pick one, and
  **Import schema** to pull its real columns, primary key, foreign-key relations,
  and enums into the entity.
- **Preview** loads live rows onto the canvas. This works in the **online**
  designer too, because Supabase is reached over HTTP.

**Generate app** emits `createClient` in `connections.ts` reading
`PUBLIC_SUPABASE_URL` / `PUBLIC_SUPABASE_ANON_KEY` from `.env` (the key is never
inlined into source), plus a `.env.example`. Tick **Live updates (Realtime)** to
also emit a live subscription ([Real-time](https://svgrid.com/docs/enterprise/studio/realtime/)), and use the
[Supabase Auth provider](https://svgrid.com/docs/enterprise/studio/auth/) to gate the app.

---

## Path A - Connect from the browser (`supabase-js`)

This is a SvelteKit page (or any Svelte SPA). Install the client:

```bash
npm i @supabase/supabase-js
```

Put your keys in `.env` (Vite exposes `PUBLIC_`-prefixed vars to the browser):

```bash
PUBLIC_SUPABASE_URL="https://xxxxxxxxxxxx.supabase.co"
PUBLIC_SUPABASE_ANON_KEY="eyJhbGciOiJI..."
```

Build the data source with **`createSupabaseDataSource`** - it maps the grid's
sort, filter, paging, and CRUD onto the Supabase query builder for you. You bring
the client (so RLS + auth apply per request); it's the whole data layer:

```ts
// src/lib/customers.source.ts
import { createClient } from '@supabase/supabase-js'
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
import { createSupabaseDataSource } from '@svgrid/enterprise'
import { customersSchema, type Customer } from '$lib/customers'

const client = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY)

export const customersSource = createSupabaseDataSource<Customer>({
  client,
  table: 'customers',
  schema: customersSchema,
  // searchColumns: ['name', 'email'],  // optional; defaults to text + enum fields
})
```

Then wire it exactly like the in-memory source from
[Getting started](https://svgrid.com/docs/enterprise/studio/getting-started/) - the page code is identical:

```ts
const controller = createServerDataSource(customersSource, {
  pageSize: 10, optimistic: true, getRowId: (r) => String(r.id),
  onChange: (s) => (view = s),
})
controller.refresh()
```

That is the entire integration. Sorting, filtering, paging, and CRUD now run
against hosted Postgres, with RLS enforced on every request.

> **Don't know the columns ahead of time?** `introspectSupabaseTable({ url, key,
> table })` reads them from PostgREST and returns an `EntitySchema` you can feed
> to `schemaToColumns` and `createSupabaseDataSource` - that's how the
> [live demo](https://svgrid.com/demos/194-studio-supabase/) adapts to any table.
> It also reads **foreign keys**, so a FK column comes back as a `relation` field
> and the form gets a searchable picker automatically - see [Relations](https://svgrid.com/docs/enterprise/studio/relations/).

> **Want it live?** Add [`createSupabaseRealtime`](https://svgrid.com/docs/enterprise/studio/realtime/) to refetch and
> flash rows whenever the database changes - no polling.

---

## Path B - SvelteKit server route (connection string)

Prefer to keep SQL on the server? Supabase is plain Postgres, so use the `pg`
driver with the **connection pooler** string (Dashboard -> Project Settings ->
Database -> Connection string, port `6543`). The Studio CLI writes the whole
route for you:

```bash
npm i pg
export DATABASE_URL="postgres://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres"
npx @svgrid/studio add customers --db supabase --url "$DATABASE_URL"
npm run dev            # open /customers
```

This generates the schema, a `+server.ts` API route backed by
[`createSqlDataSource`](https://svgrid.com/docs/enterprise/studio/databases/), and the page. Keys stay on the server;
the browser only talks to your own route. See [The Studio CLI](https://svgrid.com/docs/enterprise/studio/cli/) and
[Databases](https://svgrid.com/docs/enterprise/studio/databases/) for the full picture.

---

## Troubleshooting

| Symptom | Cause and fix |
| --- | --- |
| **On connect it shows no data and only offers to "add columns"** - even though the table has columns and rows | The demo infers your columns *from the rows it fetches*, so an RLS-blocked read means it sees neither rows nor columns and falls back to manual entry. Add the `select` policy from Step 3, then reconnect. Confirm it's RLS by running `select * from your_table;` in the SQL Editor (that bypasses RLS): if it returns rows but the demo shows none, RLS is the cause. |
| **Grid is empty, no error** | RLS is on but no policy grants `select`. Add the policy in Step 3, or scope one to your users. |
| **`new row violates row-level security`** on create | Your policy allows `select` but not `insert`. Use `for all` (as above) or add a `with check` policy for writes. |
| **"Could not reach Supabase"** | Wrong Project URL, or a network / CORS issue. Confirm the URL is the `https://xxxx.supabase.co` form, not the database host. |
| **401 / invalid key** | You pasted the wrong key. Use the **anon public** key from Settings -> API, not the service_role or the JWT secret. |
| **Numbers arrive as strings** | Postgres `numeric` comes back as text over PostgREST. Use `integer` / `bigint` for numeric columns, or cast in a view. |

More answers in [Troubleshooting & FAQ](https://svgrid.com/docs/enterprise/studio/troubleshooting/).

---

## See also

- [Supabase demo](https://svgrid.com/demos/194-studio-supabase/) - this guide, live
- [Databases](https://svgrid.com/docs/enterprise/studio/databases/) - the SQL sources and dialects
- [Data binding](https://svgrid.com/docs/enterprise/studio/data-binding/) - the `ServerDataSource` contract
- [Getting started](https://svgrid.com/docs/enterprise/studio/getting-started/) - the page code, end to end

---

SvGrid is the Svelte 5 data grid (`npm install @svgrid/grid`, MIT). This page: https://svgrid.com/docs/enterprise/studio/supabase/ . All docs: https://svgrid.com/llms.txt
