A database mobile app is two databases, and the gap between them is the project.
Most people planning a database mobile app picture one box of data with an app reading from it. What you get in practice is two boxes: the copy on the phone and the copy on the server. Almost everything that hurts later, an edit made in a lift that never arrives, a duplicated row, a list that refuses to refresh, comes out of the gap between those two copies. Which one is the source of truth is the first real decision, and it comes before any mobile app database design work at all.
This page covers the three shapes a data driven mobile app can take, what the on-device store actually is, what the shared store has to settle when two people edit the same record, and why a published app makes every schema change a slower job than it was on the web.
Work out which shape you needThe short version
Choose the shape first, then choose the database.
There are only three shapes. The data lives on the phone and nowhere else. It lives on a server and the app is a window onto it. Or it lives in both and something reconciles them. The third is what most people mean by an app backed by a database, and it costs several times what the other two cost.
Nearly every painful mobile data project is a server-only app that was quietly asked to behave like the third shape, usually in the week somebody first used it on a train.
Three shapes, and only one of them is expensive
Phone only means the database file sits inside the app sandbox. It is fast, it works in a basement, and it dies with the app: uninstall and the data goes too, and nobody else ever sees a row of it. For a personal log, a survey a single inspector fills in, or a calculator that keeps its history, this is the right answer, and people talk themselves out of it far too easily.
Server only means every screen is a network request. Data is shared the second it is written, there is one copy to back up and one copy to correct when it is wrong. The price is that the app is useless without a connection, and a spinner on every screen is how a genuinely useful tool starts to feel slow.
Both means the phone holds its own copy and pushes changes up when it can. This is the shape people assume they are buying, and it is the one that needs rules. What happens to a record edited on two phones. What happens to a record created offline that the server deleted before it arrived. What the person is shown while the two copies disagree. Write those rules down before you write the schema, because they will change the schema.
Try it
Which shape does your app need
Three answers decide it. Tick the ones that are true and the shape falls out of them.
Phone only
One database file in the app sandbox. No API, no accounts, no sync code, and uninstalling the app deletes the data.
The on-device half is a real database, not a cache
In a React Native and Expo project the store on the phone is normally SQLite, and the official Expo library keeps that database file across restarts of the app. You get real tables, real indexes and real SQL. A list of two thousand rows can be filtered, sorted and totalled on the device without a single request leaving it.
Two settings in the Expo documentation do most of the work. It recommends turning on WAL journal mode when you create the database, for general performance. And its migration example tracks the schema with SQLite's own user_version pragma, so an app launching against an old file can step it up to the current shape. The same library offers synchronous and asynchronous calls, and it warns that running heavy work through the synchronous ones can block the JavaScript thread, which on a phone means a frozen screen.
The local store is also what decides how the app feels. A stock count in an inventory tracking app that reads from the phone and syncs in the background feels instant. The same count fetched live over warehouse Wi-Fi feels broken. Same data, same server, different shape.
The shared half has to decide who wins
Two things belong here and both get skipped. The first is that the phone must not hold database credentials. Anything shipped inside an app binary can be read by anyone who has the app, so the client talks to an API and the API talks to the database. There is no arrangement where a mobile app connects to Postgres directly and the password stays private.
The second is what happens when two people write the same row. Postgres runs at Read Committed by default. In that mode a second UPDATE on a row waits for the first transaction to finish, then re-checks its own condition against the updated version of the row and applies itself if it still matches. Nothing is lost at the storage level, but nothing is merged either. For a plain update by id the later write simply overwrites the earlier one, and the first person never learns their change is gone. If that is wrong for your data, the rule has to live in your code, usually as a version number the client sends back so a stale edit can be rejected instead of applied.
Who owns a row is the other half of the same question. An employee self service app where anyone can read anyone else's record is not a smaller feature set, it is a different product. Ownership belongs in the schema and in the API, not in whether a screen happens to show the button.
A published app makes every schema change slower
A mobile app with a database has two schemas to move, the one on the server and the one in the file on every phone, and they never move at the same moment. A web app has one version live. A mobile app has every version anybody has not got round to updating, and those builds keep sending last month's shape to your API.
The practical consequence is that server changes stay additive for a while. Add a column, make it nullable or give it a default, write both the old field and the new one until the old clients have gone, then drop the old one. A rename shipped in a single migration breaks every phone that has not updated, and you will hear about it from people who cannot fix it themselves.
Row IDs are the retrofit nobody enjoys. If the server hands out the primary key, a record cannot exist until the phone is online. That rules out creating anything offline and invites duplicate rows every time a submit is retried on a bad connection. Generate the ID on the device instead, as a UUID, and a record created underground keeps the same identity when it finally arrives. On day one this costs nothing. In month six it means rewriting every foreign key you have.
What each shape actually gives you
| Shape | Usable with no signal | Shared between people | Survives a new phone | You write conflict rules |
|---|---|---|---|---|
| One database file on the phone | Yes | No | No | No |
| Phone file plus platform backup | Yes | No | only if restored | No |
| Server only, a request per screen | No | Yes | Yes | No |
| Server plus a cache for reads | reads only | Yes | Yes | No |
| Two way sync between both copies | Yes | Yes | Yes | Yes |
Building one around your own records
The spreadsheet products with a mobile view are good at the server-only shape and unconvincing at the third one. They are priced per editor per month and built around their own field types, which is fine until your data has a rule they do not offer: a quantity that must never go below zero, a record only its owner may open, a status that can move forward but not back. At that point you are writing the rule in a formula column and hoping nobody edits it.
Newly is an AI app builder. You describe the app in plain English and it writes a real React Native and Expo project you own, runs it on a cloud iPhone or Android simulator while it builds, and uploads iOS builds to TestFlight through your own Apple Developer account. The Deploy tab publishes an Android build to Google Play internal testing and also produces a standalone release APK with the JavaScript bundled. It costs $25 a month and there is no free plan. On the data question the documentation is blunt, and worth reading before you plan around it. A new app saves its data on the phone, with no accounts, no server and no sign-in. Newly Backend gets added only when the app needs sign-in, shared data or sync across devices, and it is a fixed set of pieces: a sign-in service, an API service, a Postgres database for each environment and file storage, with schema changes applied as migrations the agent writes. You cannot point it at your own database. The documentation states that it does not connect to Supabase, Firebase or other backend providers. There is no database viewer in the product either, although the CLI will hand you a read-only address for the dev database that you can open in a database tool of your own.
Whichever way you go, the shape decision is the one that matters and the rest is plumbing. If you have settled on a server with sync and want the next layer of detail, that is the backend behind it.
Questions people ask about database mobile apps
An app whose main job is to hold structured records rather than display content: jobs, stock, customers, readings, timesheets. The interesting part is not the app, it is that there are usually two stores. One on the phone and one on a server. How they stay in agreement is what makes this category harder than it looks.
Describe the records your app actually holds
List the three or four record types, who is allowed to see each one, and which screens have to work with no signal. Build from that list rather than from a schema.
Start building