Skip to content

Integration · Data & infra

Supabase and pgvector: one Postgres for your data and its embeddings

We use Supabase when an AI feature needs app data and embeddings in the same database. Documents, chunks and vectors sit next to the users and tenants they belong to, and row-level security decides what each request can retrieve before the model sees a word.

Supabase

Data & infra

In our build stackA tool we build with. Not a partnership, certification or reseller status.

Access

Authentication and least-privilege scopes

The publishable key in the browser with Supabase Auth sessions; the secret key only in server code and Edge Functions

  • publishable key: RLS-governed
  • secret key: server only
  • auth: JWT per user
  • db roles: least privilege

The secret key bypasses row-level security, so it never ships to a browser, a mobile app or a repository.

Objects

What we read and what we write

  • documents table

    Read: YesWrite: Yes

    Source files and metadata, with tenant and owner.

  • chunks table + vector column

    Read: YesWrite: Yes

    Text chunks with one embedding each.

  • HNSW index

    Read: YesWrite: No

    Approximate nearest-neighbour search on the embedding column.

  • RLS policies

    Read: YesWrite: No

    Who may select, insert or update each row, checked on every query.

  • match function (SQL, via RPC)

    Read: YesWrite: No

    Similarity search filtered by tenant, run as the caller.

  • Edge Function

    Read: YesWrite: Yes

    Embeds new chunks and calls the model with a server-side key.

Data flow

System of record and write-back

Postgres is the system of record for both the app data and the embeddings, so deleting a document removes its chunks and vectors in the same transaction.

  1. A file lands in storage and a row is written to documents.
  2. An Edge Function chunks it, embeds the chunks and writes the vectors.
  3. A question calls the match function as the signed-in user, so RLS limits results to their tenant.
  4. The model answers from the returned chunks, citing each chunk’s document.
sqlTenant isolation on the chunks table (sample policy)
alter table chunks enable row level security;

create policy "tenant members read their chunks"
on chunks for select to authenticated
using (
  auth.uid() is not null
  and tenant_id in (select tenant_id from memberships where user_id = auth.uid())
);

create index on chunks using hnsw (embedding vector_cosine_ops);

Worked example

Supabase in a build

Schema tree for a tenant-safe RAG store: tenants, memberships, documents and chunks tables, the embedding column, HNSW index, RLS policy, match function, storage bucket and an Edge Function.
Schema for a tenant-safe RAG store (Sample data)Sample data
  • public
  • tenantsone row per customer account
  • membershipsuser to tenant, used by every policy
  • documentspath, title, tenant, updated time
  • chunkstext, document, tenant
  • embeddingpgvector column
  • hnsw indexcosine distance
  • policy: tenant members readRLS on every query
  • match_chunks()similarity search as the caller

Sample workflows

What teams build with Supabase

  • Pattern 01

    A help assistant per customer account

    Each customer gets answers drawn only from their own documents, because the search runs under their session and RLS filters the rows.

  • Pattern 02

    Search across tickets and notes

    Support history is embedded nightly, and agents search it by meaning from inside your app.

  • Pattern 03

    Document answers with deletion that sticks

    Deleting a file removes its chunks and vectors in one transaction, so an answer cannot cite something that was removed.

Failure handling

What breaks, and what the build does about it

  • A filtered search returns fewer matches than asked for

    Selective filters can thin an approximate index’s results, so we use iterative scans where available or over-fetch, and test recall on your data.

  • Embeddings go stale after an edit

    A change to a document re-embeds only its changed chunks, and stale rows are flagged until that finishes.

  • An Edge Function hits its time or memory limit

    Big files are chunked through a queue across several invocations instead of one long run.

Fit · Supabase

When it is the wrong choice

Saying no early is cheaper than rebuilding later.
  • You already run Postgres where pgvector is available. Add the extension there instead of a second database.
  • Hundreds of millions of vectors under heavy query load. A dedicated vector store may fit better; test before deciding.
  • Nobody on the team can write and review RLS policies. Keep queries server-side until someone can own them.

Alternatives worth pricing

  • Cloudflare D1 + Vectorize

    Edge-first apps on Workers, as on this website.

  • Postgres you already run

    Same pgvector approach, inside the database you operate today.

  • A dedicated vector database

    Very large indexes where vector search is the main workload.

Limits

Limits and common errors

Vendor limits change; each line cites the vendor page and the date we checked it.
  • pgvector adds a vector column type and distance operators; enable it from the dashboard or with create extension vector.

    Source: pgvector: Embeddings and vector similarity (opens in a new tab) · Supabase Docs · accessed

  • Publishable keys are safe in a browser; secret keys give full access and bypass row-level security, so they must never ship in a browser, an app or source control.

    Source: Understanding API keys (opens in a new tab) · Supabase Docs · accessed

  • Enable RLS on every table in an exposed schema. With RLS on and no policies, nothing is readable through the publishable key; auth.uid() is null for anonymous requests.

    Source: Row Level Security (opens in a new tab) · Supabase Docs · accessed

  • HNSW is the recommended index and can be built right after the table is created; selective filters can return fewer rows than the limit.

    Source: HNSW indexes (opens in a new tab) · Supabase Docs · accessed

  • Edge Functions get 256 MB of memory and 2 s of CPU time per request, with a wall-clock limit of 150 s on Free and 400 s on paid plans.

    Source: Edge Function limits (opens in a new tab) · Supabase Docs · accessed

Security

Security notes

  • RLS is on for every exposed table, and policies check that auth.uid() is not null before comparing ids.
  • Storage policies mirror the table policies, so a user who cannot read a document row cannot download its file.
  • The secret key lives only in server code and Edge Functions and is rotated when someone leaves.

FAQ

Frequently asked questions

Why keep embeddings in Postgres instead of a separate vector database?

One database means one set of permissions, one backup and one delete. The row-level security that protects your app data also protects the vectors, and one query can filter by tenant, date or status.

How does row-level security protect a RAG chatbot?

The similarity search runs as the signed-in user, so Postgres applies the same policies it uses for every other query. A user in one tenant cannot retrieve another tenant’s chunks, whatever the prompt asks for.

Is the Supabase publishable key safe to put in the browser?

Yes, as long as RLS is on and the policies are right, because the key only grants what the policies allow. The secret or service-role key bypasses RLS, and it stays on the server.

Which embedding model do you use with pgvector?

Whichever tests best on your documents and language mix, from a hosted API or a model in your network. The column’s dimension follows the model, so that choice is fixed before loading data at scale.

Can you add AI search to our existing Supabase project?

Usually, yes. We enable the vector extension, add chunk tables beside your current schema, write policies that reuse your memberships and backfill embeddings in batches.

Next step

Have a workflow in mind? Let’s scope it together.

Book a free 30-minute strategy call. Bring one process you want to automate and we will talk through scope, integrations and a sensible first step.

Free 30-minute call · no commitment

Send an RFP or security questionnaire