FastAPI Request Cookies
Introduction
Cookies are small pieces of data stored on the client's browser. They're commonly used to store user preferences, session information, or tracking data across multiple requests. In web applications, cookies allow servers to maintain stateful information in an otherwise stateless HTTP protocol.
FastAPI provides simple and powerful tools to work with cookies in your applications. In this guide, we'll explore how to:
- Access cookies from incoming requests
- Validate and process cookie data
- Send cookies to clients
- Work with secure cookies
- Implement practical cookie-based features
Accessing Cookies in FastAPI
Basic Cookie Access
FastAPI makes it straightforward to access cookies sent by the client's browser. You can use the cookies
attribute of the Request
object:
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/cookies")
async def read_cookies(request: Request):
return {"cookies": request.cookies}
When a client sends a request with cookies, this endpoint will return all cookies as a dictionary. For example, if the request includes cookies sessionid=abc123
and preference=dark-mode
, the response would be:
{
"cookies": {
"sessionid": "abc123",
"preference": "dark-mode"
}
}
Using the Cookie Parameter
For more control and validation, FastAPI provides a dedicated Cookie
parameter. This approach is type-safe and supports validation:
from fastapi import FastAPI, Cookie
app = FastAPI()
@app.get("/cookie-param")
async def read_cookie_param(session_id: str = Cookie(None)):
return {"session_id": session_id}
If the client sends a cookie named session_id
, FastAPI will extract its value and pass it to your function. If the cookie doesn't exist, the default value (None
in this example) will be used.
Optional vs Required Cookies
You can make cookies either optional or required:
from fastapi import FastAPI, Cookie
app = FastAPI()
# Optional cookie
@app.get("/optional-cookie")
async def read_optional_cookie(user_id: str | None = Cookie(default=None)):
if user_id:
return {"user_id": user_id}
return {"message": "No user_id cookie found"}
# Required cookie
@app.get("/required-cookie")
async def read_required_cookie(tracking_id: str = Cookie()):
return {"tracking_id": tracking_id}
For required cookies, if the client doesn't send the cookie, FastAPI will automatically return an error response with a status code of 422 (Unprocessable Entity).
Validating Cookie Data
Type Validation
FastAPI validates cookie data based on the type annotation you provide:
from fastapi import FastAPI, Cookie
app = FastAPI()
@app.get("/validate-cookie-type")
async def validate_cookie_type(
user_id: int = Cookie(), # Must be convertible to an integer
max_items: int = Cookie(default=10)
):
return {
"user_id": user_id,
"max_items": max_items
}
Advanced Validation with Pydantic
You can implement more complex validation using Pydantic models:
from fastapi import FastAPI, Cookie, HTTPException
from pydantic import BaseModel, validator
app = FastAPI()
class UserSettings(BaseModel):
theme: str
items_per_page: int
@validator("theme")
def validate_theme(cls, v):
if v not in ["light", "dark", "system"]:
raise ValueError("Theme must be 'light', 'dark', or 'system'")
return v
@app.get("/user-settings")
async def read_settings(
theme: str = Cookie(default="system"),
items_per_page: int = Cookie(default=20)
):
try:
settings = UserSettings(theme=theme, items_per_page=items_per_page)
return settings.dict()
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
Setting Cookies in Responses
FastAPI allows you to set cookies in your responses using the Response
object:
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/set-cookie")
def set_cookie(response: Response):
response.set_cookie(
key="session_id",
value="abc123",
max_age=1800, # 30 minutes in seconds
httponly=True # Cookie cannot be accessed via JavaScript
)
return {"message": "Cookie set successfully"}
Cookie Parameters
When setting cookies, you can customize various parameters:
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/set-detailed-cookie")
def set_detailed_cookie(response: Response):
response.set_cookie(
key="user_preference",
value="dark-mode",
max_age=2592000, # 30 days in seconds
expires=2592000, # Alternative to max_age (but less precise)
path="/", # Cookie is valid for all paths
domain=None, # Current domain
secure=True, # Only sent over HTTPS
httponly=True, # Not accessible via JavaScript
samesite="lax" # Controls when cookies are sent with cross-site requests
)
return {"message": "Preference cookie set successfully"}
Working with Secure Cookies
HttpOnly Cookies
To protect against XSS (Cross-Site Scripting) attacks, use httponly=True
when setting cookies. This prevents JavaScript from accessing the cookie:
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/set-secure-cookie")
def set_secure_cookie(response: Response):
response.set_cookie(
key="auth_token",
value="very-secure-token",
httponly=True
)
return {"message": "Secure cookie set"}
Secure Cookies
For sensitive information, use secure=True
to ensure cookies are only sent over HTTPS connections:
@app.get("/set-https-cookie")
def set_https_cookie(response: Response):
response.set_cookie(
key="sensitive_data",
value="confidential-information",
secure=True,
httponly=True
)
return {"message": "HTTPS-only cookie set"}
SameSite Attribute
The samesite
attribute helps protect against CSRF (Cross-Site Request Forgery) attacks:
@app.get("/set-samesite-cookie")
def set_samesite_cookie(response: Response):
response.set_cookie(
key="csrf_token",
value="token123",
samesite="strict", # Cookie is not sent for cross-site requests
httponly=True
)
return {"message": "SameSite cookie set"}
SameSite options include:
"strict"
: Cookie is only sent for same-site requests"lax"
: Cookie is sent for same-site requests and top-level navigations with safe HTTP methods"none"
: Cookie is sent for all requests (requiressecure=True
)
Deleting Cookies
To delete a cookie, set its expiration to a time in the past:
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/delete-cookie")
def delete_cookie(response: Response):
response.delete_cookie(key="session_id")
return {"message": "Cookie deleted"}
Practical Examples
Remember User Preferences
This example implements a theme preference system using cookies:
from fastapi import FastAPI, Request, Response
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/theme", response_class=HTMLResponse)
async def theme_page(request: Request):
theme = request.cookies.get("theme", "light")
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Theme Preference</title>
<style>
body {{
font-family: Arial, sans-serif;
background-color: {{"#f0f0f0" if theme == "light" else "#333"}};
color: {{"#333" if theme == "light" else "#f0f0f0"}};
transition: all 0.3s ease;
}}
button {{
padding: 10px;
margin: 10px;
cursor: pointer;
}}
</style>
</head>
<body>
<h1>Current Theme: {theme}</h1>
<button onclick="setTheme('light')">Light Theme</button>
<button onclick="setTheme('dark')">Dark Theme</button>
<script>
function setTheme(theme) {{
document.cookie = `theme=${{theme}}; path=/; max-age=31536000`;
location.reload();
}}
</script>
</body>
</html>
"""
return HTMLResponse(content=html_content)
Simple Authentication System
Here's a basic authentication system using cookies:
from fastapi import FastAPI, Response, Request, HTTPException, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets
app = FastAPI()
security = HTTPBasic()
# In a real app, you'd store this in a database
USERS = {
"admin": "password123"
}
def get_current_user(request: Request):
user_token = request.cookies.get("session_token")
if not user_token or user_token != "admin-secure-token":
raise HTTPException(status_code=401, detail="Invalid authentication")
return "admin"
@app.post("/login")
def login(credentials: HTTPBasicCredentials = Depends(security), response: Response = None):
username = credentials.username
password = credentials.password
if username not in USERS or USERS[username] != password:
raise HTTPException(status_code=401, detail="Invalid credentials")
# In a real app, generate a secure random token
token = "admin-secure-token"
response.set_cookie(
key="session_token",
value=token,
httponly=True,
secure=True,
samesite="strict",
max_age=1800 # 30 minutes
)
return {"message": "Successfully logged in"}
@app.get("/profile")
def profile(username: str = Depends(get_current_user)):
return {
"message": f"Hello, {username}!",
"status": "authenticated"
}
@app.post("/logout")
def logout(response: Response):
response.delete_cookie(key="session_token")
return {"message": "Successfully logged out"}
Shopping Cart
A cookie-based shopping cart implementation:
from fastapi import FastAPI, Request, Response
import json
import base64
app = FastAPI()
@app.get("/cart")
async def view_cart(request: Request):
# Get cart from cookie or initialize empty cart
cart_cookie = request.cookies.get("shopping_cart")
if cart_cookie:
# Decode base64 and parse JSON
try:
cart_json = base64.b64decode(cart_cookie).decode('utf-8')
cart = json.loads(cart_json)
except:
cart = {"items": []}
else:
cart = {"items": []}
return cart
@app.post("/cart/add")
async def add_to_cart(request: Request, response: Response, product_id: int, quantity: int = 1):
# Get existing cart or initialize
cart_cookie = request.cookies.get("shopping_cart")
if cart_cookie:
try:
cart_json = base64.b64decode(cart_cookie).decode('utf-8')
cart = json.loads(cart_json)
except:
cart = {"items": []}
else:
cart = {"items": []}
# Add or update product in cart
product_exists = False
for item in cart["items"]:
if item["product_id"] == product_id:
item["quantity"] += quantity
product_exists = True
break
if not product_exists:
cart["items"].append({
"product_id": product_id,
"quantity": quantity
})
# Encode cart as JSON, then as base64
cart_json = json.dumps(cart)
cart_base64 = base64.b64encode(cart_json.encode('utf-8')).decode('utf-8')
# Set the cookie
response.set_cookie(
key="shopping_cart",
value=cart_base64,
max_age=86400, # 24 hours
httponly=True
)
return {"message": "Product added to cart", "cart": cart}
Summary
Cookies in FastAPI provide a way to maintain state between HTTP requests. In this guide, we've covered:
- Accessing cookies from incoming requests using both
Request.cookies
and theCookie
parameter - Validating cookie data with type annotations and Pydantic
- Setting cookies in responses with various security options
- Working with secure cookies to protect against XSS and CSRF attacks
- Deleting cookies when they're no longer needed
- Practical examples including theme preferences, authentication, and shopping carts
Remember that cookies are stored on the client side, so never store sensitive information in cookies without proper encryption. For sensitive data, consider using server-side sessions with only a session identifier stored in cookies.
Additional Resources
Exercises
- Create an endpoint that counts how many times a specific user has visited your site using cookies.
- Implement a language preference system that uses cookies to remember the user's preferred language.
- Create a "remember me" login functionality that sets a long-lived cookie when selected.
- Build a simple A/B testing system that randomly assigns users to test groups and stores their group in a cookie.
- Implement a secure authentication system with proper CSRF protection using cookies.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)