System DesignBeginner 11 min Lesson 1 of 42

Client and Server

What actually happens when you open a website: who asks, who answers, and why the split exists at all.

System Design · Lesson 1 of 42
0/42 done(0%)

What is it? #

A server is a computer that stays connected to the internet, waiting for requests and answering them. A client is whatever asks — a browser, a mobile app, another program.

The split exists because some things cannot live on the client. Shared data, secrets and rules that must not be tampered with have to sit somewhere the user does not control.

Anyone can open the browser developer tools and change what the page does. Nobody can change what your server does. That is why price calculations, permission checks and payments happen on the server.

Physically a server is ordinary hardware. What makes it a server is that it runs continuously, listens on a port, and is reachable from outside.

Think of it like this #

A restaurant kitchen. You sit at a table and order; you do not walk in and cook. The kitchen holds the ingredients, the recipes and the standards, and it serves hundreds of tables from one place.

If every customer had their own kitchen, no two meals would follow the same recipe and nobody could control the ingredients.

Simple example #

You type an address into a browser. The browser opens a connection, sends a request, and waits. A program on a machine somewhere reads the request, decides what to do, and sends back HTML. The browser draws it.

Code #

TEXT
CLIENT                                   SERVER
(browser, app)                           (always-on machine)

  "GET /products/42"  ───────────────▶   receive request
                                         check who is asking
                                         read the database
                                         apply business rules
  ◀───────────────  "200 OK + data"      build a response
  render the page
PYTHON
# The smallest possible server, using only the standard library
from http.server import BaseHTTPRequestHandler, HTTPServer
import json


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/health":
            body = json.dumps({"status": "ok"}).encode()
            self.send_response(200)
            self.send_header("Content-Type", "application/json")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
        else:
            self.send_error(404, "Not found")


if __name__ == "__main__":
    server = HTTPServer(("0.0.0.0", 8000), Handler)   # listen on port 8000
    print("listening on http://0.0.0.0:8000")
    server.serve_forever()                            # wait for requests, forever
BASH
curl http://localhost:8000/health
# {"status": "ok"}

How it works #

HTTPServer(("0.0.0.0", 8000), Handler) binds to a port and starts listening. 0.0.0.0 means "accept connections on any network interface", as opposed to 127.0.0.1, which accepts only connections from the same machine.

serve_forever() is the defining behaviour of a server: a loop that waits for a connection, handles it, and waits again. It never finishes on its own.

do_GET runs for each GET request. It inspects the path, decides what to return, and writes a status code, headers and a body.

The 404 branch matters. A server must answer every request with something, even if the answer is "no such thing".

On the client side, curl resolves the name, opens a TCP connection to port 8000, sends the request text, reads the response, and closes.

Everything else in this track — load balancers, caches, queues, databases — is added around this basic exchange to make it faster, more reliable or able to serve more clients.

Real-world use #

Real servers are the same shape with more layers. A production Python application runs behind a process manager, behind Nginx, behind a load balancer, behind a CDN. Each layer is still a server answering requests.

The client side has grown too. A browser page, a mobile app and another backend service are all clients, and they often call the same API.

The security consequence is the one to internalise. Anything the client computes can be faked: a discount calculated in JavaScript can be edited by the user. The server must recompute and verify anything that matters.

The other practical consequence is state. Servers usually handle each request independently, which is what allows several identical servers to sit behind a load balancer — the topic of a later lesson.

Common mistakes #

  • Trusting data from the client. Validate everything server-side, every time.
  • Calculating prices, discounts or permissions in the browser and believing the result.
  • Binding to 127.0.0.1 and wondering why nothing outside the machine can connect.
  • Assuming a server keeps memory of the last request; usually it does not.
  • Putting API keys in frontend code, where anyone can read them.

Practice #

Run the server above. Add a /time endpoint returning the current time as JSON, and make an unknown path return a JSON 404 body rather than HTML. Then call both endpoints with curl and with a browser, and look at the response headers in the browser developer tools.

Quick quiz

  1. 1. What makes a computer a server?

  2. 2. Why must important rules run on the server?

  3. 3. What does binding to `0.0.0.0` mean?

  4. 4. What does `serve_forever()` do?

  5. 5. Which is a client?

Summary

  • A server listens continuously and answers requests; a client asks.
  • The split exists so shared data and rules live where users cannot change them.
  • Never trust anything computed or validated only on the client.
  • Binding address and port decide who can reach your server.
  • Everything else in system design is layered around this exchange.