Module 2 · Lesson 2 of 8
Clients, servers and databases
Who does what, and why the database is a separate process.
Gatepass has three kinds of program: the app on the student's phone, the API server, and the database. Each one has a job, and much of good design is keeping each job in the right place. Put logic in the wrong one and you get apps that can be cheated, servers that can't scale, or data that quietly goes wrong.
Three roles
The client is whatever the user holds: the phone app, a browser tab, the volunteer's scanner. It shows things and collects input. It asks; it never decides anything that matters.
The server runs your code. It receives requests, checks who's asking and whether they're allowed, applies the business rules ("is this event on sale?", "is this student already holding a pass?"), and talks to the database.
The database stores the data and keeps it correct when many requests arrive at once. It's the source of truth: when the app and the database disagree about how many passes are left, the database is right.
Never trust the client
The client runs on a device you don't control. Anyone can open the browser's developer tools, or replay the app's requests with a script, and send anything they like. So every rule that protects money or fairness must run on the server.
Suppose the Gatepass app sends this to book a pass:
{ "event": 42, "passType": "day", "price": 0 }
If the server charges whatever price the app sent, someone will set it to zero within a day of launch. The server must look up the price itself. Same for "is this student allowed a second pass?" and "is this event still on sale?". The app can check those too, for a nicer experience, but the server's check is the one that counts.
The client is for showing and collecting. Every decision that matters is made, again, on the server.
Why the database is a separate process
You could keep Gatepass's data in a JSON file next to the server code. Lots of prototypes do. It breaks in three ways as soon as the app is real:
- Concurrency. Two booking requests at the same moment both read "1 pass left", both write "0 left", and you've sold two. Databases have transactions and locks so this can't happen.
- Durability. If the server crashes halfway through writing the file, you can end up with half a file. Databases write to a log first, so after a crash they come back to the last complete state.
- More than one server. The day you run two API servers, each has its own file. A database is one shared place every server talks to over the network.
That last point shapes the whole course. Because the data lives in the database, the API servers can be stateless: any server can handle any request, because nothing important is kept in the server's memory between requests. Stateless servers are easy to add, remove and restart. Stateful ones are not.
Who does what in a booking
Let's walk "book a day pass" through the three roles:
- Client: the student picks a pass type and taps Pay. The app disables the button so a double tap doesn't send two requests, and sends
POST /events/42/bookingswith the pass type and a payment token. - Server: checks the session token (who is this?), checks the event is on sale, looks up the real price, and asks the payment provider to charge it.
- Database: in one transaction, decrements the remaining passes if any are left and inserts the booking. If nothing's left, the transaction does nothing and the server refunds and returns "sold out".
- Server → client: returns
201 Createdwith the booking and QR data. - Client: shows the pass and saves it locally so it works offline at the gate.
The client's double-tap protection is nice to have, but the server must still cope with duplicate requests: a flaky network can make the app retry. You'll learn the proper fix, idempotency keys, in Module 7.
Check yourself
The Gatepass app hides the 'Buy' button once a student owns a pass. Is that enough to enforce one pass per student?
Thin client, thick client
How much should the client do? There's a spectrum. A thin client, like a basic web page, asks the server for everything and does little itself. A thick client, like a modern phone app, caches data, works offline, and validates input before sending it.
Gatepass's scanner is deliberately thick: it downloads the list of valid passes and checks them locally, because the gate Wi-Fi is unreliable. The tradeoff: two gates can each accept the same pass before they sync. That's acceptable for a fest. It wouldn't be for a bank.
Key takeaways
- Clients show and collect, servers decide, databases store and keep data correct.
- Never trust the client: re-check prices, permissions and limits on the server.
- A database is a separate process because it handles concurrency, survives crashes and is shared by every server.
- Keeping state out of API servers makes them stateless, which makes them easy to scale.
- Thick clients can work offline, at the cost of decisions made without the latest data.
Finished reading?
Mark it complete to tick it off in the course.