pythondependency-injectionfastapi

How to specify dependencies for the entire router?


class User(BaseModel):
    name: str
    token: str

fake_db = [
    User(name='foo', token='a1'),
    User(name='bar', token='a2')
]

async def get_user_by_token(token: str = Header()):
    for user in fake_db:
        if user.token == token:
            return user
        else:
            raise HTTPException(status_code=401, detail='Invalid token')


@router.get(path='/test_a', summary='Test route A')
async def test_route_a(user: User = Depends(get_user_by_token)):
    return {'name': user.name}


@router.get(path='/test_b', summary='Test route B')
async def test_route_a(user: User = Depends(get_user_by_token)):
    return {'name': user.name}

I would like to avoid code duplication. Is it possible to somehow set the line user: User = Depends(get_user_by_token) for the entire router? At the same time, I need the user object to be available in each method.

It is very important that the openapi says that you need to specify a header with a token for the method.
enter image description here


Solution

  • You can use the dependencies parameter to add global dependencies when creating the router instance:

    router = APIRouter(dependencies=[Depends(get_user_by_token)])
    

    or, when adding the router to the app instance:

    app.include_router(router, dependencies=[Depends(get_user_by_token)])
    

    Please have a look at FastAPI's documentation on Dependencies for more details.

    As for getting the return value of a global dependency, you can't really do that. The way around this issue is to store the returned value to request.state (as described here), which is used to store arbitrary state (see the implementation of State as well). Hence, you could have something like this:

    def get_user_by_token(request: Request, token: str = Header()):
        for user in fake_db:
            if user.token == token:
                request.state.user = user
        # ...
    

    Then, inside your endpoint, you could retrieve the user object using request.state.user, as described in this answer.