The fact that both endpoints return the same resource (they both return a user) is an indicator that maybe they should just be 1 endpoint that returns a user specified by an ID, or the current logged-in one by default.
While you can differentiate based on how the endpoints are defined (as given by the other answer), I find that solution to be "brittle", in the sense that you (and other developers maintaining and reading your code) would have to always remember that the order matters, which can easily cause bugs when you (or someone else) reorganize or refactor the code.
So, as an alternative, you can just combine those 2 endpoints into 1 get_user endpoint:
from typing import Optional
from fastapi import Query
from pydantic import BaseModel, NonNegativeInt
class User(BaseModel):
user_id: NonNegativeInt
other_field: str = ""
@classmethod
async def find_one(cls, user_id: NonNegativeInt) -> "User":
"""This is a fake method that returns a User from somewhere"""
return cls(user_id=user_id, other_field=str(user_id) * 5)
def get_current_active_user() -> User:
"""This is a fake method that somehow gets the logged-in user"""
return User(user_id=0, other_field="This is the logged-in user")
@router.get("/user", response_model=User)
async def get_user(user_id: Optional[NonNegativeInt] = Query(default=None)):
if user_id:
user = await User.find_one(user_id)
else:
user = get_current_active_user()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
$ curl -XGET localhost:8000/user
{"user_id":0,"other_field":"This is the logged-in user"}
$ curl -XGET localhost:8000/user?user_id=444
{"user_id":444,"other_field":"This is a different user"}
The advantages of this are, for one, the order doesn't anymore matter, and two, the same set of validations and parameters on the request and the response can be done on the same endpoint function.
Now for the case of having 2 different response models:
... the first one returns all the data for a specific user and the second one only the data of the current (logged in) user (with the schema UserOut, which has less fields)
I don't know the use-case why the response for the logged-in user needs to have less fields, but, check out the section on defining multiple models from the FastAPI tutorials. Basically, you would want a base user model that has the minimum fields (to represent your logged-in user) and a more specific user model with all the other fields to represent the "found" users) Then define your route to be a Union of these 2 different types.
class BaseUser(BaseModel):
user_id: NonNegativeInt
class LoggedInUser(BaseUser):
pass
class SomeOtherUser(BaseUser):
other_field: str
@classmethod
async def find_one(cls, user_id: NonNegativeInt) -> "SomeOtherUser":
"""This is a fake method that returns a User from somewhere"""
return cls(user_id=user_id, other_field="This is a different user")
def get_current_active_user() -> LoggedInUser:
"""This is a fake method that somehow gets the logged-in user"""
return LoggedInUser(user_id=0)
@router.get("/user", response_model=Union[SomeOtherUser, LoggedInUser])
async def get_user(user_id: Optional[NonNegativeInt] = Query(default=None)):
if user_id:
user = await SomeOtherUser.find_one(user_id)
else:
user = get_current_active_user()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
$ curl -XGET localhost:8000/user
{"user_id":0}
$ curl -XGET localhost:8000/user?user_id=444
{"user_id":444,"other_field":"This is a different user"}