FastAPI Header Dependencies
Introduction
HTTP headers are an essential part of web communication, allowing clients and servers to exchange additional information with their requests and responses. In FastAPI applications, you'll often need to access, validate, or rely on specific headers for various purposes such as authentication, content negotiation, or tracking.
This guide will teach you how to create and use dependencies specifically designed to work with HTTP headers in FastAPI. By the end, you'll understand how to extract header values, apply validation, and build reusable components for your APIs.
Understanding HTTP Headers
Before diving into FastAPI specifics, let's quickly review what HTTP headers are:
HTTP headers are key-value pairs sent in requests and responses that provide additional context about the communication. Common examples include:
Authorization
: Contains credentials for authenticationContent-Type
: Specifies the media type of the resourceUser-Agent
: Identifies the client applicationAccept-Language
: Indicates preferred languages for the response
Basic Header Dependencies in FastAPI
Accessing Headers Directly
FastAPI provides a simple way to access headers using the Header
class from the fastapi
module:
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items/")
async def read_items(user_agent: str = Header(None)):
return {"User-Agent": user_agent}
When you run this code and make a request to /items/
, FastAPI will:
- Extract the
User-Agent
header from the incoming request - Pass it as the
user_agent
parameter to your function - Return it in the response
The output might look like:
{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
}
Converting Header Names
HTTP headers are typically written in "kebab-case" (words separated by hyphens), but Python variables use "snake_case". FastAPI automatically converts between these formats:
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/headers/")
async def read_header(accept_language: str = Header(None)):
return {"Accept-Language": accept_language}
In this example, accept_language
in your code automatically maps to the Accept-Language
header in the HTTP request.
Creating Custom Header Dependencies
Let's build more sophisticated header dependencies for real-world scenarios:
Authentication Header Dependency
A common use case is extracting and validating authentication tokens from headers:
from fastapi import FastAPI, Header, HTTPException, Depends
from typing import Optional
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "special-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
return x_token
@app.get("/items/", dependencies=[Depends(get_token_header)])
async def read_items():
return {"items": "Successfully authenticated!"}
In this example:
- We define a dependency
get_token_header
that requires theX-Token
header - It validates that the token matches our expected value
- If validation fails, it raises an HTTP 400 error
- The endpoint uses this dependency to protect access
Working with Multiple Headers
Dependencies can work with multiple headers simultaneously:
from fastapi import FastAPI, Header, Depends
from typing import Optional, Tuple
app = FastAPI()
async def get_localization_headers(
accept_language: str = Header(None),
x_country: Optional[str] = Header(None)
):
# Default to English/US if headers not provided
language = accept_language or "en"
country = x_country or "US"
return language, country
@app.get("/localized/")
async def get_localized_content(
loc_info: Tuple[str, str] = Depends(get_localization_headers)
):
language, country = loc_info
return {
"language": language,
"country": country,
"message": f"Content localized for {language}/{country}"
}
This dependency extracts and provides both language and country code headers to help localize content.
Optional vs. Required Headers
By default, when you use Header()
without a default value or with ...
, the header is required:
async def required_header(api_key: str = Header(...)):
# Header is required, FastAPI will handle validation
return api_key
async def optional_header(user_agent: Optional[str] = Header(None)):
# Header is optional with None as default
return user_agent or "Unknown User-Agent"
Header Lists
Some HTTP headers can appear multiple times in a request. You can collect all values using a list:
from fastapi import FastAPI, Header
from typing import List
app = FastAPI()
@app.get("/items/")
async def read_items(x_token: List[str] = Header(None)):
return {"X-Token values": x_token}
If the request contains multiple X-Token
headers (or a single comma-separated value), FastAPI will provide them as a list.
Real-World Example: API Version Control
Let's build a practical example using header dependencies for API version control:
from fastapi import FastAPI, Header, HTTPException, Depends
from enum import Enum
from typing import Callable, Optional
app = FastAPI()
class ApiVersion(str, Enum):
V1 = "1.0"
V2 = "2.0"
V3 = "3.0"
def version_dependency(x_api_version: Optional[str] = Header(None)):
if x_api_version is None:
# Default to v1 if no version specified
return ApiVersion.V1
try:
return ApiVersion(x_api_version)
except ValueError:
raise HTTPException(
status_code=400,
detail=f"API version {x_api_version} not supported. Supported versions: {[v.value for v in ApiVersion]}"
)
@app.get("/users/me")
async def get_user(version: ApiVersion = Depends(version_dependency)):
if version == ApiVersion.V1:
return {"version": version, "user": {"id": 123, "name": "John Doe"}}
elif version == ApiVersion.V2:
return {
"version": version,
"user": {
"id": 123,
"name": "John Doe",
"email": "[email protected]" # V2 includes email
}
}
elif version == ApiVersion.V3:
return {
"version": version,
"user": {
"id": 123,
"name": "John Doe",
"email": "[email protected]",
"preferences": {"theme": "dark"} # V3 adds preferences
}
}
In this example:
- We define an API versioning system using a custom header
X-API-Version
- The dependency extracts and validates the version
- Our endpoint behavior changes based on the requested version
Advanced Patterns: Combining Headers with Other Dependencies
You can combine header dependencies with other FastAPI dependency types for more complex scenarios:
from fastapi import FastAPI, Header, Depends, HTTPException
from typing import Optional
app = FastAPI()
# Database dependency
async def get_db():
db = {"users": {"alice": {"role": "admin"}, "bob": {"role": "user"}}}
yield db
# Would close DB connection in a real app
# Authorization header dependency
async def get_current_user(
authorization: str = Header(...),
db = Depends(get_db)
):
# Simple Bearer token extraction (a real app would use JWT or OAuth)
scheme, _, token = authorization.partition(" ")
if scheme.lower() != "bearer":
raise HTTPException(status_code=401, detail="Invalid authentication scheme")
if token not in db["users"]:
raise HTTPException(status_code=401, detail="Invalid token")
return token
# Role-checking dependency that builds on the user dependency
def get_admin_user(username: str = Depends(get_current_user), db = Depends(get_db)):
if db["users"][username]["role"] != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
return username
# API endpoints
@app.get("/users/me")
async def read_user_me(username: str = Depends(get_current_user)):
return {"username": username}
@app.get("/admin/settings")
async def read_admin_settings(admin: str = Depends(get_admin_user)):
return {"admin": admin, "settings": "Admin settings here"}
This example demonstrates a dependency chain where:
- We have a database dependency
- We have a user authentication dependency that requires an
Authorization
header - We have an admin role check that depends on the authenticated user
Performance Considerations
Headers are typically small and fast to process, but there are still some considerations:
- Keep header dependencies lightweight to ensure your API remains responsive
- Consider caching header processing results when possible
- For binary or large data, use request bodies instead of headers
Summary
FastAPI's header dependencies provide a clean, type-safe way to access HTTP headers in your API endpoints. You've learned how to:
- Extract values from HTTP headers using
Header()
- Create reusable header dependencies for validation and processing
- Work with required and optional headers
- Handle header lists and multiple values
- Combine header dependencies with other dependency types
- Implement real-world patterns like authentication and versioning
By leveraging these techniques, you can build more robust APIs that properly interact with HTTP headers for better client-server communication.
Additional Resources
- FastAPI Official Documentation on Dependencies
- Mozilla Developer Network (MDN) HTTP Headers Reference
- RFC 7231 - HTTP/1.1 Semantics: Headers
Exercises
- Create a header dependency that extracts and validates a client ID and secret from custom headers
- Build a caching system using the
If-Modified-Since
andETag
headers - Implement content negotiation by examining the
Accept
header and returning different formats (JSON, XML, etc.) - Create a rate limiting system using custom headers to track API usage
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)