Search code examples
python-3.xfastapipydantic

how to map http post request to model classes in fast api?


I am creating a fast api application. The basic sample is below. I added a route with a method named test and I want to access the request sent by clients in post request. I have looked at pydantic to create model classes to map incoming request to model classes. but I want to know , where/how does the mapping happen.

say my client posts a request via javascript, as such . to read this request, what do i need to configure. how can i map this request , to a model class and then to my route method. also, i want to verify few things that are passed in the request header in my route method. how do i access both headers and the body (mapped to my model class) . does fastapi with pydantic , maps the request object to our model class or do i have to write that code too?

fetch("https://myapi.example.com/text", {
  method: "POST",
  body: JSON.stringify({
    userId: 1,
    title: "whatever",
    completed: false
  }),
  headers: {
    "Content-type": "application/json; charset=UTF-8"
  }
});

app.py

from fastapi import FastAPI, Request

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello, World!"}

@app.POST("/test")
async def Test(request: Request): 
   ...
     

Solution

  • The easiest way to map your request body is using a pydantic model:

    from fastapi import FastAPI
    from pydantic import BaseModel
    from pydantic import ConfigDict
    from pydantic import Field
    
    
    class Test(BaseModel):
        model_config = ConfigDict(populate_by_name=True)
    
        user_id: int = Field(
            ...,
            alias="userId"
        )
        title: str
        completed: bool
    
    
    app = FastAPI()
    
    
    @app.post("/test")
    async def test(test: Test):
        print(test.user_id)
        return {"message": "Hello, World!"}
    

    As you can see in fastapi docs, if FastAPI sees a pydantic model, it will be interpreted as a request body.

    But if you want to access the raw request body, you can add the Request as a parameter of your route:

    app = FastAPI()
    
    
    @app.post("/test")
    async def test(request: Request):
        my_request = await request.body()
        print(json.loads(my_request))
    
        return {"message": "Hello, World!"}
    
    

    And if you test it, you'll see it is basically the same:

    curl --request POST \
      --url http://localhost:8000/test \
      --header 'Content-Type: application/json' \
      --data '{
        "userId": 1,
        "title": "My Title",
        "completed": false
    }'
    
    
    INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
    {'userId': 1, 'title': 'My Title', 'completed': False}
    INFO:     127.0.0.1:56431 - "POST /test HTTP/1.1" 200 OK
    

    You can read more about it in Using the Request Directly

    To access the request headers, you can use the Request directly or add the header you want as a parameter:

    @app.post("/test")
    async def test(request: Request, x_test: Annotated[str, Header(...)]):
        print(x_test)
        print(request.headers.get("x-test"))
    
        return {"message": "Hello, World!"}