The variable that forgets
The first version of nearly every generated app keeps its data in a variable: an array of bookings at the top of a file. It works for as long as the process runs. Restart the dev server and the bookings are gone, which on a laptop is a mild surprise.
On a serverless host it is a constant one. Vercel, Netlify and Cloudflare run your code in short-lived processes: a request arrives, a process handles it, and after some idle time the process is discarded. Two requests a minute apart may run in different processes, each starting with an empty array. Nothing was deleted; nothing was ever kept.
Writing to a file on disk (fs.writeFile("./data.json")) has the same fate for the same reason. The disk belongs to the process, and the process goes away. It works locally, which is why the model reaches for it, and it is silently wrong in production.
Data that must outlive a request has to live somewhere designed to outlive one. That is what a database is for, and the choice is smaller than it looks.
SQLite: a database in a file
SQLite stores a whole database in one file and runs inside your process; there is no server to install or connect to. It is free, it is in more devices than any other database on earth, and for a single-process app it handles far more than you will throw at it.
For local development it is the honest place to start. Setup is one line, the file sits next to your code, and you can open it with any SQLite browser to look at your tables directly. Add it to .gitignore; it is data, not code.
The limitation is the one above. A file on a serverless host’s disk does not persist. So SQLite works locally and on a host that gives you a persistent machine (Render, Fly, Railway, a VPS) and fails on serverless unless you use a hosted SQLite service that keeps the file elsewhere: Turso or Cloudflare D1 both do this, with free tiers.
Postgres: the one everybody ends up on
Postgres is a server: a separate process your app connects to over the network. It handles many simultaneous connections, it is what most hosted database services run, and every ORM and tool supports it first. Neon, Supabase and others offer a free Postgres database that a small app can live on for a long time.
The cost is a connection string in your environment variables and one new concept: connections are limited. A serverless app that opens a fresh connection on every request can exhaust a free tier’s limit of a few dozen within a busy minute, producing errors that mention “too many clients”. Hosted services provide a pooled connection string for this: a proxy that shares a small number of real connections among many short-lived processes. Use the pooled one from serverless code. The unpooled one is for migrations.
The decision, made once
Local development: SQLite, or a Postgres database in a Docker container if you want production and development to match exactly.
Production on a persistent host: either works.
Production on serverless: hosted Postgres, or hosted SQLite. Pick the one your host has a one-click integration with, and stop.
Do not let the choice be made by the model’s default. Asked for “a database”, it will pick whatever is most common in its training data for your framework, and sometimes an in-memory store because that is the shortest working answer. Name the database in the specification.
Two things people expect a database to be, which it is not
It is not automatically backed up. A free hosted database usually has a short restore window or none; that lesson comes later in this block.
It is not a place to keep files. Images and PDFs go to object storage, and the database holds their addresses. Storing a 4 MB image as a blob in a table works until the table has a thousand of them, at which point every query that touches the table slows down and the free tier’s storage limit arrives.
Reading your own data
Whatever you choose, install a way to look at the tables directly: a SQLite browser, psql, the host’s built-in table editor, or an ORM studio such as npx prisma studio or npx drizzle-kit studio. Half of “the app is not saving” is settled in ten seconds by looking at the table and finding the row is there and the screen is not reading it, or that it is not there and the write never happened.
Every lesson here is open. Your progress is recorded once you pass the Building with AI exam.
Check your understanding
A prototype stores bookings in a JavaScript array. It works on the laptop for a week. Deployed to a serverless host, bookings disappear every few minutes. Why?
Pick the one you would defend. Your answer stays private.
Stuck on something?Ask Beatriz, your AI tutor