Warming up the neural circuits...
By the end of this chapter you will:
Every modern app is two programs talking to each other. The split is not technical luxury — it's a security and trust boundary. Understand the boundary, and you'll never write a frontend bug into your backend or a backend bug into your frontend.
You walk into a restaurant. You read the menu, point at a dish, and the waiter takes your order. The waiter walks into the kitchen — a place you are not allowed to enter — where a cook prepares your food using ingredients you never see, recipes you don't know, and equipment you can't touch. The cook hands the plate to the waiter. The waiter hands the plate to you.
You are the client. The kitchen is the server. The waiter is the HTTP request. The recipe book and pantry are the business logic and database — locked away, only the kitchen can touch them.
Why is this design good?
That's client–server. Trust boundary, separation of concerns, and shared resources — all in one model.
A client is any program that initiates a request to another program. That's it. Some examples:
gmail.com/api/v1/inboxcurl hitting your local dev serverClients are never trusted. Anyone can modify them. Browsers have DevTools. Mobile apps can be decompiled. curl can send any header. You should assume every request is hostile until proven otherwise.
A server is a program that waits for requests and responds to them. It listens on a port (we'll see why in Chapter 3). It runs continuously. It is the only place where:
If your server were also untrusted, there would be no point — you'd just be exposing the database to the world.
Anything on a client can be edited. Anything on the server, you control. So every important decision — auth, pricing, , permissions — must be made on the server. The client only displays.
No matter the language, framework, or scale, every backend has the same three layers:
Rendering diagram…
Sits in front of your app. Handles things every request needs:
You usually don't write this layer. It's nginx, Cloudflare, an API Gateway, or like Helmet.
This is where Express lives. Routes match URLs, controllers run business logic, services talk to the database. This is the chapter you'll spend most of your career in.
Persistent storage: PostgreSQL, Redis, S3, vector DBs. Almost always behind a private network — the database should never be reachable from the public internet.
A pricing example. Suppose a "Buy" costs ₹100.
Wrong:
// On the client
const price = 100;
fetch('/api/checkout', { method: 'POST', body: JSON.stringify({ price }) });A hostile client edits price to 1 in DevTools and presses Buy. The server obediently charges ₹1.
Right:
// On the client
fetch('/api/checkout', { method: 'POST', body: JSON.stringify({ itemId: 'sku_42' }) });// On the server
const item = await db.items.findById(req.body.itemId);
const charge = await stripe.charges.create({ amount: item.The server looked up the price itself. The client doesn't get to vote on amounts. Every important decision is made on the server, full stop.
Client-server is the default but not the only model. Peer-to-peer (P2P) lets nodes talk to each other directly without a central server.
| Client-server | Peer-to-peer | |
|---|---|---|
| Example | Gmail, every SaaS | BitTorrent, Bitcoin, WebRTC video calls |
| Has a single source of truth? | Yes (the server) | No (consensus or per-peer) |
| Scales by adding capacity? | Yes (add more servers) | Yes (more peers = more capacity) |
| Easy to moderate / control? | Yes | No |
| Trust model | Trust the server | Trust the protocol |
Most apps you'll build are client-server. P2P shines for content distribution, real-time media, and decentralized currencies.
A stateful server remembers things between requests for a given client (e.g. session in memory). A stateless server stores nothing per-client — every request is independent and carries everything it needs (token, params).
Stateless wins for scaling. If your server holds no per-client , you can run 5 copies behind a and any copy can serve any user. State, when needed, gets pushed into the database or Redis — somewhere shared.
Rendering diagram…
This is the cornerstone of horizontal scaling. We'll come back to it in L5.
Trusting the client to enforce anything. Validation that runs only in is decoration. The server must validate every , every time.
| Mistake | Why it's wrong | What to do instead |
|---|---|---|
| Pricing on the client | A user changes the price in DevTools and pays ₹1 | Look up the price on the server every time |
| "Hidden" admin endpoints | They show up in network tab | Use real auth + role checks |
| Holding sessions in server memory | A second app instance can't see them, deploys log everyone out | Push session into Redis or sign a |
| Doing validation only in the frontend | Any client can skip the form | Re-validate on the server with Zod / Joi |
| Putting the DB on a public IP | The internet will find it within hours | Private network or VPC; only the app talks to it |
Production. Treat the trust boundary as the first thing you defend. Every public endpoint must have auth, validation, and rate limits before the business logic runs.
Performance. Stateless servers scale linearly with hardware. Stateful servers do not — they require sticky sessions, which break load balancing during deploys.
Security. Assume the client is hostile. Even your own official mobile app can be modified. Never ship an API endpoint that "trusts the app."
Beginner. Why don't we just do everything on the client? Because clients are untrusted, ephemeral, and isolated from each other. Pricing, permissions, and persistence have to live somewhere consistent and tamper-proof.
Senior. Walk me through how you would make a stateful service horizontally scalable. Identify the state. Push it to Redis/Postgres. Make handlers stateless. Verify with a chaos test killing a random replica mid-request.
A client initiates requests; a server waits for them. The split exists because clients are untrusted. Every backend has three layers — edge, app, data — and every important decision must happen in the app or data layer. Stateless servers scale; stateful ones don't.