Typorithm · easy

A request crosses a real ASGI application

pythonfastapiindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

A request crosses a real ASGI application

0%

Concept and contract

Observe routing, JSON and status codes through an in-process client.

A Python function is not an HTTP API until an application decides how a request reaches it and how its result becomes a response. FastAPI provides an ASGI application with routing, dependency handling and serialization. Begin by observing those boundaries rather than deploying a server before the smallest contract is understood.

The example defines one GET route returning a small JSON object. TestClient sends a local request through the application without opening an internet socket. The reported status and decoded body are observations of the application, not a direct call to the endpoint function.

Mechanism and worked example

The decorator associates the path and method with an endpoint.

The decorator associates the path and method with an endpoint. Returning a dictionary lets FastAPI produce a JSON response. A different path produces a not-found response, and a different method on the same path is not automatically another accepted operation. These are distinct contracts that a function-only test can miss.

Each inspection creates a fresh application and enters the client context. There is no database, remote service or live feed. The helper returns only the response status and JSON body, allowing exact comparisons while leaving the supplied request description unchanged.

Complete worked example

python

from fastapi import FastAPI
from fastapi.testclient import TestClient

def inspect_example(request):
    app = FastAPI()
    @app.get("/hello")
    def hello():
        return {"message": "hello"}
    with TestClient(app) as client:
        response = client.request(request.get("method", "GET"), request.get("path", "/hello"))
        body = None if request.get("method") == "HEAD" else response.json()
        return {"status": response.status_code, "body": body}
Reasoning
The decorator associates the path and method with an endpoint. Returning a dictionary lets FastAPI produce a JSON response. A different path produces a not-found response, and a different method on the same path is not automatically another accepted operation. These are distinct contracts that a function-only test can miss. Each inspection creates a fresh application and enters the client context. There is no database, remote service or live feed. The helper returns only the response status and JSON body, allowing exact comparisons while leaving the supplied request description unchanged.

Focused decision: use the complete example for context

python

from fastapi import FastAPI

app = FastAPI()

@app.get("/hello")
def hello():
    return {"message": "hello"}
Focused decision
Reconstruct this focused decision after the warm-up. The complete imports, model or application, and test client remain in the worked example. Typing records practice; only the downloadable computer lab executes Python.

Counterexamples and common mistakes

The controlled mistake returns a Python string instead of an object.

The controlled mistake returns a Python string instead of an object. The endpoint still executes successfully, but the JSON shape is different. A successful status alone would miss this regression. Test both the envelope and the individual values that clients rely on.

Likewise, changing the URL in a client does not create a matching server route. Do not treat every failure as a connection problem or silently replace an unexpected response with an empty object. Inspect the method, path, status and documented representation before investigating transport behavior.

Controlled mistake: observe the actual wrong behavior

python

from fastapi import FastAPI
from fastapi.testclient import TestClient

def inspect_example(request):
    app = FastAPI()
    @app.get("/hello")
    def hello():
        return "hello"
    with TestClient(app) as client:
        response = client.request(request.get("method", "GET"), request.get("path", "/hello"))
        body = None if request.get("method") == "HEAD" else response.json()
        return {"status": response.status_code, "body": body}
Reasoning
The controlled mistake returns a Python string instead of an object. The endpoint still executes successfully, but the JSON shape is different. A successful status alone would miss this regression. Test both the envelope and the individual values that clients rely on. Likewise, changing the URL in a client does not create a matching server route. Do not treat every failure as a connection problem or silently replace an unexpected response with an empty object. Inspect the method, path, status and documented representation before investigating transport behavior.

Independent practice: one bounded change

Add HEAD as an explicitly supported method on the same path while retaining GET.

Add HEAD as an explicitly supported method on the same path while retaining GET. Change the route declaration to api_route with the two declared methods. A HEAD response has no body; the inspection helper accounts for this HTTP behavior rather than attempting to decode absent JSON.

Run an accepted GET, an accepted HEAD and a missing-path request. Keep POST rejected. The task is a small routing change, not a rewrite of the client or a blanket allowance for every method. Write down the expected status before running each case.

Step 1: Support HEAD explicitly while preserving GET and rejecting unrelated methods. Step 2: Retain the working imports and application structure. Change the stated policy, run the supplied practice cases, then explain the newly accepted or rejected boundary.

Practice starter: solve before reading the solution

python

from fastapi import FastAPI
from fastapi.testclient import TestClient

def inspect_example(request):
    app = FastAPI()
    @app.get("/hello")
    def hello():
        return {"message": "hello"}
    with TestClient(app) as client:
        response = client.request(request.get("method", "GET"), request.get("path", "/hello"))
        body = None if request.get("method") == "HEAD" else response.json()
        return {"status": response.status_code, "body": body}

# Independent task: Support HEAD explicitly while preserving GET and rejecting unrelated methods.
Reasoning
Support HEAD explicitly while preserving GET and rejecting unrelated methods.
  • Hint 1: Find the one field or branch that owns this policy.
  • Hint 2: Compare the first changed acceptance case with the retained baseline.

Solution, tests and trade-offs

The solution declares GET and HEAD together.

The solution declares GET and HEAD together. It preserves the GET object and returns no decoded body for HEAD. A missing path remains a 404, and unsupported methods remain visible. The original baseline demonstrates a working API before the student adds one deliberate capability.

This fixture establishes request handling, not production capacity or readiness. A deployed service still needs suitable process supervision, timeouts, error reporting and security boundaries. Later lessons introduce those responsibilities in manageable pieces instead of suggesting that a hello-world response proves them all.

Practice solution and acceptance checks

python

from fastapi import FastAPI
from fastapi.testclient import TestClient

def inspect_example(request):
    app = FastAPI()
    @app.api_route("/hello", methods=["GET", "HEAD"])
    def hello():
        return {"message": "hello"}
    with TestClient(app) as client:
        response = client.request(request.get("method", "GET"), request.get("path", "/hello"))
        body = None if request.get("method") == "HEAD" else response.json()
        return {"status": response.status_code, "body": body}
Reasoning
The solution declares GET and HEAD together. It preserves the GET object and returns no decoded body for HEAD. A missing path remains a 404, and unsupported methods remain visible. The original baseline demonstrates a working API before the student adds one deliberate capability. This fixture establishes request handling, not production capacity or readiness. A deployed service still needs suitable process supervision, timeouts, error reporting and security boundaries. Later lessons introduce those responsibilities in manageable pieces instead of suggesting that a hello-world response proves them all.

Interview reasoning and next step

Explain why the GET test exercises more than calling hello() directly.

Explain why the GET test exercises more than calling hello() directly. Identify which part of the observed result would expose a changed JSON shape, a wrong path or an unsupported method. These distinctions are basic debugging tools for any framework.

Next, add typed path and query parameters. Keep the same habit: test the HTTP boundary and inspect the exact accepted and rejected behavior. Type hints are useful only when the framework applies them at the relevant request location.

Optional stretch — after the core task works: Add one independently reasoned boundary case and explain its expected value before running it.

Computer practice: https://applydjinn.com/typorithm/python-api-lab-20261105.zip. Use an isolated Python environment and the pinned dependencies. Run verify.py --track fastapi, then edit the working exercise baseline and run --practice fa-app. The reference suite rejects meaningful counterexamples and checks values, types and unchanged inputs. All data is owned and all HTTP clients are in-process. No production database, feed, account or paid service is used.

Observe routing, JSON and status codes through an in-process client.