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
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.
- A file lands in storage and a row is written to documents.
- An Edge Function chunks it, embeds the chunks and writes the vectors.
- A question calls the match function as the signed-in user, so RLS limits results to their tenant.
- The model answers from the returned chunks, citing each chunk’s document.
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
- 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
- storage
- functions
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
- 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
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.
Where it is used
Related use cases, services and guides
Limits
Limits and common errors
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

