2022年2月5日土曜日

The story of changing Python web framework from Flask to FastAPI.

https://note.com/navitime_tech/n/nc0381517d067

I shared.

NAVITIME_Tech

August 28, 2020




Hi, I'm Kenny. I'm in charge of developing services using public transportation timetables and improving the release flow at NAVITIME Japan.


This time, I'd like to talk about how we introduced FastAPI as a Python web framework.



Table of contents

A Python web framework

A web framework later than Django and Flask

Installing FastAPI

Defining the Routing

Starting the Server

Documentation

Request Parameters

Validation with Pydantic

Summary

Web frameworks in Python

There are two major web frameworks in Python


Django: a full-stack framework

Flask: a micro-framework.

It is said that Django is for large scale development and Flask is for small to medium scale development, but since the server we developed this time was a small server, we used Flask before.


However, even if you use either framework, you will need to use plugins or third party help to use the following functions.


OpenAPI

JSON Schema

GraphQL

WebSocket

Validation using type hints

Asynchronous processing

CORS configuration

Support for working with reverse proxies

Both Django and Flask lack native support for recently introduced server-side technologies and new Python 3 features.


We have been using plugins and third party libraries to compensate for these features, but when they stop working due to destructive changes caused by version upgrades, it took us quite a while to investigate which library was the cause.


This experience makes me want a modern framework.


Web frameworks later than Django and Flask

New Python web frameworks come out every year, and all of them are similar to Flask in design.


When considering the introduction of a new framework, I did some research on the following points.


It should be as simple as Flask.

It satisfies the aforementioned functional requirements.

It has a lot of GitHub stars.

Rich documentation

I checked the number of GitHub stars for Python web frameworks and found the following (as of August 25, 2020). 1.


1. flask: 51,800

2. django: 51,500

3. fastapi: 20,100

4. tornado: 19,400

Django REST Framework: 18,600

Of these, I focused on FastAPI, which came in third.


tiangolo/fastapi

FastAPI framework, high performance, easy to learn, fast to c

github.com

FastAPI is a framework that appeared in the second half of 2018, and is the most recent of the above frameworks. Nonetheless, it has this number of stars, so it's getting a lot of attention.


The biggest difference between FastAPI and other frameworks is not the difference in functionality, but rather the richness of the documentation and the intuitive design.


Even if it provides the same functionality, it is not always easy for developers to immediately understand how to use it. However, I feel that FastAPI is very low cost in the time it takes to achieve what I want to do. I think that the generous support of the Developer Experience has led to its popularity.


For these reasons, I decided to rewrite the server implemented in Flask with FastAPI.


Installing FastAPI

To install FastAPI, do the following


$ pip install fastapi uvicorn

Uvicorn is one of the implementations of ASGI, the spiritual successor of WSGI (Web Server to Web Framework Interface) used in Python server development.


When you develop servers with Flask, you will probably use Gunicorn as a WSGI server, but you can think of Uvicorn as an asynchronous version of Gunicorn.


Let's see how to use FastAPI in comparison with Flask, and install the following so that you can use both Flask and Gunicorn.


$ pip install flask gunicorn

Defining Routing

If we rewrite the routing definition written in Flask in FastAPI, it will look like this


Flask


from flask import Flask


app = Flask(__name__)



@app.route("/hello")

def hello():

   return {"Hello": "World!"}

FastAPI


from fastapi import FastAPI


app = FastAPI()



@app.get("/hello")

def hello():

   return {"Hello": "World!"}

The implementation is quite similar, and the transition to FastAPI was quite easy because of the similarity in the way Flask is written.


Starting the Server

Save the source codes of Flask and FastAPI as flask_app.py and fastapi_app.py, respectively. To start the server, follow the instructions below.


Flask


$ gunicorn flask_app:app

[2020-08-26 15:21:09 +0900] [27823] [INFO] Starting gunicorn 20.0.4

[2020-08-26 15:21:09 +0900] [27823] [INFO] Listening at: http://127.0.0.1:8000 (27823)

[2020-08-26 15:21:09 +0900] [27823] [INFO] Using worker: sync

