Heiko Polinski

postgres

Where does your data actually live?

A plain-language tour of how a database works. The example is a real, small app: a chat window for AI models that kept everything inside the browser, until the day that stopped being enough.

There is no code to write. You will, however, be handed receipts.

The problem a database solves

This chat app used to keep every conversation in a small storage box that browsers offer to websites. The box is called localStorage, and it sits inside one specific browser on one specific machine.

5 MB
001

It is tiny.5 MB

About five megabytes in total, for everything. Two AI-generated images and the box is full.

5 MB
002

It is stuck.1 browser

The box belongs to one browser on one machine. Open the app anywhere else and there is simply nothing there. No sync, no copy, no way out.

5 MB
003

It is fragile.0 backups

Clear your browsing data and your chats are swept out along with the cookies. There is nowhere else they exist.

The fix is to move the data out of the app entirely, into a database: a guarded, organized place that lives on a server on the internet. The size limit effectively disappears, closing the browser deletes nothing, and any machine that logs in sees the same chats.

your laptopany other machinethe database
One guarded place in the middle. Every screen is just a view of it.

The cast of characters

Four characters run this show. You will meet them again and again, so here they are with names.

client
001

The client

The app in your browser. It draws what you see and holds a temporary working copy of your data, like papers spread out on a desk.

clientserver
002

The app server

A small middleman program on the internet. It does the things a browser must never do itself, like holding secret keys.

clientserverdatabase
003

The database

The permanent record. Organized, searchable, and strict about its rules. Ours is Postgres, a thirty-year-old classic, run for us by a service called Supabase.

clientserverdatabaseshelf
004

The file storage

A shelf next door for big files, like images. A database is great at small structured facts and bad at big blobs, so the files sit on the shelf and the database keeps a note of where they are.

Supabase, the service this app moved to, is really just characters three and four plus a login desk, rented by the month instead of built from scratch.

What a database actually is

Strip away the mystery and a database is a set of spreadsheets with rules.

Each spreadsheet is called a table. Each line in it is a row. The chat app has exactly two tables: one holds a row per conversation, the other a row per message.

And the two ahead are not an illustration. This page is running a complete, real Postgres database inside your browser, and these are its actual tables. Press the buttons: each press runs real SQL against it, and the machine prints a receipt to prove it.

The machine is warming up: a small, real Postgres is loading into this page.

postgres 18 · in your browser · warming up

conversations
idtitle
1Trip ideas
2Poem drafts
messages
idchattext
11Ferry or plane?
21Ferry. Slower, better.
32A rhyme for orange?

Look at the chat column, the highlighted one. Every message carries the id of the conversation it belongs to. That belongs-to column is a relation: the thing that puts the relational in relational database. One conversation row connects to many message rows, like a folder tab connecting to the pages inside it.

Relations can carry rules. This app has one: if a conversation is deleted, its messages are deleted with it. Toss the folder and the pages inside go too. Nobody has to remember to clean up, because the database enforces it. Try it: send a couple of messages, then delete the chat, and watch its message rows vanish on their own.

The write path: sending a message

When you hit send, four things happen in a deliberately sneaky order. The order is what makes the app feel fast.

screen: instanthalf a second laterwritten for good
001

You hit send.

You type a message and press send. Nothing has left your machine yet.

screen: instanthalf a second laterwritten for good
002

The screen updates first.

The client puts the message on screen immediately. The working copy changes before anything is saved, which is the whole reason the app feels instant.

screen: instanthalf a second laterwritten for good
003

The row rides over.

A moment later the client sends the new data across the wire: insert this message into the messages table.

screen: instanthalf a second laterwritten for good
004

The database writes it down.

Postgres checks its rules first. Is this user allowed? Does this conversation exist? Only then does the row go to disk, and once it is on disk it stays there.

The read path: opening the app

The reverse trip happens every time you open the app, and it is the whole reason the move was worth it.

any machinethe database
001

You log in somewhere new.

A different laptop, a phone, a machine you have never opened the app on. The screen starts empty, because nothing is stored here.

any machinemy chats, pleasethe database
002

The client asks for your rows.

All conversations for this user, newest first, with their messages in order. One question, sent to the database.

any machinemy chats, pleasethe database
003

The database sends them back.

It finds the matching rows and returns them. An index, which is basically a pre-sorted table of contents, keeps this fast even with thousands of rows to look through.

any machinemy chats, pleasethe database
004

The list rebuilds itself.

The client lays the rows out and you get the exact same chat list you left on the other machine. Nothing was copied between the two. Both are just views of the same rows.

Who gets in

There are two layers of doors, and the second one is the good part.

Authentication is the front door: an email and a password, checked at the login desk. Pass, and you are handed a signed pass card, a token, that quietly rides along with every request you make afterwards.

Row Level Security is a bouncer standing inside the database itself. Every table can have a rule attached, and this app uses one: a user may only see and touch rows whose owner column matches their own id.

The bouncer works here too. The table ahead lives in the same in-page database as the one you pressed earlier, with a real Row Level Security policy switched on. Borrow a pass card and look.

Hold the pass card of

The bouncer is still putting on the jacket: the in-page database is loading.

postgres 18 · in your browser · warming up

chats
idownertitle
1aliceTrip ideas
2alicePoem drafts

The chats table as Alice sees it. Bob's rows are not blurred out or locked. As far as her queries are concerned, they do not exist.

Even someone talking to the database directly, skipping the app entirely, meets the bouncer. That is exactly what the toggle does: a bare select with no filter, and every single row is checked against the rule before it is handed over.

The side shelf: images and tickets

When the AI paints an image in a chat, the picture itself would bloat the database. So it takes a different path.

The server puts the image file on the storage shelf, and the database stores only the shelf position: a short line of text tucked into the message row.

the fat filethe shelfthe skinny note in the row
The fat file goes to the shelf. Only its position goes into the table.

The shelf is private. To actually show you the picture, the client asks for a signed URL: a temporary link that works like a coat-check ticket. Anyone holding the ticket can view that one image until the ticket expires. Nobody, ticket or not, gets to browse the shelf.

The booth ahead hands out real-looking tickets that live fifteen seconds, so you can watch one lapse. The genuine article usually lasts an hour.

On the private shelf

No ticket in hand, so the picture stays on the shelf. Ask for one and watch what you get.

The whole picture

Every trip through the app is some combination of four short walks. The numbers on the right are the characters from chapter two, so you can see who gets involved in each one.

  1. 001

    Log in1 · 2

    Client to login desk, pass card back.

  2. 002

    Send a message1 · 2 · 3

    Screen updates instantly, rows ride over a beat later, the bouncer checks each one.

  3. 003

    Generate an image1 · 2 · 3 · 4

    File to the shelf, note into the table, ticket to your screen.

  4. 004

    Open it anywhere1 · 2 · 3 · 4

    Log in, read your rows, watch the same chats rebuild, fresh tickets for the images.

  5. Everything the app does4 walks

clientserverdatabaseshelf
The whole cast, assembled.

A database turns

my data is trapped in this one browser

into

my data lives in one guarded, organized place, and every screen is just a view of it.