Skip to content
Aabha AI Academy

Stage 1 · L04

Place middleware around the request lifecycle

Core · original Session 1 plus essential local Git/Docker setup

Middleware wraps the request/response path so shared behavior need not be repeated in every handler. This snapshot generates an X-Request-ID and measures elapsed application time with perf_counter. The identifier belongs to this server's observation of the request. An arbitrary incoming value is not reused as trusted correlation.

Execution enters identify_request, records the start time, awaits call_next, then adds response headers. That work happens for valid reads and ordinary 404/422 responses produced by the inner application. Look at headers with curl -i rather than assuming they appear in the JSON body. The Server-Timing value is milliseconds elapsed for this wrapped path, not a benchmark for database or provider time that does not exist yet.

The lifespan context is process-resource ownership; middleware is per-request work. They solve different lifetime problems. Opening an HTTP client or database engine anew in middleware for every request would make ownership costly and unclear. Stage 02 owns an engine at the application boundary and a session per request. Stage 09 replaces this simple teaching middleware with structured observations and explicit outer-error/streaming behavior.

The current decorator-based middleware does not certify all exceptional or streaming paths. An unhandled server error outside this inner response can bypass these headers, and a response's streaming completion can occur after call_next returns. State that limit when reading timing. The guided test checks the headers on a successful response; it does not claim comprehensive observability.

Generate a local request UUID; Start perf_counter timer; call_next → handler response; Add X-Request-ID and timing; Return the endpoint body; Simple middleware does not prove; streaming completion.
Generate a local request UUID; Start perf_counter timer; call_next → handler response; Add X-Request-ID and timing; Return the endpoint body; Simple middleware does not prove; streaming completion.

Follow the running code

Focused lesson example; see the end-of-stage capstone for the cumulative app · stage 01

request_id = str(uuid4())
start = perf_counter()
response = await call_next(request)
response.headers['X-Request-ID'] = request_id
response.headers['Server-Timing'] = f'app;dur={(perf_counter()-start)*1000:.2f}'
return response

Predict and observe this focused example using the concepts explained above. Its boundary is stated in the focused answer.

Guided lab

  1. Read the explanation and predict the focused example’s outcome.
  2. GET the same resource twice, including a caller-supplied X-Request-ID. Compare the generated response IDs and timing header.
  3. Compare the observed outcome with the focused answer and state its boundary.

Expected: Each response receives a newly generated UUID instead of trusting the submitted value. Server-Timing reports elapsed wrapped application time in milliseconds. It is not database/provider time or streaming completion. Lifespan is process ownership, whereas middleware runs per request.

  • A stopped process has no middleware response; unexpected/streaming error coverage awaits stage 09.

Focused exercise and answer

Complete this focused exercise before reading its answer. The full native transfer is introduced only at the end of the stage.

Your transfer task: GET the same resource twice, including a caller-supplied X-Request-ID. Compare the generated response IDs and timing header.

  1. GET the same resource twice, including a caller-supplied X-Request-ID. Compare the generated response IDs and timing header.
Inspect the matching answer

This answer addresses the focused exercise above; the cumulative implementation is shown only after the stage prerequisites.

Each response receives a newly generated UUID instead of trusting the submitted value. Server-Timing reports elapsed wrapped application time in milliseconds. It is not database/provider time or streaming completion. Lifespan is process ownership, whereas middleware runs per request.

Check your reasoning

Should one mutable request identifier be stored globally?

Show the explanation

No. Concurrent requests need independent context; a global current identifier would be overwritten by another request.

Reading progress

54 lessons remain open to guests. Marking a lesson read records reading only; it does not award assessment credit or a certificate.

Device reading marks require browser storage. Reading is always available.

Sign in or create an account to save separate account progress. Your current page is kept.