Write an async decorator that logs execution time for FastAPI
Python closures, async/await, and decorator stacking in FastAPI.
use functools.wraps, wrap perf_counter around awaited call, log ms, and place decorator above path operation.
forgetting to await the coroutine or omitting wraps.
WHAT THIS TESTS: This question probes whether you can write higher-order functions in Python that correctly handle asynchronous execution, preserve function metadata, and integrate cleanly with FastAPI path operation functions. The interviewer cares about your grasp of closures, the difference between a coroutine and its result, and decorator stacking order in a real web framework.
A GOOD ANSWER COVERS: First, import wraps from functools to ensure the decorated function retains its original name and signature, which FastAPI uses for OpenAPI schema generation. Second, define an outer decorator function that accepts the original async function and returns an async inner wrapper. Third, inside the wrapper, capture a start timestamp with time.perf_counter, await the original function, capture the end timestamp, compute the delta, convert it to milliseconds, and log the result. Fourth, return the result of the awaited call so downstream code receives the correct payload. Fifth, apply the decorator by placing it directly above the path operation function, between the business logic and the FastAPI route decorator like app.get, so the timing logic executes on every request to that endpoint.
COMMON WRONG ANSWERS: A major red flag is writing a synchronous wrapper that calls the async function without awaiting it; this returns an unawaited coroutine object, making the timer measure almost zero seconds while the actual work never runs. Another red flag is omitting functools.wraps, which strips the function metadata and can confuse FastAPI dependency injection and automatic documentation. A third mistake is using time.time instead of perf_counter for sub-second measurements, since time.time is wall-clock time and can move backwards, whereas perf_counter is monotonic and designed for benchmarking.
LIKELY FOLLOW-UPS: The interviewer may ask how to make the decorator configurable so the log level or message format can be customized per endpoint. They might also ask how this decorator differs from FastAPI middleware, which runs for every request before it reaches any specific path operation and again on the response, whereas the decorator targets a single path operation. Another follow-up is how to handle exceptions inside the wrapper so that timing is logged even when the coroutine raises an error.
ONE CONCRETE EXAMPLE: Imagine an endpoint that queries a database for 150 milliseconds. With the decorator applied, the logs show ENTRY for the function name, a 152 millisecond elapsed time, and EXIT with the returned JSON payload. Without functools.wraps, FastAPI might show the wrapper function name in the generated OpenAPI docs instead of the real endpoint name. If the wrapper forgets to await, the endpoint returns a coroutine object to the framework, causing a 500 error or an unhandled runtime warning.
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.