Bird
Raised Fist0
FastAPIframework~10 mins

Custom middleware creation in FastAPI - Step-by-Step Execution

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Concept Flow - Custom middleware creation
Request received by FastAPI
Middleware intercepts request
Middleware processes or modifies request
Pass request to next handler (route or next middleware)
Response generated by route
Middleware intercepts response
Middleware processes or modifies response
Response sent back to client
Middleware sits between the client and route handlers, intercepting requests and responses to process or modify them.
Execution Sample
FastAPI
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()

class CustomMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        print("Before request")
        response = await call_next(request)
        print("After response")
        return response

app.add_middleware(CustomMiddleware)
This code creates a middleware that prints messages before and after processing each request.
Execution Table
StepActionRequest StateResponse StateOutput/Effect
1Request received by FastAPIRequest object createdNo response yetIncoming request starts processing
2Middleware dispatch startsRequest passed to middlewareNo response yetPrints 'Before request'
3Middleware calls next handlerRequest forwarded to route handlerNo response yetRoute processes request
4Route generates responseRequest handledResponse object createdResponse ready to return
5Middleware resumes after call_nextRequest handledResponse receivedPrints 'After response'
6Middleware returns responseRequest handledResponse returnedResponse sent to client
7Request cycle endsNo active requestResponse sentCycle complete
💡 Response sent back to client, middleware cycle complete
Variable Tracker
VariableStartAfter Step 2After Step 3After Step 5Final
requestNoneRequest objectRequest objectRequest objectRequest handled
responseNoneNoneNoneResponse objectResponse sent
Key Moments - 3 Insights
Why do we call 'await call_next(request)' inside the middleware?
Because 'call_next' passes the request to the next handler (like the route). Without calling it, the request would stop and no response would be generated. See execution_table step 3.
What happens if we modify the response after 'call_next'?
The middleware can change the response before sending it back to the client. This happens after step 5 in the execution_table, where the middleware can add headers or change content.
Can middleware stop the request from reaching the route?
Yes, if middleware returns a response directly without calling 'call_next', the route is skipped. This is not shown in the example but is important to know.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, what is printed first when a request is processed?
A"After response"
B"Response sent"
C"Before request"
D"Request received"
💡 Hint
Check step 2 in the execution_table where middleware starts processing.
At which step does the route handler generate the response?
AStep 3
BStep 4
CStep 2
DStep 5
💡 Hint
Look for when the response object is created in the execution_table.
If middleware does not call 'call_next', what happens?
AThe middleware returns a response directly, skipping the route
BAn error occurs automatically
CThe request is forwarded to the route
DThe response is delayed but eventually sent
💡 Hint
Refer to key_moments about middleware behavior when skipping call_next.
Concept Snapshot
Custom middleware in FastAPI intercepts requests and responses.
Define a class inheriting BaseHTTPMiddleware.
Override async dispatch(request, call_next).
Call 'await call_next(request)' to continue.
Modify request or response as needed.
Add middleware with app.add_middleware().
Full Transcript
Custom middleware in FastAPI acts like a checkpoint between the client and your route handlers. When a request comes in, the middleware's dispatch method runs first. It can do things before the request reaches your route, like logging or modifying the request. Then it calls 'await call_next(request)' to pass the request to the next handler, usually your route. After the route creates a response, the middleware can again process or change the response before sending it back to the client. This lets you add features like logging, headers, or authentication checks in one place. Remember, if you don't call 'call_next', the request won't reach your route and the middleware must return a response directly. This flow ensures you can control how requests and responses move through your app.

Practice

(1/5)
1. What is the main purpose of creating custom middleware in FastAPI?
easy
A. To handle user authentication only
B. To define database models
C. To create HTML templates
D. To run code before and after each request is processed

Solution

  1. Step 1: Understand middleware role

    Middleware runs code around request processing, before and after the main handler.
  2. Step 2: Identify correct purpose

    Custom middleware is not for database or templates but for request/response handling.
  3. Final Answer:

    To run code before and after each request is processed -> Option D
  4. Quick Check:

    Middleware = pre/post request code [OK]
Hint: Middleware wraps requests to add extra processing [OK]
Common Mistakes:
  • Confusing middleware with database or template code
  • Thinking middleware only handles authentication
  • Believing middleware runs only after requests