[2020-08-26 15:21:09 +0900] [27826] [INFO] Booting worker with pid: 27826

FastAPI


$ uvicorn fastapi_app:app

INFO: Started server process [14366].

INFO: Waiting for application startup.

INFO: Application startup complete.

INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

The startup method is very similar. By the way, FastAPI can also be connected to WSGI, so you can start it with Gunicorn.


Documentation

This is where FastAPI really comes into its own, as it can automatically generate documentation in both Swagger UI and ReDoc styles.


After starting the FastAPI server, you can access the following URLs to see the beautiful documentation pages.


Swagger UI: http://localhost:8000/docs


Enlarge image 1


ReDoc: http://localhost:8000/redoc


Enlarge image 2


Flask does not have a feature to automatically generate documentation. To be honest, I think it's worth installing FastAPI just to use this feature.


Request Parameters

I'm sure there are many people who have trouble understanding how to get the request parameters when using Flask. I am one of them.


Let's say you have an API like the following


POST /users/{name}?age={age}

{

    "country": {country}

}

Each parameter is specified in a different part of the request.


{name} : path parameter

{age} : Query parameter

{country} : Body

FastAPI is a very intuitive way to solve this problem.


Let's take a look at how to write it (error checking is omitted).


Flask


from flask import Flask, request


app = Flask(__name__)



@app.route("/users/<name>", methods=["POST"])

def create_user(name):

   age = request.args.get("age", type=int)

   body = request.json


   return {

       "age": age,

       "name": name,

       "country": body["country"],

   }

FastAPI


from fastapi import FastAPI, Query, Body


app = FastAPI()



@app.post("/users/{name}")

def create_user(name: str, age: int = Query(None), body: dict = Body(None)):

   return {

       "age": age,

       "name": name,

       "country": body["country"],

   }

In the case of FastAPI, the path parameter, query parameter, and body can all be defined as function arguments.


Also, FastAPI is more intuitive and easier to understand because it uses the type hints introduced in Python 3 to specify the parameter types.


I remember spending a lot of time trying to figure out how to receive query parameters and bodies in Flask. It seems to me that the Developer Experience is greatly affected by the design that makes it easy to find the way to implement these commonly used functions.


Validation with Pydantic

FastAPI ships with a type validation library called Pydantic, which can be used to validate the types of request and response parameters.


Let's try to validate the response to the aforementioned API. The same method can be used to validate the request.


First of all, we need to import the following


from pydantic import BaseModel, Field

Next, define the schema class to validate the JSON returned in the response.


class User(BaseModel):

   age: int = Field(description="age", ge=0)

   name: str = Field(description="Name")

   country: str = Field(description="Country of origin")

By using this class, you can validate that the response JSON has "age", "name", and "country" fields, and that "age" is an integer greater than or equal to 0.


We will specify this class with a decorator


@app.post("/users/{name}", response_model=User)

In this way, an error will be returned if you try to return a response that does not match the User schema.


The entire source code looks like this


from fastapi import FastAPI, Query, Body

from pydantic import BaseModel, Field


app = FastAPI()



class User(BaseModel):

   age: int = Field(description="age", ge=0)

   name: str = Field(description="Name")

   country: str = Field(description="Country of origin")



@app.post("/users/{name}", response_model=User)

def create_user(name: str, age: int = Query(None), body: dict = Body(None)):

   return {

       "age": age,

       "name": name,

       "country": body["country"],

   }

The schema description will be automatically reflected in the documentation as well.


Enlarge screenshot 2020-08-26 17.17.27


Summary

Flask is said to be a micro-framework, but FastAPI has a well-balanced design that seems to redefine the micro-framework to meet recent needs.


There are many more attractive features of FastAPI. If you want to know more about other features, please check out the official documentation.


FastAPI Documentation


#python

#NaviTime Japan

#web framework


Conskie@Conskie


NAVITIME_Tech


Follow us

Follow us on Twitter and Facebook to get the latest news about NAVITIME technology and our service. If you have any questions or comments, please visit our official website (https://www.navitime.co.jp/).

0 コメント:

コメントを投稿