Database with Supabase
A database makes data survive a refresh and stay queryable — and Supabase adds accounts and per-row privacy enforced by the database itself.
Your app works — but every time the user refreshes, their progress vanishes, because it only ever lived in memory. And you need user accounts where each person sees only their data, never everyone’s. You need somewhere durable to keep data, a way to query it, and a way to keep each user’s rows private. That’s a database — and Supabase gives you one with accounts and security built in.
A database persists and queries structured data
A database stores data in tables — rows are records, columns are fields. Unlike memory, it survives restarts and refreshes; unlike a flat file, you can query it efficiently — find, filter, sort, and relate records. You read and write with queries (often SQL, or a client library that wraps it). This is where durable things live: user accounts, lesson progress, chat history.
Supabase = hosted Postgres + batteries
Supabase gives you a real Postgres database without managing a server, plus built-in authentication (sign-up/login and user identities) and auto-generated APIs. Because it’s Postgres, the pgvector you met in Module 4 lives here too — the same database holds your normal rows and your embeddings. It’s the data layer for your backend, so you don’t rebuild accounts and storage from scratch for every project.
Row-Level Security: privacy by default
The crucial safety feature is Row-Level Security (RLS): rules the database itself enforces about which rows a given user may read or change. You set it default-deny, then add a policy like “a user can access only rows where user_id equals their authenticated id.” Because the rule lives in the database — not scattered through app code — a bug in one endpoint, or a request crafted to ask for more, still can’t leak another user’s data. (Same principle as the last lessons: enforce at the trusted layer, never by trusting the caller.)
Memory is a pile of sticky notes on your desk: leave the room (refresh) and they blow away. A database is a filing cabinet — labeled drawers and folders (tables and rows) where things stay put and you can find them fast. Row-Level Security is giving each tenant a key that opens only their own drawer: even if someone walks up and asks the clerk for “all the drawers,” the lock only ever lets them into theirs.
Storing lesson progress safely: 1. Aprogresstable with columns:user_id,lesson_id,mastered,updated_at. 2. A user finishes a lesson → the backend writes a row{ user_id, lesson_id, mastered: true }. 3. RLS policy: a user mayselect/insert/updateonly rows whereuser_id= their auth id; deny everything else by default. 4. On login, the app queries the table and the database returns only that user’s rows — even a query that tried to grab more would be blocked by RLS. 5. Refresh? The data’s still there. Another user? Can’t see it. Persistence and privacy, both handled.
This is the reading. The interactive version — active-recall quiz, a hands-on experiment you run in your own AI, and an earned mastery check — is free in the app.
Start this lesson free →