Python has always been a popular choice for developing lightweight web apps, thanks to awesome frameworks like Flask, Django, Falcon, and many others. Due to Python's position as the number one language for machine learning, it is particularly convenient for packaging models and exposing them as a service.
For many years, Flask was the number one tool for the job, but there is a new challenger in town, in case you haven't heard. FastAPI is a relatively new web framework for Python, taking inspiration from its predecessors, perfecting them, and fixing many of their flaws. Built on top of Starlette, it brings a ton of awesome features to the table.
It has gained significant traction recently, and after spending the last year working with it every day, I can confidently say that the hype is justified. If you haven't tried it yet, I would like to give you five reasons to give it a shot.
1. Simple yet brilliant interface
All web frameworks need to balance between functionality and giving freedom to the developer. Django is powerful yet very opinionated. On the other hand, Flask is low-level enough to provide a large degree of freedom, but a lot is left for the user to do. FastAPI is more on the Flask side of the spectrum, but it strikes a healthier balance.
To give you an example, let's see how an endpoint is defined in FastAPI.
from fastapi import FastAPI
from pydantic import BaseModel
class User(BaseModel):
email: str
password: str
app = FastAPI()
@app.post("/login")
def login(user: User):
# ...
# do some magic
# ...
return {"msg": "login successful"}
For defining the schema, it uses Pydantic, which is another excellent Python library used for data validation. This is simple to do here, yet so much is happening in the background. The responsibility to validate the input is delegated to FastAPI. If the request is incorrect, for instance, the email
field contains an int, a FastAPI app will return an appropriate error code instead of breaking down with the dreaded Internal Server Error (500). And it is practically free.
We can serve this simple example app with uvicorn:
uvicorn main:app
Now the app is ready to accept requests. In this case, a request would look like
curl -X POST "http://localhost:8000/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"email\":\"string\",\"password\":\"string\"}"
The icing on the cake is that it automatically generates the documentation according to the OpenAPI using the interactive Swagger UI.
Swagger UI for the FastAPI app
Async
One of the biggest disadvantages of Python WSGI web frameworks compared to those in Node.js or Go was the inability to handle requests asynchronously. Since the introduction of ASGI, this is no longer an issue, and FastAPI is taking full advantage of this. All you have to do is simply declare the endpoints with the async
keyword like this:
@app.post("/")
async def endpoint():
# ...
# call async functions here with `await`
# ...
return {"msg": "FastAPI is awesome!"}
Dependency injections
FastAPI has a really cool way to manage dependencies. Although it is not forced on the developer, it is strongly encouraged to use the built-in injection system to handle dependencies in your endpoints.
To give an example, let's write an endpoint where users can post comments for certain articles.
from fastapi import FastAPI, Depends
from pydantic import BaseModel
class Comment(BaseModel):
username: str
content: str
app = FastAPI()
database = {
"articles": {
1: {
"title": "Top 3 Reasons to Start Using FastAPI Now",
"comments": []
}
}
}
def get_database():
return database
@app.post("/articles/{article_id}/comments")
def post_comment(article_id: int, comment: Comment, database = Depends(get_database)):
database["articles"][article_id]["comments"].append(comment)
return {"msg": "comment posted!"}
FastAPI automatically evaluates the get_database
function at runtime when the endpoint is called, so you can use the return value as you wish. There are (at least) two good reasons for this.
- You can override the dependencies globally by modifying the
app.dependency_overrides
dictionary. This can make testing a breeze since you can mock objects quickly. - The dependency (the
get_database
in our case) can perform more sophisticated checks, allowing you to separate them from business logic. This greatly simplifies things. For instance, user authentication can be easily implemented with this technique.
Easy integration with databases
SQL, MongoDB, Redis, or whatever you choose, FastAPI doesn't force your hand to build your application around it. If you have ever tried to work with MongoDB using Django, you know how painful it can be. With FastAPI, you don't need to go the extra mile; adding a database to your stack is as simple as possible. (Or, to be more precise, the amount of work to be done will be determined by the database you choose, not by the complications added by the web framework.)
But really, look at this beauty.
from fastapi import FastAPI, Depends
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine("sqlite:///./database.db")
Session = sessionmaker(bind=engine)
def get_db():
return Session()
app = FastAPI()
@app.get("/")
def an_endpoint_using_sql(db = Depends(get_db)):
# ...
# do some SQLAlchemy
# ...
return {"msg": "an exceptionally successful operation!"}
Voila! I can see you typing
pip install fastapi
into your terminal already.
GraphQL support
When you are working with a complex data model, REST can be a severe hindrance. It is definitely not fun when a tiny change in the frontend requires updating the schema for an endpoint. GraphQL shines in these situations. Although GraphQL support is not unique among Python web frameworks, Graphene and FastAPI work together seamlessly. No need to install any extensions like graphene_django
for Django. It just works natively.
+1: Great documentation
Of course, a great framework cannot truly shine without an equally great documentation. Django, Flask, and all the others excel in this aspect, but FastAPI is on par with them. Of course, since it is much younger, there are no books written about it yet, but it is just a matter of time.
To summarize, whether you are looking for a fast and lightweight framework to serve your deep learning models or something more complex, FastAPI delivers. If you haven't tried it yet, I strongly encourage you to do so. I am pretty sure you'll stick with it.