Close your app and open it again. The stuff is still there. That's the whole job of a database, and everything else is detail.
Without one, your app forgets everything the moment the page reloads, which is fine for a calculator and useless for almost anything else.
What a database actually is
Think of a set of spreadsheets that a program can read and write very quickly, and that several people can use at once without corrupting each other's work.
Each sheet is a table. A table for users, a table for their posts, a table for whatever else your app keeps.
Each row is one thing: one user, one post. Each column is a fact about that thing: name, email, when it was created.
That's genuinely most of it. The rest is refinements on the idea.
The two kinds you'll hear about
SQL databases, like Postgres, want you to decide the columns in advance. Every row in the users table has the same shape. This sounds restrictive and turns out to be useful, because the database can then stop you from saving nonsense.
NoSQL databases, like Firebase's, are looser. Rows can have different shapes. Faster to start, easier to make a mess with later.
For a first app, Postgres is the boring correct answer, usually through something like Supabase which gives you a database plus logins plus file storage without having to run a server yourself. Boring correct answers are underrated.
Store facts, not conclusions
This is the one idea in this lesson that will save you real pain, and it's easy to get wrong.
Say your app has a streak counter. The tempting thing is a column called streak_days that you bump up by one each day.
Now what happens when someone uses the app twice in a day? When they cross a timezone? When a save fails halfway? Your number drifts away from reality, and because it's the only record you have, you can never tell it's wrong or fix it.
The alternative is to record what actually happened: a row each time they complete something, with a timestamp. The streak becomes something you work out from those rows whenever you need it.
Slightly more work each time you display it. Impossible to get out of sync, because the rows are the truth and the streak is just arithmetic over them.
The general rule: store the events that happened, calculate the summaries. Facts survive bugs. Conclusions don't.
Relationships, meaning how tables connect
Your users table has users. Your posts table has posts. Something has to record which post belongs to whom.
That's a column in the posts table holding the id of a user. It's called a foreign key, which is a heavier name than the idea deserves. It's just a pointer.
Get this arrangement roughly right early, because it's one of the few things that's genuinely painful to change once you have real data in there. Most of your app can be rewritten in an afternoon. Reshaping a database with live users in it is a different kind of day.
That doesn't mean agonising over it. It means spending twenty minutes sketching your tables before you write code, rather than discovering the shape by accident.
What you don't need to worry about yet
Plenty of database advice online is written for teams with millions of users. Indexing strategy, sharding, query optimisation, caching layers. All real, none of it yours yet.
At your scale, almost anything reasonable will be fast enough. Don't let a performance article aimed at a company with 400 engineers talk you into complexity you'll never need. You can add all of that later, if the problem ever actually arrives, and it usually doesn't.
A quick story. My app had a daily reset, where each user's day rolled over at midnight and new content unlocked.
The bug was that midnight was being worked out in the wrong timezone. So for users in some countries, the day would flip at the wrong hour. Their streak could break while they were asleep, through nothing they did.
That's a data and logic problem wearing a UI costume. And it's invisible from where I was sitting, because my own timezone happened to work fine. It only showed up because I asked for the code to be reviewed adversarially rather than just tried out.
Dates and times are where a surprising number of beginner bugs live. Whenever your app cares about "today," it's worth asking whose today you mean.
If you want help designing your tables
That third point catches the streak-counter mistake before it costs you anything.
Next: Logins, and why they're harder than they look →
Go deeper: Chapter 20, Databases and Data Modeling and Chapter 19, Backend Architecture