🌙
☀️ Dark
The Engineer's Bible | Volume 1: Foundations

Chapter 16: Computers Talking to Computers (APIs & Networking)

Learning Objectives

Prerequisites

Chapter 7: Hash Maps (JSON is basically a universal hash map).

Why Does This Exist?

Everything we have built so far lived entirely on your own laptop.

But what if you are building a Weather App? Your laptop doesn't have a thermometer in London. You need data from a weather station.

What if you want to process credit cards? You cannot legally write a payment processing engine. You need to securely ask Stripe to do it for you.

We needed a universal, standardized language for two completely different computers (maybe one running Python on Mac, the other running Java on Linux) to pass data back and forth instantly over cables under the ocean.

We needed APIs (Application Programming Interfaces).

History

In the early days of the ARPANET, computers communicated using highly specific, complex, and proprietary binary protocols.

In 1989, Tim Berners-Lee invented HTTP (Hypertext Transfer Protocol) for the World Wide Web. It was simple, text-based, and human-readable.

Later, Roy Fielding defined REST (Representational State Transfer). It standardized how we use HTTP verbs (GET, POST, PUT, DELETE) to interact with data on remote servers, mapping perfectly to the CRUD database operations we learned in Chapter 15.

Mental Model

Think of a restaurant.

You are the Client (the web browser or mobile app). You sit at the table. You want a burger, but you are not allowed to go into the kitchen.

The Kitchen is the Server (the database and backend code). It has all the food and does all the work.

The Waiter is the API. You give the waiter a formatted Request ("I would like Burger #5"). The waiter walks to the kitchen, gives the order, waits, and brings back a Response (the burger on a plate).

You don't need to know how to cook a burger; you just need to know how to ask the waiter properly.

Internal Working

When you type https://api.github.com/users/alice, the OS performs a DNS lookup to convert that URL into an IP Address (e.g., 140.82.112.4).

Your computer constructs a plain-text HTTP Request:

python
GET /users/alice HTTP/1.1
Host: api.github.com
Accept: application/json

This text is converted to electrical pulses, routed through your modem, across fiber optic lines, to a server rack in a GitHub data center.

The server reads the text, queries its database, constructs a JSON string containing Alice's data, and sends a Response back along with a Status Code (e.g., 200 OK).

Syntax

In modern programming, we don't write the raw HTTP text ourselves. We use libraries. In Python, the requests library is the standard.

json
1import requests
2
3# Make a GET request (Ask the waiter for the menu)
4response = requests.get("https://pokeapi.co/api/v2/pokemon/pikachu")
5
6if response.status_code == 200:
7    data = response.json() # Converts JSON text into a Python Dictionary
8    print(data["weight"])
9else:
10    print("Error finding Pokemon.")

Visual Explanation

REST API / CRUD Mapping: Client Action HTTP Verb Database Action (SQL) ------------- --------- --------------------- Read a Profile -> GET -> SELECT * FROM users Create Account -> POST -> INSERT INTO users Update Email -> PUT/PATCH -> UPDATE users SET... Delete Account -> DELETE -> DELETE FROM users

Tiny Example

Sending data TO a server (POST request).

json
1payload = {"title": "My new post", "body": "Hello World!"}
2response = requests.post("https://jsonplaceholder.typicode.com/posts", json=payload)
3print(response.status_code) # Output: 201 (Created)

Common Mistakes

Blocking the Main Thread

Why it fails: The network is incredibly slow compared to a CPU. If you make a requests.get() call, your program freezes and waits for the server to reply. If the server takes 10 seconds, your entire app is frozen for 10 seconds. In a mobile app, this causes the screen to lock up, and the OS will kill the app.

The Fix: Network requests must eventually be handled Asynchronously (running in the background while the UI keeps updating). We will cover this in Concurrency (Chapter 17).

Debugging

When APIs fail, the server tells you exactly why using a 3-digit HTTP Status Code.

Mini Project

Time: 20 minutes.

Goal: The Chuck Norris Generator.

Install the requests library. Make a GET request to https://api.chucknorris.io/jokes/random. Parse the JSON response into a dictionary. Print the value of the "value" key to display a random joke to the terminal.

💡 See One Approach (Mini Project)

This is one valid solution — yours may differ.

python
# HTTP client — making real API requests
import urllib.request
import json

def fetch_json(url):
    """Minimal HTTP GET using only stdlib."""
    try:
        with urllib.request.urlopen(url, timeout=5) as response:
            status = response.status
            body = response.read().decode("utf-8")
            return status, json.loads(body)
    except Exception as e:
        return None, str(e)

