FastAPI Interview Questions 21-25 (Templates, Middleware, & Advanced HTTP)

Welcome to the advanced features section! While FastAPI is famous for building JSON APIs, it is a fully capable web framework. It can serve HTML, handle file uploads like a pro, and even stream video or large datasets.

In this lesson, we will look at the tools you need when specific requirements pop up—like when your boss asks for a quick admin dashboard (HTML), or when you need to log every single request time (Middleware), or export a massive CSV report without crashing the server (Streaming).

21. How do you return HTML templates using Jinja2 in FastAPI?

FastAPI doesn't come with a template engine by default (to keep it light), but it supports Jinja2 perfectly via theStarlette library it is built on.

To use it, you typically install jinja2 and set up atemplates directory.

# 1. Install dependencies
# pip install jinja2

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()

# 2. Point to your directory
templates = Jinja2Templates(directory="templates")

@app.get("/items/{id}", response_class=HTMLResponse)
async def read_item(request: Request, id: str):
    # 3. Return the TemplateResponse
    # You MUST pass the 'request' object to the context
    return templates.TemplateResponse(
        "item.html", 
        {"request": request, "id": id}
    )

Why pass the request?
Jinja2 needs the request object to build URLs (e.g., forurl_for in your HTML) and handle other context-aware features.

22. What are Request and Response objects in FastAPI?

Usually, FastAPI handles data magic for you using Pydantic. However, sometimes you need to get your hands dirty and access the raw HTTP data.

  • Request Object: Gives you access to incoming headers, cookies, the client's IP address, and the raw URL.
  • Response Object: Allows you to modify outgoing cookies, headers, and status codes manually before sending them back.
from fastapi import FastAPI, Request, Response

app = FastAPI()

@app.get("/audit/")
def audit_request(request: Request, response: Response):
    # Accessing raw request data
    client_host = request.client.host
    user_agent = request.headers.get("user-agent")
    
    # Modifying the response manually
    response.headers["X-Custom-Audit"] = "Checked"
    response.set_cookie(key="fakesession", value="123")
    
    return {
        "client_ip": client_host, 
        "browser": user_agent
    }

23. How do you define custom middleware in FastAPI?

Middleware is a function that runs before every request is processed and after every response is generated. It wraps your entire application.

Analogy: The Security Check
Think of middleware as the security gate at an airport. Every passenger (Request) must go through it to enter. Every passenger leaving (Response) passes through it again. It can measure how long you stayed or stop you if you look suspicious.

Common uses include logging, processing time headers, and CORS handling.

import time
from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    # --- Code before the request is processed ---
    start_time = time.time()
    
    # This passes the request to the next handler (the actual path operation)
    response = await call_next(request)
    
    # --- Code after the response is generated ---
    process_time = time.time() - start_time
    
    # Add a custom header with the timing
    response.headers["X-Process-Time"] = str(process_time)
    
    return response

24. How do you handle file uploads?

FastAPI allows you to define files to be uploaded by the client usingFile and UploadFile. It is important to know the difference between the two.

Featurebytes (File)UploadFile (Recommended)
MemoryStores file entirely in RAM.Uses a "Spool" (RAM up to limit, then disk).
PerformanceGood for small files only.Best for large files/images/videos.
MetadataNo metadata (just raw bytes).Access to filename, content-type, etc.
from fastapi import FastAPI, UploadFile, File

app = FastAPI()

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
    # 'file' is a file-like object
    
    # We can get metadata
    filename = file.filename
    content_type = file.content_type
    
    # We can read it or save it
    # contents = await file.read() 
    
    return {"filename": filename, "type": content_type}

25. How do you handle streaming responses?

Usually, an API waits until it has all the data before sending a response. But what if you are generating a huge CSV report or streaming a video? You don't want to load 1GB into memory.

FastAPI solves this with StreamingResponse. It takes a Python generator (which yields data chunk by chunk) instead of a static object.

import time
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

# 1. Define a generator function
def fake_video_streamer():
    for i in range(10):
        yield f"Frame {i}
"
        time.sleep(1) # Simulate slow processing

@app.get("/stream")
def main():
    # 2. Return StreamingResponse
    return StreamingResponse(
        fake_video_streamer(), 
        media_type="text/event-stream"
    )

Tip: This is extremely useful for AI applications where you want to stream the text response token-by-token (like ChatGPT does) instead of waiting for the whole answer.

🚀 Deep Dive With AI Scholar