Exercises: Module 01 — Introduction¶
Instructions¶
Complete each exercise in order. Exercises increase in difficulty. Submit your answers by editing this file or committing a solutions file alongside it.
Easy Exercises (1–3)¶
Exercise 1¶
Difficulty: Easy Objective: Identify the components of an HTTP request and response
Given the following HTTP exchange, label each part:
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...
{"name": "Alice", "email": "alice@example.com"}
---
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/99
{"id": 99, "name": "Alice", "email": "alice@example.com"}
Questions:
1. What is the HTTP method?
2. What is the URL path?
3. List two request headers and explain what each tells the server.
4. What does the 201 status code mean?
5. What does the Location response header typically indicate?
Exercise 2¶
Difficulty: Easy Objective: Match HTTP methods to CRUD operations
For each operation below, write the correct HTTP method and a plausible URL:
- Retrieve a list of blog posts
- Create a new blog post
- Replace an entire blog post (full update)
- Update only the title of a blog post (partial update)
- Delete a blog post
Exercise 3¶
Difficulty: Easy Objective: Recall the key differences between WSGI and ASGI
Answer these questions without looking at the module:
- What does WSGI stand for? What PEP defines it?
- What does ASGI stand for?
- Name one WSGI server and one ASGI server.
- Name one Python framework that is WSGI-only and one that is ASGI.
- What is the function signature of a minimal WSGI application?
Medium Exercises (4–6)¶
Exercise 4¶
Difficulty: Medium Objective: Write and run a minimal WSGI application
Write a WSGI application (no Flask, no external libraries except the standard library) that:
- Returns {"status": "ok"} with status 200 for GET /health
- Returns {"message": "Hello, <name>!"} with status 200 for GET /hello/<name> (e.g., /hello/Alice → {"message": "Hello, Alice!"})
- Returns {"error": "not found"} with status 404 for any other path
# Starter scaffold
def application(environ, start_response):
path = environ.get("PATH_INFO", "/")
method = environ.get("REQUEST_METHOD", "GET")
# Your implementation here
pass
Test it by running with python -m wsgiref.simple_server or gunicorn.
Exercise 5¶
Difficulty: Medium Objective: Extend the Flask hello-world with error handling
Take the Flask application from the module's Theory section and add:
- A
GET /statusendpoint that returns{"status": "running", "version": "1.0"}with status 200 - A
GET /divide/<float:a>/<float:b>endpoint that dividesabyband returns the result - Proper error handling: return
{"error": "cannot divide by zero"}with status 400 ifb == 0
Run the app and verify each endpoint manually with curl or a browser.
Exercise 6¶
Difficulty: Medium Objective: Explore FastAPI's automatic documentation
Take the FastAPI application from the module's Theory section:
- Run it with
uvicorn main:app --reload - Visit
http://localhost:8000/docsand explore the interactive UI - Use the interactive UI to:
- Make a GET request to
/ - Make a POST request to
/echowith{"content": "hello", "repeat": 3} - Now add a
GET /items/{item_id}endpoint that accepts an optional query parametercategory: str = Noneand returns{"item_id": item_id, "category": category} - Refresh the
/docspage. What changed?
Write a short description (3–5 sentences) of what the auto-generated documentation shows and how it differs from documentation you would write by hand.
Hard Exercises (7–8)¶
Exercise 7¶
Difficulty: Hard Objective: Implement a raw ASGI application
Write a minimal ASGI application (no FastAPI, no Starlette) that:
- Handles
GET /pingand returns{"pong": true}with status 200 - Handles
POST /reverse— reads the JSON body{"text": "hello"}and returns{"result": "olleh"}with status 200 - Returns
{"error": "method not allowed"}with status 405 for wrong methods on known paths - Returns
{"error": "not found"}with status 404 for unknown paths
# Starter scaffold — fill in the application function
import json
async def application(scope, receive, send):
"""
scope: dict with keys 'type', 'method', 'path', etc.
receive: async callable to read request body
send: async callable to send response
"""
if scope["type"] == "http":
# Your implementation here
pass
# Run with: uvicorn exercise7:application --reload
This exercise forces you to understand the raw ASGI interface before relying on the framework abstractions.
Exercise 8¶
Difficulty: Hard Objective: Compare Flask and FastAPI validation behavior
Write the same endpoint in both Flask and FastAPI:
Specification: POST /calculate accepts {"operation": "add|subtract|multiply", "a": number, "b": number} and returns {"result": number}.
Requirements:
- operation must be one of "add", "subtract", or "multiply" — return 400/422 for invalid values
- a and b must be numbers — return 400/422 if they are not
- Both fields are required
Flask version: write your own validation logic. FastAPI version: use a Pydantic model with field validators.
After writing both, write 5 sentences comparing: - The amount of code required for validation - The quality of error messages returned to the client - The ease of adding a new validation rule
Expert Exercise (9)¶
Exercise 9¶
Difficulty: Expert Objective: Build a framework-agnostic middleware layer
Design and implement a Python class RequestLogger that works as middleware for both WSGI and ASGI applications. It should:
- Log each request: method, path, and timestamp (to stdout in JSON format)
- Log each response: status code and response time in milliseconds
- Work correctly when wrapping a WSGI app (synchronous
__call__) - Work correctly when wrapping an ASGI app (async
__call__)
# Target usage:
# WSGI usage (Flask)
from flask import Flask
flask_app = Flask(__name__)
wsgi_app = RequestLogger(flask_app, mode="wsgi")
# ASGI usage (FastAPI)
from fastapi import FastAPI
fastapi_app = FastAPI()
asgi_app = RequestLogger(fastapi_app, mode="asgi")
Hint: WSGI middleware wraps application(environ, start_response). ASGI middleware wraps application(scope, receive, send). The two modes require different implementations internally.
Document your design choices in a comment block at the top of the file. What trade-offs did you make?