# JSONPlaceholder — free test REST API
print("=== REST API Demo (JSONPlaceholder) ===
")

# GET a single user
print("GET /users/1")
status, data = fetch_json("https://jsonplaceholder.typicode.com/users/1")
if data and isinstance(data, dict):
    print(f"  Status:  {status}")
    print(f"  Name:    {data.get('name')}")
    print(f"  Email:   {data.get('email')}")
    print(f"  City:    {data.get('address', {}).get('city')}")

print()

# GET list with filtering
print("GET /posts?userId=1")
status, posts = fetch_json("https://jsonplaceholder.typicode.com/posts?userId=1")
if posts:
    print(f"  Status:    {status}")
    print(f"  Posts found: {len(posts)}")
    print(f"  First title: {posts[0]['title'][:50]}...")

Bigger Project

Time: 1.5 hours.

Goal: The Weather Dashboard.

Sign up for a free API key at OpenWeatherMap. Write a script that asks the user for a City name using input(). Construct a URL string combining the base URL, the city name, and your API key. Make the GET request. Extract the current temperature, humidity, and weather description from the nested JSON dictionary and print a nicely formatted weather report.

💡 See One Approach (Bigger Project)

This is one valid solution — yours may differ.

python
# Build a REST API server using only Python stdlib (http.server)
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import threading
import urllib.request
import time

# In-memory "database"
PRODUCTS = {
    1: {"id": 1, "name": "Mechanical Keyboard", "price": 129.99, "stock": 50},
    2: {"id": 2, "name": "USB Hub",              "price":  24.99, "stock": 200},
    3: {"id": 3, "name": "Monitor Stand",         "price":  44.99, "stock": 75},
}
next_id = 4

class APIHandler(BaseHTTPRequestHandler):
    def log_message(self, *args):
        pass  # Suppress default access log

    def send_json(self, status, data):
        body = json.dumps(data, indent=2).encode()
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", len(body))
        self.end_headers()
        self.wfile.write(body)

    def do_GET(self):
        global PRODUCTS
        if self.path == "/products":
            self.send_json(200, list(PRODUCTS.values()))
        elif self.path.startswith("/products/"):
            try:
                pid = int(self.path.split("/")[-1])
                if pid in PRODUCTS:
                    self.send_json(200, PRODUCTS[pid])
                else:
                    self.send_json(404, {"error": "Product not found"})
            except ValueError:
                self.send_json(400, {"error": "Invalid product ID"})
        else:
            self.send_json(404, {"error": "Route not found"})

    def do_POST(self):
        global PRODUCTS, next_id
        if self.path == "/products":
            length = int(self.headers.get("Content-Length", 0))
            body = json.loads(self.rfile.read(length))
            
            if not body.get("name") or not body.get("price"):
                self.send_json(400, {"error": "name and price are required"})
                return
            
            product = {
                "id":    next_id,
                "name":  body["name"],
                "price": float(body["price"]),
                "stock": int(body.get("stock", 0)),
            }
            PRODUCTS[next_id] = product
            next_id += 1
            self.send_json(201, product)

# Run server in background thread
server = HTTPServer(("localhost", 8765), APIHandler)
thread = threading.Thread(target=server.serve_forever)
thread.daemon = True
thread.start()
time.sleep(0.1)  # Give server a moment to start

print("=== Products REST API ===")
print("Server running on http://localhost:8765
")

BASE = "http://localhost:8765"

# GET all products
with urllib.request.urlopen(f"{BASE}/products") as r:
    products = json.loads(r.read())
    print(f"GET /products → {r.status} ({len(products)} items)")
    for p in products:
        print(f"  #{p['id']} {p['name']:25} ${p['price']:.2f}")

# GET single product
with urllib.request.urlopen(f"{BASE}/products/2") as r:
    p = json.loads(r.read())
    print(f"
GET /products/2 → {r.status}")
    print(f"  {p}")

# POST new product
new_product = json.dumps({"name": "Webcam", "price": 79.99, "stock": 30}).encode()
req = urllib.request.Request(f"{BASE}/products", data=new_product,
                              headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(req) as r:
    created = json.loads(r.read())
    print(f"
POST /products → {r.status} (Created #{created['id']}: {created['name']})")

# GET 404
try:
    urllib.request.urlopen(f"{BASE}/products/999")
except urllib.request.HTTPError as e:
    print(f"
GET /products/999 → {e.code} (expected 404)")

server.shutdown()

Production Usage

APIs are the economy of the modern internet.

When you order an Uber, the Uber app (Client) makes a POST request to Uber's API. Uber's servers make a GET request to Google Maps' API to calculate the route. Uber then makes a POST request to Stripe's API to charge your card. Stripe makes a request to Visa's API. Dozens of servers communicate silently in milliseconds.

Best Practices

Interview Questions

🟢 Easy:What is JSON?

🔍 Reveal Answer
Answer: JavaScript Object Notation. It is a lightweight, human-readable text format used to transmit data structures (like arrays and dictionaries) over the network.

🟡 Medium:Explain the difference between a GET and a POST request.

🔍 Reveal Answer
Answer: GET is used to retrieve data and should have no side effects (it doesn't modify the database). POST is used to submit new data to the server to create a record, and includes a "payload" or body of data.

🔴 Hard:What makes an API "RESTful"?

🔍 Reveal Answer
Answer: It adheres to REST architectural constraints, mainly being "Stateless" (the server doesn't remember previous requests; every request must contain all necessary authentication and context) and using standard HTTP verbs mapped to resources (URLs acting as nouns, like /users/1).

Revision Sheet

✅ I can design a RESTful API from scratch and explain what happens at every layer of the network stack during a request.

Connections

← Previous (Chapter 20) Chapter 20 Next (Chapter 22) → Chapter 22