Skip to content
tezvyn:
Menu

Write an async decorator that logs execution time for FastAPI

Source: fastapi.tiangolo.comMediumHow cards are made

Tests

Python closures, async/await, and decorator stacking in FastAPI.

Key points

use functools.wraps, wrap perf_counter around awaited call, log ms, and place decorator above path operation.

Watch out for

forgetting to await the coroutine or omitting wraps.

What's really being asked

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.

The full answer

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.

The mistakes people make

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.

What usually comes next

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.

A 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.

Interview question

When building an async execution-time decorator for FastAPI, why is omitting functools.wraps on the inner wrapper considered a red flag?

  • a.It causes the wrapper to run synchronously, blocking the event loop during high-concurrency requests.
  • b.It strips the original function's metadata, which breaks FastAPI's OpenAPI schema generation and dependency injection.Correct
  • c.It makes FastAPI treat the endpoint as synchronous, offloading it to a thread pool and skewing the timer.
  • d.It prevents the decorator from awaiting the original coroutine, leading to unhandled runtime warnings.
Why?

functools.wraps preserves the original function's name, signature, and metadata, which FastAPI relies on to generate OpenAPI docs and resolve dependencies; omitting it exposes the wrapper's metadata instead. Distractor A is wrong because wraps has no effect on whether code runs synchronously or blocks the event loop.

Just read this? Test yourself on what you have been reading.

Read the original → fastapi.tiangolo.com

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on python — each one lists the topics its interview covers.

See open roles