Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read a body JSON list with FastAPI

The body of an HTTP PUT request is a JSON list - like this:

[item1, item2, item3, ...]

I can't change this. (If the root was a JSON object rather than a list there would be no problem.)

Using FastAPI I seem to be unable to access this content in the normal way:

@router.put('/data')
def set_data(data: DataModel): # This doesn't work; how do I even declare DataModel?

I found the following workaround, which seems like a very ugly hack:

class DataModel(BaseModel):
    __root__: List[str]


from fastAPI import Request

@router.put('/data')
async def set_data(request: Request): # Get the request object directly
    data = DataModel(__root__=await request.json())

This surely can't be the 'approved' way to achieve this. I've scoured the documentation both of FastAPI and pydantic. What am I missing?

like image 303
Ian Goldby Avatar asked Mar 25 '20 08:03

Ian Goldby


People also ask

Can you use JSON body with GET request?

Not only does the HTTP spec allow body data with GET request, but this is also common practice: The popular ElasticSearch engine's _search API recommends GET requests with the query attached in a JSON body. As a concession to incomplete HTTP client implementations, it also allows POST requests here.

Can we use @requestBody with get?

Yes. In other words, any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however, are restricted such that a body, if any, has no semantic meaning to the request.

How to use fastapi request body?

In this post, we will learn how to use FastAPI Request Body. We will use Pydantic BaseModel class to create our own class that will act as a request body. When we need to send some data from client to API, we send it as a request body. In other words, a request body is data sent by client to server.

How does fastapi work in Python?

In FastAPI, you derive from BaseModel to describe the data models you send and receive (i.e. FastAPI also parses for you from a body and translates to Python objects). Also, it relies on the modeling and processing from pydantic.

How does fastapi interpret pydantic model parameters?

If the parameter is declared to be of the type of a Pydantic model, it will be interpreted as a request body. FastAPI will know that the value of q is not required because of the default value = None.

How can I declare a deeply nested object in fastapi?

So, you can declare deeply nested JSON "objects" with specific attribute names, types and validations. All that, arbitrarily nested. For example, we can define an Image model: And then we can use it as the type of an attribute: This would mean that FastAPI would expect a body similar to: Again, doing just that declaration, with FastAPI you get:


1 Answers

Descending from the model perspective to primitives

In FastAPI, you derive from BaseModel to describe the data models you send and receive (i.e. FastAPI also parses for you from a body and translates to Python objects). Also, it relies on the modeling and processing from pydantic.

from typing import List
from pydantic import BaseModel

class Item(BaseModel):
    name: str

class ItemList(BaseModel):
    items: List[Item]

def process_item_list(items: ItemList):
    pass

This example would be able to parse JSON like

{"items": [{"name": "John"}, {"name": "Mary"}]}

In your case - depending on what shape your list entries have - you'd also go for proper type modeling, but you want to directly receive and process the list without the JSON dict wrapper around it. You could go for:

from typing import List
from pydantic import BaseModel

class Item(BaseModel):
    name: str

def process_item_list(items: List[Item]):
    pass

Which is now able to process JSON like:

[{"name": "John"}, {"name": "Mary"}]

This is probably what you're looking for and the last adaption to take is depending on the shape of your item* in the list you receive. If it's plain strings, you can also go for:

from typing import List

def process_item_list(items: List[str]):
    pass

Which could process JSON like

["John", "Mary"]

I outlined the path from models down to primitives in lists because I think it's worth knowing where this can go if one needs more complexity in the data models.

like image 166
jbndlr Avatar answered Oct 01 '22 09:10

jbndlr