Building an API
Time to be the kitchen, not the customer. Here's a complete (tiny) API in Python's standard library — no installs. Every curl example in this guide was verified against exactly this server.
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
ITEMS = [{"id": 1, "name": "Widget"}, {"id": 2, "name": "Gadget"}]
class Handler(BaseHTTPRequestHandler):
def _send(self, code, obj=None):
body = json.dumps(obj).encode() if obj is not None else b""
self.send_response(code)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_GET(self):
if self.path == "/items":
self._send(200, ITEMS)
elif self.path.startswith("/items/"):
try:
item_id = int(self.path.split("/")[2])
except ValueError:
return self._send(400, {"error": "id must be a number"})
item = next((i for i in ITEMS if i["id"] == item_id), None)
self._send(200, item) if item else self._send(404, {"error": "not found"})
else:
self._send(404, {"error": "not found"})
def do_POST(self):
if self.path != "/items":
return self._send(404, {"error": "not found"})
length = int(self.headers.get("Content-Length", 0))
try:
data = json.loads(self.rfile.read(length) or b"{}")
except json.JSONDecodeError:
return self._send(400, {"error": "invalid JSON"})
if "name" not in data:
return self._send(422, {"error": "missing 'name'"})
item = {"id": max(i["id"] for i in ITEMS) + 1, "name": data["name"]}
ITEMS.append(item)
self._send(201, item)
def log_message(self, *a):
pass # quiet the request logging
HTTPServer(("127.0.0.1", 8471), Handler).serve_forever()
Save as server.py, run python3 server.py, and it's live at
http://127.0.0.1:8471. (Real projects use Flask/FastAPI/Express —
same ideas, less plumbing. This version shows the machinery with
zero dependencies.)
What to notice
- Methods are just
do_GET,do_POST... — the framework (even this tiny one) routes by HTTP method. Your job: fill in each one. - Status codes are chosen deliberately —
201for created,404for missing,422for "understood but invalid." The client programs against these, so pick honestly. - Errors are JSON with a message —
{"error": "not found"}. A bare status code forces the client to guess; a message tells them. - Validation before storage — the POST checks for
"name"before appending. Garbage in, garbage stored is how you get 3 AM pages.
The design checklist (for your real API)
- Nouns in paths, verbs in methods (REST).
- Right status code for every outcome (Status Codes).
- Errors as
{"error": "human-readable message"}. - Validate input; reject with
4xx, never store garbage. - Auth on everything except what's deliberately public.
- Document it (Versioning and Docs) — an undocumented API is a rumor.
Next: Status Codes