September 12, 2026
A DATABASE IS THE PART OF YOUR APP THAT REMEMBERS

Class 19 ended with a source map: a document that says what enters your product, who owns it, how fresh it is, and what to do when it fails. That map is a plan. It does not remember anything by itself.
This lesson gives your product its memory. A database preserves what the product must remember after the API response is gone, the browser tab is closed, and the model session ends.
Your source map says what enters. A database keeps what matters.
Think of the journey a single fact takes in Sonariq, the financial-research example running through this part of the course.
An SEC filing arrives as JSON from EDGAR — the SEC's public filing system, which serves company filing history and labeled financial facts your app can fetch. Your app reads it, picks out the company, the reporting period, the revenue figure, and the source URL. The user closes the laptop. A week later they return and ask: "What did we already find on this company?"
If the fact lived only in the API response, it is gone. If it lived only in the chat transcript, it is buried. If it lived in a database, the app can answer in milliseconds.
That is the whole idea. A source map describes the inputs. A database is the part of your app that remembers the outputs worth keeping.
Four places information can live — and only one remembers reliably
Beginners store things wherever is convenient. It helps to name the four options honestly:
| Storage | What it is | What it remembers | What it forgets |
|---|---|---|---|
| A file | Markdown, JSON, or CSV on disk | Small, stable reference material; great for project context and source maps | Who changed what, which records connect, what is current when many users write at once |
| An API response | JSON returned by a provider right now | The latest external fact at the moment you ask | Everything, the instant you stop holding it — it is a delivery, not a memory |
| Browser memory | State inside a running page: unsaved form inputs, loaded lists, chat history in the tab | What the current visitor is doing right now | Everything on refresh, and everything belonging to other users |
| A database | A managed store of structured records the app can save, update, connect, and query | Product records with relationships: users, companies, reports, evidence, saves, history | Nothing by design — forgetting is a deliberate deletion, not an accident |
A file is right for a small static resource: a curated country list, a prompt template, your DATA-SOURCES.md. An API response is right for the moment of retrieval. Browser memory is right for the current screen. A database is right when records must be saved, filtered, connected, and updated across sessions and users — the test Class 18 introduced.
Most real apps use all four. The mistake is using the wrong one as the memory: keeping the user's saved research only in browser state, treating a re-fetched API response as history, or managing hundreds of connected records by hand-editing JSON files.
The vocabulary, taught through a saved-research app
Imagine the simplest version of Sonariq: a user saves research reports about public companies. Here is the vocabulary, each term anchored to that app:
- Table: a named collection of the same kind of thing.
companiesis one table.research_reportsis another. - Row (record): one entry in the table. One company is one row in
companies. One saved report is one row inresearch_reports. - Column (field): one named attribute every row in the table has.
tickerandsectorare columns ofcompanies. - Data type: the rule for what a field may hold. Text, integer, decimal, date, timestamp, true/false. Types make later questions — sorting by date, averaging numbers — possible.
- ID: the stable identifier for a record, typically a number or generated string, that never changes even when everything else about the record does.
Here are the two tiny tables:
companies
| id | ticker | company_name | sector | created_at |
|---|---|---|---|---|
| 1 | AAPL | Apple Inc. | Technology | 2026-03-02 |
| 2 | NOVO-B | Novo Nordisk A/S | Healthcare | 2026-03-02 |
research_reports
| id | company_id | title | report_date | rating | source_url |
|---|---|---|---|---|---|
| 101 | 1 | Apple Q4 filing notes | 2026-02-10 | hold | https://www.sec.gov/…/0000320193 |
| 102 | 1 | Apple services-margin follow-up | 2026-03-01 | buy | https://www.sec.gov/…/0000320193 |
| 103 | 2 | Novo Nordisk pipeline review | 2026-02-20 | watch | https://…/annual-report-2025 |
Notice company_id in the second table. Report 101 does not spell out "Apple Inc." It stores 1 — the ID of the company it belongs to. That stored pointer is called a foreign key, and Lesson 20.2 teaches it fully.
Why the ID matters more than the name
Company names change. Tickers change. Novo Nordisk could rebrand a division; Apple could rename a product line that your display shows prominently. If reports identified their company by name, every rename would orphan or corrupt history.
The ID solves this. Record 1 stays record 1 while names, tickers, and sectors evolve around it. Every report pointing at 1 still points at the right company. Names are for humans to read; IDs are for the system to remember. Text search cannot substitute: it breaks on renames, typos, and duplicates, while an ID either matches or it does not.
Store more than you display
Look again at the tables. A reader-facing report card might show only the title, date, and rating. The database row keeps much more: which company it belongs to, the exact source URL, when the record was created. A production row would typically also keep source_name, retrieved_at (when your app fetched the source — distinct from the report date), status (draft, published, superseded), owner_id (which user saved it), and period_end (the fiscal period described, because "reported in February" and "describes Q4" differ).
The rule: store what future questions will need, not only what the current screen shows. The screen shows five fields. The database keeps fifteen, because next month someone will ask "which claims came from this source?", "what did we know before March?", or "which of my saves are stale?" Provenance from Class 18 — source URL, owner, date, retrieval time — lives here, attached to each record, not in a separate document nobody opens.
What the user sees What the database keeps
───────────────── ──────────────────────────
Title, date, rating → + source URL, source name
+ retrieved_at, period_end
+ status, owner_id
+ company_id (the link)
Do-now sketch workflow: from app to two tables in 15 minutes
The companies and research_reports tables above were not guessed. They came from this five-step workflow. Run it on paper before you touch any tool:
1. Pick one app. One sentence: "Sonariq saves research reports about public companies." 2. List two questions it must answer. "Which reports belong to this company?" and "What did we already find on this company, newest first?" 3. Name the tables. One table per kind of thing: companies (the thing that persists) and research_reports (the work done about it). Plural, lowercase, no spaces. 4. Give each table five fields with types. Copy the pattern from the tables above: every table gets an id (integer, never reused), one or two human names (ticker: text, title: text), one date (created_at: date, report_date: date), and — for the child table — the pointer (company_id: integer → companies.id). Every field gets a type on the same line: text, integer, date, timestamp, boolean. 5. Write the uniqueness sentence. "One row in companies is one company, and no two rows share the same ticker." "One row in research_reports is one saved report, and no two rows share the same id." If you cannot fill the blanks, the table is not defined yet.
This sketch is what you hand a coding agent. Not "make a database for stocks" but: "Create tables companies and research_reports with these five fields and types each; research_reports.company_id points at companies.id; one company per ticker." The agent writes syntax. You supply the app sentence, the two questions, and the uniqueness rule.
Bad sketch vs. fixed sketch
Beginners almost always produce the left version first. The fix is mechanical:
BAD: research_reports
title | company_name | tag | image_url | button_label
"Apple Q4 notes" | "Apple Inc." | "tech" | "…/img.png" | "View"
"Apple services" | "Apple inc." | "tech" | "…/img2.png" | "View"
Problem: no id, no types, company spelled two ways,
no date to sort by, no pointer — two rows claim
different companies that are the same company.
FIXED: research_reports
id: integer | company_id: integer → companies.id
title: text | report_date: date | rating: text
101 | 1 | "Apple Q4 filing notes" | 2026-02-10 | hold
102 | 1 | "Apple services-margin follow-up" | 2026-03-01 | buy
Uniqueness: one row is one saved report,
and no two rows share the same id.
Company name lives once in companies; reports point at id 1.
When you run the Exercise below, check your draft against this contrast: if your five fields have no id, no date or timestamp, and no pointer or owner, you wrote the left column. Swap two display fields for an id and a date, add the uniqueness sentence, and you have the right column.
What a database is not
Three boundaries prevent expensive confusion later in this part:
1. A database is not a search engine. It answers precise questions — "all reports for company 1 since January" — perfectly. It does not rank by meaning. When a user phrases a question ten different ways, that is the retrieval problem Class 22 solves on top of stored records. 2. A database is not AI memory. A model's context window holds the current conversation. A database holds the product's durable records across all users and sessions. Retrieval systems copy selected database material *into* a model's context; the database itself does not think. 3. A database is not a data provider. EDGAR owns filings. FRED owns macro series. Your database holds *your copies and your work*: retrieved facts with provenance, user saves, your reports and judgments. The provider remains the source of truth for its facts; your database is the source of truth for what your product did with them.
Check your understanding
1. What survives a closed laptop: an API response, browser memory, or a database record? 2. In your own words, what is the difference between a row and a column? 3. Why does report 101 store company_id = 1 instead of the text "Apple Inc."? 4. Name two fields an app should store but not necessarily display — and the future question each one answers.
Exercise: sketch one table
Pick an app you actually use — a reading list, a workout log, a price watchlist, a support queue. Write a DATABASE-SKETCH.md file with:
- The table name (plural, lowercase, e.g.
saved_items). - Five fields, each with a data type (
text,integer,date,timestamp,boolean). - One sentence stating what makes a row unique — the field or combination no two rows may share.
Finish line: a committed DATABASE-SKETCH.md you can read aloud: "one row is one ___, and no two rows share the same ___."
Verify: for three real examples from the app, fill in all five fields. Every example gets a distinct uniqueness value. If two examples collide, your uniqueness rule is wrong — refine it before continuing.
Common failure mode: choosing five display fields ("title, image, color, blurb, button label") with no ID, no timestamp, and no owner. If a field could not answer a future question, replace it with one that can.
What comes next
One table remembers one kind of thing. Real products remember several kinds of things that connect — companies *and* reports, users *and* saves, claims *and* sources. Next you will learn to design those connections around the questions your product must answer, instead of building one giant table shaped like the first screen.
ARTICLE DISCUSSION
JOIN THE
CONVERSATION.
Got a question, a take, or a better way to do this? Log in and leave a comment.
