A plain-language tour
Where does your data actually live?
No code to write. You will, however, be handed receipts.
The example is a real, small app: a chat window for AI models that kept every conversation inside the browser, until the day that stopped being enough.
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.
The browser's box is tiny.5 MB
About five megabytes in total, for everything. Two AI-generated images and it is full.
It sits on one machine.1 browser
The box belongs to one browser on one computer. Open the app anywhere else and there is simply nothing there. No sync, no copy, no way out.
And it can vanish.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.
The cast of characters
Four characters run this show. You will meet them again and again, so here they are with names.
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.
The app server
A small middleman program on the internet. It does the things a browser must never do itself, like holding secret keys.
The database
The permanent record. Organized, searchable, and strict about its rules. Ours is Postgres, a thirty-year-old classic, rented by the month from a service called Supabase along with the shelf next door and a login desk.
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.
What a database actually is
Strip away the mystery and a database is a set of spreadsheets with rules.
It is just tables and rows.
Each spreadsheet is a table. Each line in it is a row. The chat app has exactly two: one row per conversation, and one row per message.
One column does the joining.
Every message carries the id of the conversation it belongs to. That belongs-to column is the relation, and it is the thing that puts the relational in relational database.
Toss the folder, the pages go too.
Relations can carry rules. This app has one: delete a conversation and its messages are deleted with it, without anybody remembering to clean up. The machine ahead is a real Postgres running inside this page, so press its buttons and watch it happen.
The machine is warming up: a small, real Postgres is loading into this page.
postgres 18 · in your browser · warming up
| id | title |
|---|---|
| 1 | Trip ideas |
| 2 | Poem drafts |
| id | chat | text |
|---|---|---|
| 1 | 1 | Ferry or plane? |
| 2 | 1 | Ferry. Slower, better. |
| 3 | 2 | A rhyme for orange? |
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.
You hit send.
You type a message and press send. Nothing has left your machine yet.
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.
The row rides over.
A moment later the client sends the new data across the wire: insert this message into the messages table.
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.
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.
The client asks for your rows.
All conversations for this user, newest first, with their messages in order. One question, sent to the database.
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.
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.
The front door checks your password.
Authentication happens 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.
A second bouncer waits inside.
Row Level Security is its real name, and it stands 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.
No way around it.
Even something wired straight to the database, skipping the app entirely, meets the same bouncer. That is exactly what the toggle ahead does: a bare select with no filter, and every row is still checked against the rule before it is handed over. Borrow a pass card and look.
The bouncer is still putting on the jacket: the in-page database is loading.
postgres 18 · in your browser · warming up
| id | owner | title |
|---|---|---|
| 1 | alice | Trip ideas |
| 2 | alice | Poem 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.
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 image never enters the database.
The server puts it on the storage shelf, next door rather than inside. A database is bad at big blobs and there is no reason to make it carry one.
Only its address goes in the row.
What goes into the message row is a short line of text: where on the shelf to find the picture. The row stays skinny, and the table stays fast.
A ticket lets you look, for a while.
The shelf is private, so the client asks for a signed URL: a temporary link that works like a coat-check ticket. Anyone holding it can view that one image until it expires, and nobody, ticket or not, gets to browse the shelf. The booth ahead hands out fifteen-second ones so you can watch a ticket 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.
- 001
Log in1 · 2
Client to login desk, pass card back.
- 002
Send a message1 · 2 · 3
Screen updates instantly, rows ride over a beat later, the bouncer checks each one.
- 003
Generate an image1 · 2 · 3 · 4
File to the shelf, note into the table, ticket to your screen.
- 004
Open it anywhere1 · 2 · 3 · 4
Log in, read your rows, watch the same chats rebuild, fresh tickets for the images.
Everything the app does4 walks
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.