Supabase gives an app a Postgres database, a generated REST API and Realtime, but it has no way to pull rows out of SharePoint. Whitfield Reyes LLP keeps its Client Register, Matters and Conflicts Register there, and wants a client portal built on that data. This walkthrough uses SPSync to replicate the three lists into the firm’s Supabase project and then builds on the tables with Supabase’s own tools.
Writing the portal against the SharePoint list API is the alternative, and it means paginated calls, lookups that come back as ids and queries on non-indexed columns that can fail once a list passes 5,000 items. A Power Automate flow can copy rows until Matter Team, a multi-value person column, changes shape. Without either, someone exports to a spreadsheet and loads it by hand.
SharePoint for editing, Supabase for the app
The firm keeps editing in SharePoint. The portal reads from Supabase. SPSync reads the lists once in full and then follows them with delta queries, writing to Supabase Postgres on a schedule, so the app never calls SharePoint. The tables come from the lists: lookups and people become key columns, multi-value columns become child tables. The destination is covered on the SharePoint to Supabase page, and the wider pattern in building web and mobile apps on data the business keeps in SharePoint.
Connecting through Supabase’s pooler
Install the desktop app, or SPSync in your Azure for a tenant-wide setup, and sign in as a
user who already opens the three lists. Sign-in is delegated, so SPSync sees what that user
sees, and the permissions wizard lets an admin read what Sites.Selected grants before
approving it. SPSync never writes to SharePoint.
When you add the Supabase destination, copy the pooler host from the project’s Connect dialog. SPSync connects through the transaction pooler by default, and always with TLS. That default is ours. The shared pooler is reachable over IPv4 on every Supabase plan, and a sync run opens a connection, loads and disconnects. Supabase’s connection guide describes transaction mode for serverless and edge functions, session mode for persistent backends on IPv4-only networks, and the direct connection for migrations. A run through the transaction pooler on a live Supabase project has not been tested yet, so if the first run cannot connect, switch the destination to the session pooler or the direct connection. The direct connection is IPv6 only unless the project has Supabase’s IPv4 add-on, which the SharePoint to Supabase page covers with the other connection details. On the command line, sign-in is a device code with the token held in the OS keychain.
Matters, clients and fee earners as Postgres tables
Rows are keyed on their SharePoint item id. The Client lookup on Matters becomes
client_id, pointing at sp_clients. Matter Team becomes sp_matters__matter_team, one row
per fee earner per matter, in place of a delimited string. Relationship partners and fee
earners all resolve to sp_users, so the portal joins people the same way everywhere. New
SharePoint columns appear on the next run; columns removed in SharePoint stay in Postgres,
so the portal keeps working. Key columns have no foreign key constraints, which keeps load
order from failing a sync. Add constraints yourself once the schema settles, if your app
relies on them.
The first sync, seen in the table editor
Trigger the first run. It reads everything, and each run after it reads only what changed.
The events screen reports 150 clients, 500 matters and 350 conflicts register entries. A
table is committed in one transaction when its read completes, so the portal sees the
previous data or the complete new data. Open Supabase’s table editor and filter sp_matters
to one client’s open matters as you would in a spreadsheet. Joining sp_matters.client_id
to sp_clients.sp_item_id is a plain SQL query, which the list cannot run because it stores
the client as a lookup id.
The portal on the generated API and Realtime
Supabase generates a REST API over the replicated tables, and it regenerates after any
schema change a later run makes. A small web app lists a client’s open matters from that
endpoint without a backend of its own. Realtime can subscribe to sp_matters and show a
changed row once a run writes it. Row Level Security is yours to configure for the roles
the app reads with: SPSync writes the tables with its own role and leaves policies to you.
A Power BI or Metabase report can read the same tables if the partners want one.
Refreshing every five minutes
On a five-minute interval each run reads only the changes, so the portal trails SharePoint
by a few minutes at most. A full reconcile runs every seven days by default and re-checks
every item. You can also run one on demand to confirm the two sides agree, or after an
outage such as your own server failing or a Microsoft 365 incident. The desktop app and
SPSync in your Azure set the schedule from a picker; with the command line, spsync run
does one run and your scheduler decides how often.
Edits and deletions
An edit arrives with the next delta run. A matter deleted in SharePoint is soft-deleted in Supabase, kept and flagged, so the app decides whether to hide it or show it as closed. If you want Supabase to drop deleted rows outright, the command-line tool can hard delete.
SPSync can also send the same lists to other destinations, one replication each, all listed on the destinations page. If yours is not there, tell us. Book a call to go through your own lists; a trial in your tenant can be arranged on the call.
Connect to SharePoint and sign in
What you see: A partner's delegated sign-in to the Whitfield Reyes LLP site, then the permissions wizard's one-page pack.
Why it matters: Client and matter data is read under a partner's existing permissions, which suits a firm that has to account for every copy of client data.
What it replaces: A broad Sites.Read.All grant that would expose every client site in the tenant.
Pick the lists and the Supabase destination
What you see: Client Register, Matters and Conflicts Register ticked together and pointed at the Supabase Postgres database.
Why it matters: One connection string covers all three lists, and SPSync creates and alters the tables itself, so nobody writes a schema by hand first.
What it replaces: A Power Automate flow per list that stops copying when Matter Team changes.
Tick Client Register, Matters and Conflicts Register, name the replication and choose the Supabase destination.
Add the destination with the pooler host from your Supabase project's Connect dialog. SPSync uses the transaction pooler unless you choose the session pooler or the direct connection.
Write the replication config for all three lists against Supabase in one file.
spsync export-config --output professional-services.yaml --pipeline-name spsync-professional-services-supabase --destination-kind postgres --dataset-name professional_services --client-id $SPSYNC_CLIENT_ID --connection-string-env SPSYNC_DEST_CONNECTION_STRING --site-url https://whitfieldreyes.sharepoint.com/sites/demo-professional-services --lists-by-name "Client Register,Matters,Conflicts Register"Check the Graph permissions and the Supabase connection before the first run.
spsync doctor professional-services.yaml
Run the first sync
What you see: 150 clients, 500 matters and 350 conflicts register entries written, broken out by list on the run's own events screen.
Why it matters: A matter opened in SharePoint reaches the portal on the next five-minute run, with no full reload of the lists.
What it replaces: A spreadsheet export loaded into Postgres by hand.
Click Save and run, open the run's own events and watch the per-list row counts arrive: 150 clients, 500 matters, 350 conflicts register entries.
Set the schedule to five minutes so the three-list replication checks for changes twelve times an hour.
Run the replication; the terminal shows the full load first, then delta counts on later runs.
spsync run professional-services.yaml
See the result: lookups, child tables and joins
What you see: sp_matters joined to sp_clients in a SQL query, and the Matter Team column arriving as its own child table.
Why it matters: The portal needs each matter with its client and fee earners, and Postgres can serve that in one query.
What it replaces: Lookup ids resolved one call at a time against the SharePoint list API.
Open the destination database and browse sp_clients, sp_matters and sp_conflicts_register; each keeps the SharePoint item id as its key.
Run a join between sp_matters and sp_clients on client_id, something no SharePoint list view answers directly.
Query the Supabase database directly to confirm the join and the row counts.
psql "$SPSYNC_DEST_CONNECTION_STRING" -c "select m.title, c.title, c.sector, m.status from professional_services.sp_matters m join professional_services.sp_clients c on m.client_id = c.sp_item_id limit 10"
Use the replica: table editor, auto API and Realtime
What you see: Supabase's table editor on the replicated tables, a small web app reading sp_matters through the auto-generated REST API, and a Realtime subscription on the same table.
Why it matters: App developers work with the Supabase tools they already know, and SharePoint throttling never reaches the portal.
What it replaces: A client portal built straight on the SharePoint list API, with paginated calls and opaque lookup ids.
Open sp_matters in Supabase's table editor and filter it to open matters for one client, the way you would in a spreadsheet.
Point a small web app at Supabase's auto-generated REST API for sp_matters, and subscribe to the table with Realtime.
Keep the replication current from a script or CI job, independent of when anyone opens the app.
spsync run professional-services.yaml