-
First Check
Commit to Help
Example Codehttps://github.com/claeyswo/fastapi_poc/tree/mainDescriptionHello, We're in the process of migrating our entire codebase from Pyramid to FastAPI. Due to some legacy architectural choices we ended up with a lot of endpoints that accept and return slightly different content based on a users roles, sometimes in combination with the state of the db model. This is something we're going to address in the future, but for the time being, I came up with an idea that uses Pydantic discriminators to dynamically determine the request and response schemas. I have a little testapp here. The relevant sections are: Which also renders nicely in the docs.
Now the thing is that I use ContextVar's to inject userinfo into my discriminator. And I found this.
I know there are libraries that make extensive use of them, like starlette-context, but I don't know if I can use that as a reference. And my other team members all have their doubts about using them. So my question is: Should this be avoided? Is it ok? Are there any pitfalls? Operating SystemLinux Operating System DetailsNo response FastAPI Version0.128.7 Pydantic Version2.12.5 Python VersionPython 3.11.11 Additional ContextNo response |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 5 replies
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
This comment was marked as spam.
-
|
Context vars can be difficult to deal with, they can be complex and hard to debug, depending on how you use them. But given you already have some complex code, they are probably right for your use case. Here are the caveats: In FastAPI, you need to set them in an So, you need to set the context in an async function. Now, if the code that gets the data to be set is non-async blocking code (e.g. calling a DB), and if you run it directly on the async function, it will block the event loop, blocking any other operation. So, you will likely need to set the context var in an async function to some container (e.g. a dict) and then in the non-async blocking function that gets the data and sets the actual value (e.g. a user object), you can get the container value from the context var, and set the user inside of that container. This way, the blocking function is never really setting the value in the context var, it's only accessing it (the container), and modifying that container. So, you will probably need something like this: from contextvars import ContextVar
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
# The context var holds a container (dict), not the final value directly
request_context: ContextVar[dict] = ContextVar("request_context")
async def set_context():
"""Async dependency that initializes the context var with a container.
Must be async so the context var is set in the main async task,
not in a thread worker.
"""
ctx: dict = {}
token = request_context.set(ctx)
yield ctx
request_context.reset(token)
def get_user_from_db(ctx: Annotated[dict, Depends(set_context)]) -> dict:
"""Sync dependency, runs in a thread worker.
It receives the container from set_context and mutates it.
It never calls request_context.set(), only reads and mutates the
existing container, so the child-context limitation doesn't apply.
"""
# Simulate blocking DB lookup
user = {"id": 1, "name": "Alice", "role": "admin"}
ctx["user"] = user
return user
@app.get("/")
async def root(user: Annotated[dict, Depends(get_user_from_db)]):
return {
"user": user,
"role_from_ctx": request_context.get().get("user", {}).get("role"),
} |
Beta Was this translation helpful? Give feedback.

Context vars can be difficult to deal with, they can be complex and hard to debug, depending on how you use them. But given you already have some complex code, they are probably right for your use case.
Here are the caveats:
In FastAPI, you need to set them in an
asyncdependency function or path operation, or an async middleware. If the function is non-async, FastAPI will run it on a thread worker. And if the context var is set on a thread worker, that is, let's say, a "child" context, so the value will not be propagated upwards.So, you need to set the context in an async function.
Now, if the code that gets the data to be set is non-async blocking code (e.g. calling a DB), and if you r…