Write a FastAPI middleware that adds X-Process-Time header
Tests FastAPI lifecycle and header mutation. Strong answers use the http middleware decorator, await call_next, compute elapsed time, and inject X-Process-Time before returning. Red flag: forgetting to await call_next or mutating headers after the return.
WHAT THIS TESTS:
This question probes your grasp of the FastAPI middleware lifecycle and the request-response flow. The interviewer wants to see that you know middleware sits between the server and path operations, that you understand call_next is the hook that yields control to the route handler, and that you can safely mutate response headers before the response leaves the application. It also checks whether you reach for appropriate timing utilities and respect async boundaries.
A GOOD ANSWER COVERS:
A good answer hits four things in order. First, use the @app.middleware("http") decorator to register the middleware for HTTP requests. Second, define an async function that accepts request and call_next as parameters. Third, record a start timestamp with time.perf_counter, await call_next(request) to get the response generated by downstream path operations, then compute the delta. Fourth, assign the elapsed time to response.headers["X-Process-Time"] as a string, and return the same response object. Mentioning that background tasks and dependencies with yield execute after the middleware returns shows deeper lifecycle awareness.
COMMON WRONG ANSWERS:
Common red flags include forgetting to await call_next, which blocks the event loop and prevents the route from running. Another is instantiating a new Response object instead of enriching the one returned by call_next; this drops headers, cookies, or status codes set by the path operation. Some candidates try to set headers on the request object, or they use time.time instead of time.perf_counter, which is less precise for short durations. A subtle mistake is formatting the header as a float without limiting precision, leading to noisy values like 0.0043210296630859375 instead of a clean millisecond string.
LIKELY FOLLOW-UPS:
The interviewer may ask how to exclude certain paths like health checks from timing, which you can do by inspecting request.url.path before recording. They might ask what happens if an exception is raised inside call_next, in which case you should wrap the await in a try-finally block to guarantee logging or metrics emission. Another follow-up is ordering: multiple middlewares execute in the order they are added, so if authentication middleware runs first, it will wrap your timing middleware.
ONE CONCRETE EXAMPLE:
A senior candidate might import time and Request, apply the app middleware http decorator, define an async function named add process time header that takes request and call next, capture start time with time.perf_counter, await call_next with the request to get the response, compute the delta, assign the X Process Time header on the response object to the string representation of that delta, and return the response. This keeps the original response intact, uses high-resolution timing, and respects the async contract.
Read the original → fastapi.tiangolo.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.