pythonapifastapioptional-parameters

How to provide only one of multiple optional parameters in FastAPI?


The API should serve an endpoint where a user can either provide a guid, a path or a code. This is what the url scheme should look like:

api/locations?code={str}&guid={str}&path={str}

The current code is as follows:

@router.get("/api/locations")
def get_functional_locations(
    guid: Optional[str] = None,
    code: Optional[str] = None,
    path: Optional[str] = None,
) -> Union[List[Locations], PlainTextResponse]:
   if guid:
        return ...

    if path:
        return ...

    if code:
        return ...

    return PlainTextResponse(status_code=status.HTTP_400_BAD_REQUEST)

Is there another way to provide this multiple optional parameters and have an XOR that only allows the user to fill in exactly one parameter?


Solution

  • Firstly, I would ask, if you have three parameters that are mutually exclusive and all are ways to get the same information, shouldn't that be three different endpoints, e.g. api/locations/guid/{guid}, api/locations/code/{code} and api/locations/path/{path}? It's usually preferable to have several smaller functions that do one thing.

    If you however do want this, you can always write your own code for it, e.g. using a class as a dependency, here together with a pydantic root validator

    from fastapi import Depends, HttpException
    from pydantic import BaseModel, root_validator
    
    class Params(BaseModel):
        guid: str | None = None
        code: str | None = None
        path: str | None = None
    
        @root_validator
        def validate(cls, values):
            if len([val for val in values.values() if val is not None]) != 1:
                raise HTTPException(400, "Exactly one of guid, code, and path must be provided")
            return values
    
    
    @app.get("/api/locations")
    def foo(params: Params = Depends()):
        ...