Back to Data, Analytics & Infrastructure

DatabasesSupabase and Postgres

Two live apps, built on Supabase and Postgres.

Supabase is a hosted Postgres database with sign in, file storage and live updates built in. I run two things on it, each with its own database: the assistant on this site, and Velora, my expense tracker. In both, the rules that must never break, like who may see which data, are checked by the database itself, not by the app.

Projects
Two, separate, both live
Database
Postgres 17, with search by meaning
Shared between them
No data. One way of working
Access
Decided by the database
01

Each person sees only their own data

Velora keeps several people's expenses in one table. Below, the same request is sent three times by three different people, and three different answers come back. The database decides who gets what, so no screen or app can forget to check.

A tower at night. Almost every window is dark and one of them is lit.
Every row is in the table, the way every room in that building is there. What each person gets back is the one that is theirs.Photograph by LISK OBE on Unsplash

One request, three people

select id, merchant, amount
  from expenses
 order by created_at desc;

In plain words: give me every expense, newest first.

Notice that no line says which person. That is not a trick. The database already knows who is asking.

Who is asking

  • Streaming subscription€15.99
  • Groceries€84.20
  • Boiler repair€86.40

First account: 3 What comes back

Values are illustrative. The mechanism is real. Because the rule lives in the database, forgetting it returns an empty result rather than somebody else's rows.
02

What I built, in nine pieces

Across the two projects. Five belong to the assistant on this site, four to Velora.

  • Assistant

    Search by meaning

    Writing is stored as numbers, so a question finds the passage that means the same thing even when the words are different.

  • Assistant

    The database does the search

    The database ranks the matches and hands back the few that count. The app asks a question instead of doing the maths.

  • Assistant

    A second, looser search

    A strict search runs first. When nothing is close enough, one looser search runs rather than answering with nothing.

  • Assistant

    Facts in plain tables

    Six small tables hold the facts about me, so my job title comes from a row and never from a model's memory of one.

  • Velora

    Each person sees only their own rows

    The database decides which rows a signed in person may read, on every request, so no app has to remember to check.

  • Velora

    One step fixes every past expense

    Change a recurring price and one routine goes back and corrects every expense that rule already covers.

  • Velora

    Delete now, remove for good later

    Deleting hides a row for thirty days. A scheduled job then removes what is older, behind a secret only that job knows.

  • Velora

    Live on every device

    Each edit reaches every open session as it happens, so two devices looking at the same month never disagree.

  • Assistant

    Facts come from one reviewed file

    Those fact tables are rebuilt from a file kept with the code, so what is live cannot quietly drift from what was reviewed.

03

How both databases are run

Four rules I follow in both projects.

  1. 01

    Let the database decide who sees what

    In Velora, access rules live in the database and Postgres checks them on every request. The assistant has no user accounts, so its tables are closed to the outside and read only from the server.

    Prevents: No leak from a forgotten check

  2. 02

    One job per table

    The search index and the facts about me are separate tables, because they answer different questions and fail in different ways.

    Prevents: No table doing two jobs

  3. 03

    The database does the hard parts

    Anything that has to be exactly right is a routine inside Postgres. The app asks for an answer instead of assembling one.

    Prevents: No correctness that depends on which app is asking

  4. 04

    Facts come from one reviewed file

    The fact tables are rebuilt from a file kept with the code, so there is one place to change a fact and one place to review it.

    Prevents: No fact that exists only in the live database

04

My role

Both projects are mine end to end, so the layout of both databases, the routines, the access rules and the releases are all decisions I made and had to live with.

  • 01Designed the layout of both databases: what each table is for, what it holds, and what belongs somewhere else.
  • 02Wrote the Postgres routines, including the search behind this site's assistant and the correction behind Velora's recurring rules.
  • 03Wrote the access rules and chose what happens when a check is missing: an empty result rather than an error, so a gap is silent for an attacker and loud for the tests.
  • 04Split the assistant's knowledge in two, a search index for prose and plain tables for facts, after the model kept improvising a job title.
  • 05Handled the boring half: separate keys for separate privileges, a scheduled clean up behind a secret, and a cap on how often the paid step can be called.

The first thing I would change is how the databases are updated. Both grew by hand, which works while one person holds the whole picture and stops working the moment that is not true.

05

What changed

2

Separate databases, run the same way

A screen added tomorrow cannot forget the access check. A second app cannot skip the fix. A question about my job title is answered from a row, so the worst case is that the row is out of date rather than that the answer was made up.

Before
After
Every screen had to remember to check who was asking
The database checks on every request
A price change fixed one row at a time
One step corrects every expense the rule covers
Every record downloaded to the app before anything was compared
The database returns the few that match
Facts edited by hand, drifting quietly
Facts rebuilt from a file anyone can review

0

Access checks living outside the database

1

Step to fix every past expense

6

Fact lists checked before the assistant answers

06

Built with

  • Postgres 17
  • pgvector
  • Supabase
  • TypeScript
  • Next.js
  • The Supabase MCP server