Skip to main content
Databases9 min readPublished September 19, 2026Updated September 22, 2026

How databases work in AI-built apps

A database stores your app's data in tables: fixed columns, one row per thing. Your app reads and writes rows through queries. In an AI-built app the tables, the relationships between them, and the rules about who can touch which row were all generated for you — and the rules are the part worth reading first.

What is a table, really?

A spreadsheet with enforced structure. Each column has a type, so a date column cannot hold 'next Tuesday'. Each row usually has an `id` that nothing else shares, which is how other tables refer to it.

iduser_idtitlecreated_at
c1f...9ab...Shopping list2026-09-20T10:04Z
d2e...9ab...Launch plan2026-09-21T16:20Z

The `user_id` column is doing the important work here: it is what makes a row belong to somebody. Almost every access rule you will ever write depends on a column like it.

How do tables relate to each other?

By storing each other's ids. A note holds the id of its author; an order holds the id of its customer. That reference is called a foreign key, and the database can enforce it — refusing to store a note whose author does not exist, or deleting notes when their author is deleted.

  • One-to-many: one user has many notes (the note holds the user id).
  • Many-to-many: users belong to many teams and teams have many users (a third table holds the pairs).
  • One-to-one: a user has one profile row (often used to keep public fields separate from account fields).

What is a query doing?

The same request, two ways of writing it
-- SQL
select id, title from notes where user_id = $1 order by created_at desc limit 20;

// Client library
supabase.from("notes").select("id,title").eq("user_id", userId)
  .order("created_at", { ascending: false }).limit(20);

Every query answers four questions: which table, which columns, which rows, in what order. If a list on your page is wrong, one of those four is wrong — and it is nearly always 'which rows'.

Why does the database say no?

Modern hosted databases enforce row-level access rules. A query that is perfectly valid will still return nothing if the rules do not allow this particular user to see those rows. Crucially, this often looks like an empty list rather than an error, which is why people spend hours debugging the wrong layer.

An empty list with no error is a permissions symptom until proven otherwise. Check the rules before you check the query.

What should you check in an AI-generated schema?

  1. 1Does every table holding personal data have a column identifying the owner?
  2. 2Are access rules enabled on every table, and does each rule name a real condition rather than allowing everyone?
  3. 3Are permissions granted to the roles your app actually uses?
  4. 4Do timestamps exist so you can tell when something was created or changed?
  5. 5Is anything stored that you would not want to explain in a privacy policy?

Frequently asked questions

Do I need to learn SQL?
Not to build, but reading it is worth an afternoon. Almost every serious debugging session ends with someone reading a query.
Where is my data actually stored?
On your database provider's servers, in a region you chose or were assigned. It is not inside your app or your hosting deployment.
What happens to data when I redeploy?
Nothing. Code and data are separate; only a migration changes the database.

Practice this in MessyDev

Reading it once helps. Doing it once sticks. These are the hands-on parts of MessyDev that cover the same ground.

Keep going