{"course":{"slug":"building-apps-with-ai","title":"Building Apps with AI","url":"https://addaly.com/learn/building-apps-with-ai","level":"beginner","track":"build"},"lesson":{"slug":"where-the-data-goes-when-the-app-restarts","title":"Where the data goes when the app restarts","url":"https://addaly.com/learn/building-apps-with-ai/where-the-data-goes-when-the-app-restarts","position":48,"of":87,"minutes":8,"answer":"Anything held in a variable or written to the app's own disk vanishes on a serverless host, so the first real decision is which database.","keyIdea":"Anything held in a variable or written to the app's own disk vanishes on a serverless host, so the first real decision is which database, and SQLite is the honest place to start until you deploy."},"body":{"format":"markdown","text":"## The variable that forgets\n\nThe 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.\n\nOn 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.\n\nWriting 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.\n\nData 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.\n\n## SQLite: a database in a file\n\nSQLite 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.\n\n> Figure: Two kinds of storage that look identical on a laptop. Outlives the request: A row in hosted Postgres, on Neon or Supabase; Hosted SQLite, on Turso or Cloudflare D1; A SQLite file on a host that gives you a persistent machine; Object storage, for anything that is a file. Looks permanent for about a day: An array of bookings at the top of a file; fs.writeFile to the app's own disk; A SQLite file on a serverless host; Anything that survives a restart only because nothing restarted. The right-hand column is what a model reaches for, because it is the shortest working answer and it works locally. On a serverless host the process is discarded and so is everything it held.\n\nFor 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.\n\nThe 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.\n\n## Postgres: the one everybody ends up on\n\nPostgres 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.\n\nThe 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.\n\n## The decision, made once\n\nLocal development: SQLite, or a Postgres database in a Docker container if you want production and development to match exactly.\n\nProduction on a persistent host: either works.\n\nProduction on serverless: hosted Postgres, or hosted SQLite. Pick the one your host has a one-click integration with, and stop.\n\nDo 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.\n\n## Two things people expect a database to be, which it is not\n\nIt is not automatically backed up. A free hosted database usually has a short restore window or none; that lesson comes later in this block.\n\nIt 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.\n\n## Reading your own data\n\nWhatever 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."},"check":{"question":"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?","options":[{"text":"The host's free tier deletes data after a few minutes of inactivity to encourage people to upgrade to a paid plan.","correct":false,"tempting":"Assigns a motive to what is a property of the runtime; the host is not deleting anything, it is starting fresh processes."},{"text":"Serverless hosts do not support JavaScript arrays.","correct":false,"tempting":"Arrays work; what is missing is a process that lives long enough to keep one."},{"text":"The array is too large for the host's memory limit.","correct":false,"tempting":"A few bookings occupy kilobytes; the loss happens regardless of size because the process itself does not persist."},{"text":"Each request may run in a new process with an empty array, and idle processes are shut down.","correct":true}]},"prev":{"slug":"a-domain-of-your-own","title":"A domain of your own","url":"https://addaly.com/learn/building-apps-with-ai/a-domain-of-your-own"},"next":{"slug":"the-schema-and-the-migration","title":"The schema, and the migration that changes it","url":"https://addaly.com/learn/building-apps-with-ai/the-schema-and-the-migration"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Where the data goes when the app restarts', in Building Apps with AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/building-apps-with-ai/where-the-data-goes-when-the-app-restarts (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Where the data goes when the app restarts","courseTitle":"Building Apps with AI","module":{"index":6,"slug":"the-parts-that-persist","title":"The parts that persist: data, accounts and files"},"answer":null,"keyIdea":"Anything held in a variable or written to the app’s own disk vanishes on a serverless host, so the first real decision is which database, and SQLite is the honest place to start until you deploy.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure 6.1","figure":{"left":{"title":"Outlives the request","items":["A row in hosted Postgres, on Neon or Supabase","Hosted SQLite, on Turso or Cloudflare D1","A SQLite file on a host that gives you a persistent machine","Object storage, for anything that is a file"]},"type":"compare","right":{"title":"Looks permanent for about a day","items":["An array of bookings at the top of a file","fs.writeFile to the app’s own disk","A SQLite file on a serverless host","Anything that survives a restart only because nothing restarted"]},"title":"Two kinds of storage that look identical on a laptop","caption":"The right-hand column is what a model reaches for, because it is the shortest working answer and it works locally. On a serverless host the process is discarded and so is everything it held."},"alt":"Figure: Two kinds of storage that look identical on a laptop. Outlives the request: A row in hosted Postgres, on Neon or Supabase; Hosted SQLite, on Turso or Cloudflare D1; A SQLite file on a host that gives you a persistent machine; Object storage, for anything that is a file. Looks permanent for about a day: An array of bookings at the top of a file; fs.writeFile to the app’s own disk; A SQLite file on a serverless host; Anything that survives a restart only because nothing restarted. The right-hand column is what a model reaches for, because it is the shortest working answer and it works locally. On a serverless host the process is discarded and so is everything it held."}},"blocks":[{"t":"h2","id":"the-variable-that-forgets","runs":[{"text":"The variable that forgets"}]},{"t":"p","runs":[{"text":"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."}]},{"t":"p","runs":[{"text":"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."}]},{"t":"p","runs":[{"text":"Writing to a file on disk ("},{"text":"fs.writeFile(\"./data.json\")","code":true},{"text":") 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."}]},{"t":"p","runs":[{"text":"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."}]},{"t":"h2","id":"sqlite-a-database-in-a-file","runs":[{"text":"SQLite: a database in a file"}]},{"t":"p","runs":[{"text":"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."}]},{"t":"p","runs":[{"text":"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 "},{"text":".gitignore","code":true},{"text":"; it is data, not code."}]},{"t":"p","runs":[{"text":"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."}]},{"t":"h2","id":"postgres-the-one-everybody-ends-up-on","runs":[{"text":"Postgres: the one everybody ends up on"}]},{"t":"p","runs":[{"text":"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."}]},{"t":"p","runs":[{"text":"The cost is a connection string in your environment variables and one new concept: "},{"text":"connections are limited","b":true},{"text":". 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."}]},{"t":"h2","id":"the-decision-made-once","runs":[{"text":"The decision, made once"}]},{"t":"p","runs":[{"text":"Local development: SQLite, or a Postgres database in a Docker container if you want production and development to match exactly."}]},{"t":"p","runs":[{"text":"Production on a persistent host: either works."}]},{"t":"p","runs":[{"text":"Production on serverless: hosted Postgres, or hosted SQLite. Pick the one your host has a one-click integration with, and stop."}]},{"t":"p","runs":[{"text":"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."}]},{"t":"h2","id":"two-things-people-expect-a-database-to-be-which-it-is-not","runs":[{"text":"Two things people expect a database to be, which it is not"}]},{"t":"p","runs":[{"text":"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."}]},{"t":"p","runs":[{"text":"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."}]},{"t":"h2","id":"reading-your-own-data","runs":[{"text":"Reading your own data"}]},{"t":"p","runs":[{"text":"Whatever you choose, install a way to look at the tables directly: a SQLite browser, "},{"text":"psql","code":true},{"text":", the host’s built-in table editor, or an ORM studio such as "},{"text":"npx prisma studio","code":true},{"text":" or "},{"text":"npx drizzle-kit studio","code":true},{"text":". 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."}]}],"check":{"question":[{"text":"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?"}],"options":[{"text":[{"text":"The host’s free tier deletes data after a few minutes of inactivity to encourage people to upgrade to a paid plan."}],"correct":false,"tempting":[{"text":"Assigns a motive to what is a property of the runtime; the host is not deleting anything, it is starting fresh processes."}]},{"text":[{"text":"Serverless hosts do not support JavaScript arrays."}],"correct":false,"tempting":[{"text":"Arrays work; what is missing is a process that lives long enough to keep one."}]},{"text":[{"text":"The array is too large for the host’s memory limit."}],"correct":false,"tempting":[{"text":"A few bookings occupy kilobytes; the loss happens regardless of size because the process itself does not persist."}]},{"text":[{"text":"Each request may run in a new process with an empty array, and idle processes are shut down."}],"correct":true,"tempting":null}],"order":[1,0,2,3],"why":null},"prev":{"slug":"a-domain-of-your-own","title":"A domain of your own","minutes":8},"next":{"slug":"the-schema-and-the-migration","title":"The schema, and the migration that changes it","minutes":9}}}