FastAPI
FastAPI is one of the next generation python web framework that uses ASGI (asynchronous server gateway interface) instead of the traditional WSGI. It also includes a number of useful functions to make API creations easier.
Uvicorn
FastAPI uses Uvicorn as its ASGI. We can configure its settings as described here. We can also specify it in the fastapi python app script, or at the terminal when we launch uvicorn.
For the former, with the below specification, we can just execute python app.py to start the application.
# app.py
from fastapi import FastAPI
import uvicorn
app = FastAPI()
if __name__ == "__main__":
uvicorn.run('app:app', host='0.0.0.0', port=5000)
If we run from the terminal, with the app residing in example.py.
uvicorn example:app --host='0.0.0.0' --port=5000
The documentation recommends that we use gunicorn which have richer features to better control over the workers processes.
gunicorn app:app --bind 0.0.0.0:5000 -w 1 --log-level debug -k uvicorn.workers.UvicornWorker
Request-Response Schema
FastAPI uses the pydantic library to define the schema of the request & response APIs. This allows the auto generation in the OpenAPI documentations, and for the former, for validating the schema when a request is received.
For example, given the json:
{
"boundingPoly": {
"normalizedVertices": [
{
"x": 0.406767,
"y": 0.874573,
"width": 0.357321,
"height": 0.452179,
"score": 0.972167
},
{
"x": 0.56781,
"y": 0.874173,
"width": 0.457373,
"height": 0.452121,
"score": 0.982109
}
]
},
"name": "Cat"
}
We can define in pydantic as below, using multiple basemodels for each level in the JSON.
- If there are no values input like
y: float
, it will listed as a required field - If we add a value like
y: float = 0.8369
, it will be an optional field, with the value also listed as a default and example value - If we add a value like
x: float = Field(..., example=0.82379)
, it will be a required field, and also listed as an example value
More attributes can be added in Field()
, that will be populated in OpenAPI docs.
class _normalizedVertices(BaseModel):
x: float = Field(..., example=0.82379, description="X-coordinates"))
y: float = 0.8369
width: float
height: float
score: float
class _boundingPoly(BaseModel):
normalizedVertices: List[_normalizedVertices]
class ResponseSchema(BaseModel):
boundingPoly: _boundingPoly
name: str = "Human"
We do the same for the request schema and place them in the routing function.
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import List
import json
import base64
import numpy as np
@app.post('/api', response_model = ResponseSchema)
async def human_detection(request: RequestSchema):
JScontent = json.loads(request.json())
encodedImage = JScontent['requests'][0]['image']['content']
npArr = np.fromstring(base64.b64decode(encodedImage), np.uint8)
imgArr = cv2.imdecode(npArr, cv2.IMREAD_ANYCOLOR)
pred_output = model(imgArr)
return pred_output
Open-API
OpenAPI documentations of Swagger UI or Redoc are automatically generated. You can access it at the endpoints of /docs
and /redoc
.
First, the title, description and versions can be specified from the initialisation of fastapi. The request-response pydantic schema and examples will be added after its inclusion in a POST/GET request routing function.
from fastapi import FastAPI
app = FastAPI(title="Human Detection API",
description="Submit Image to Return Detected Humans in Bounding Boxes",
version="1.0.0")
@app.post('/api', response_model= RESPONSE_SCHEMA)
def human_detection(request: REQUEST_SCHEMA):
do something
return another_thing