Reputation: 69
I'm trying to create an API using FastAPI + MongoDB that can return multiple values from the request. The MongoDB is full of data and using mongoengine I can consult one or all data when sending a request to the specific endpoint. What I'm trying to do now is receive one or more data from an endpoint, for example:
When consulting the endpoint http://127.0.0.1:8000/rice
I get a JSON response with all the data from this item that is on MongoDB. But the problem is that I need to have this endpoint consulting one or more data from MongoDB and return as much data as the user sends to the endpoint, for example: http://127.0.0.1:8000/rice&pasta&bean
and return the JSON with the information that it is in the MongoDB about rice
, pasta
and bean
.
In the code I have a main.py with the routes like:
@app.get('/{description}', status_code=200)
def get_description(description):
return JSONResponse(TabelaService().get_description(description))
This function calls another function that call another function that use queryset
to consult the data from MongoDB and serialize it:
def get_description(self, description):
try:
description = TabelaNutricional.get_by_description(description)
return self.serialize(description)
except:
raise DescriptionNotFound
And bellow is the function who get the data from MongoDB:
@queryset_manager
def get_by_description(doc_cls, queryset, description):
nutriente = queryset(description=str(description)).get()
return nutriente
Does anyone has a clue to how to get more data in the endpoint? Thank you!
Upvotes: 1
Views: 5733
Reputation: 34551
List
of Query
parametersYou can declare a query parameter with a type of List
and explicitly use Query
, as demonstrated in this and this answer, and as explained in the documentation. In this way, you can receive multiple values for the query parameter, for instance:
http://127.0.0.1:8000/?items=rice&items=pasta&items=bean
On server side, you can loop through the list of items and call your function for each item in the list, and create a dictionary with the results to send back to the client (see related answer). For instance:
from fastapi import Query
from typing import List
@app.get('/', status_code=200)
def get_description(items: List[str] = Query(...)):
data = {}
for i in items:
d = TabelaService().get_description(i)
data[i] = d
return JSONResponse(data)
List
of Form
parametersYou could send a list of Form
parameters in the request body instead. Have a look at related answers here and here.
from fastapi import Form
from typing import List
@app.post('/')
async def submit(items: List[str] = Form(...)):
data = {}
for i in items:
# ...
return JSONResponse(data)
requests
import requests
url = 'http://127.0.0.1:8000/'
data = {'items': ['foo', 'bar']}
r = requests.post(url, data=data)
print(r.text)
List
of Body
parameters / JSON dataYou could alternatively send the data encoded as application/json
data in the request body. Since when defining a parameter with List
in FastAPI endpoint, and that parameter is not a path parameter, it is automatically considered as a body parameter, i.e., expecting JSON data. If it wasn't defined with List
, you would need to use the Body
type or one of the other options demonstrated in this answer to defined that parameter as a JSON body parameter.
from typing import List
@app.post('/')
async def submit(items: List[str]):
data = {}
for i in items:
# ...
return JSONResponse(data)
requests
import requests
url = 'http://127.0.0.1:8000/'
payload = ['foo', 'bar']
r = requests.post(url, json=payload)
print(r.text)
In case you would like passing the list of data using the items
key on client side, you should use the special Body
parameter embed. This is required, in case you only had a single body parameter defined in the endpoint. Example:
from fastapi import Body
@app.post('/')
async def submit(items: List[str] = Body(..., embed=True)):
pass
requests
# ...
payload ={'items': ['foo', 'bar']}
r = requests.post(url, json=payload)
If, however, you had more than one body parameters defiend in the endpoint, FastAPI would automatically expect you to post the JSON body parameters using each of the key names defined in the endpoint. Example:
@app.post('/')
async def submit(items: List[str], colours: List[str]):
pass
requests
# ...
payload ={'items': ['foo', 'bar'], 'colours': ['white', 'blue']}
r = requests.post(url, json=payload)
If you still want to use a Path parameter instead, you can use the below and call it as shown in your question i.e., http://127.0.0.1:8000/rice&pasta&bean
. See this related answer, which explains how to have /
characters in that single path parameter as well, if required.
@app.get('/{items}', status_code=200)
def get_description(items: str):
items = items.split('&')
data = {}
for i in items:
d = TabelaService().get_description(i)
data[i] = d
return JSONResponse(data)
Note: If you used the above option (i.e., using a path parameter), the Swagger UI autodocs (that can be accessed at http://127.0.0.1:8000/docs) wouldn't load. That's because when accessing that URL, the endpoint above is called, and takes docs
as the value for the items
path parameter (see related answers here, as well as here and here). Thus, you can either add an extra path on that endpoint, e.g., @app.get('/api/{items}'
and call it using, for instance, http://127.0.0.1:8000/api/rice&pasta&bean
—.thus, allowing /docs
to load successfully—or simply use one of the remaining approaches demonstrated above.
Upvotes: 1