2. Which method must be overridden when creating a custom middleware class in FastAPI?
easy
A. dispatch
B. execute
C. process
D. handle_request

Solution

  1. Step 1: Recall FastAPI middleware structure

    Custom middleware classes override the dispatch method to handle requests.
  2. Step 2: Match method names

    Only dispatch is the correct method name; others are invalid in FastAPI middleware.
  3. Final Answer:

    dispatch -> Option A
  4. Quick Check:

    dispatch method overrides middleware behavior [OK]
Hint: dispatch handles request and response in middleware [OK]
Common Mistakes:
  • Using incorrect method names like handle_request
  • Confusing middleware methods with route handlers
  • Forgetting to override dispatch method
3. Given this middleware code snippet, what will be printed when a request is processed?
class LogMiddleware:
    async def dispatch(self, request, call_next):
        print('Before request')
        response = await call_next(request)
        print('After request')
        return response
medium
A. Only Before request printed
B. Before request printed, then After request printed after response
C. Only After request printed
D. No output printed

Solution

  1. Step 1: Analyze dispatch method flow

    Print 'Before request' runs before calling next handler, 'After request' runs after awaiting response.
  2. Step 2: Understand async call_next behavior

    call_next processes the request and returns response; code after await runs after response is ready.
  3. Final Answer:

    Before request printed, then After request printed after response -> Option B
  4. Quick Check:

    Print before and after call_next = B [OK]
Hint: Code before await runs first, after await runs last [OK]
Common Mistakes:
  • Thinking 'After request' prints before response
  • Ignoring async await order
  • Assuming only one print runs
4. Identify the error in this custom middleware code:
class MyMiddleware:
    async def dispatch(self, request):
        response = await call_next(request)
        return response
medium
A. Response must be created manually, not awaited
B. dispatch method should not be async
C. Missing call_next parameter in dispatch method
D. Middleware class must inherit from BaseHTTPMiddleware

Solution

  1. Step 1: Check dispatch method signature

    dispatch must accept both request and call_next parameters to call next handler.
  2. Step 2: Identify missing parameter

    Code lacks call_next parameter, causing runtime error when calling call_next(request).
  3. Final Answer:

    Missing call_next parameter in dispatch method -> Option C
  4. Quick Check:

    dispatch(request, call_next) required [OK]
Hint: dispatch needs call_next argument to forward requests [OK]
Common Mistakes:
  • Omitting call_next parameter
  • Making dispatch synchronous
  • Not inheriting middleware base class (optional but recommended)
5. You want to create a middleware that adds a custom header 'X-Process-Time' showing how long the request took. Which code snippet correctly implements this?
hard
A. class TimerMiddleware: async def dispatch(self, request, call_next): start = time.time() response = await call_next(request) process_time = time.time() - start response.headers['X-Process-Time'] = str(process_time) return response
B. class TimerMiddleware: async def dispatch(self, request): start = time.time() response = await call_next(request) response.headers['X-Process-Time'] = str(time.time() - start) return response
C. class TimerMiddleware: def dispatch(self, request, call_next): start = time.time() response = call_next(request) response.headers['X-Process-Time'] = str(time.time() - start) return response
D. class TimerMiddleware: async def dispatch(self, request, call_next): response = await call_next(request) response.headers['X-Process-Time'] = time.time() return response

Solution

  1. Step 1: Check dispatch method signature and async usage

    dispatch must be async and accept request and call_next parameters.
  2. Step 2: Verify timing and header addition

    Start time before await call_next, calculate duration after, add header as string.
  3. Step 3: Identify correct code snippet

    class TimerMiddleware: async def dispatch(self, request, call_next): start = time.time() response = await call_next(request) process_time = time.time() - start response.headers['X-Process-Time'] = str(process_time) return response correctly implements timing, async, and header addition.
  4. Final Answer:

    The code snippet that correctly adds X-Process-Time header -> Option A
  5. Quick Check:

    Async dispatch with timing and header = A [OK]
Hint: Measure time before and after await call_next, add header [OK]
Common Mistakes:
  • Missing call_next parameter
  • Not awaiting call_next
  • Adding header before timing calculation
  • Using synchronous dispatch method