FastAPI Seems Good, so Why Don't We Build Something Similar For Flask?

FastAPI Seems Good, so I Built APIFlask 🍯





Grey Li @ PyCon 2021

slides available at greyli.com/slides/pycon2021

"FastAPI has Ruined Flask Forever for Me"

when I search "FastAPI Flask" on Google...
and more on Google

You Shouldn't Compare FastAPI to Flask

but why?

Starlette 🍊 -> FastAPI 🍹

Flask 🍎 -> [ ❓ ]

copyright reddit/flask @elephantail

What Should I Compare FastAPI To?

what's the apple juice in Flask world?

Flask API Extensions

  • Flask-RESTful
  • Flask-RESTPlus
  • Flask-RESTX
  • Flask-RESTy
  • Flask-Rebar
  • flask-smorest
  • flask-apispec
  • ...

API Frameworks Based on Flask

  • Eve
  • Connexion
  • Flask-API
  • APIFlask

APIFlask is the [Apple Juice]

in the honey pot

picture 4

⚑ FastAPI = Starlette + Pydantic

🍯 APIFlask = Flask + Marshmallow

Main Features

  • Automatic request validation and deserialization
  • Automatic response formatting and serialization
  • Automatic OpenAPI spec/documentations generation
  • Automatic interactive API documentation
find more at apiflask.com

Minimal Application

$ pip install apiflask
from apiflask import APIFlask

app = APIFlask(__name__)


@app.get('/')
def say_hello():
    return {'message': 'Hello!'}

Wait, What's the Difference? πŸ€”

Key Differences

  • When creating an application instance, use APIFlask instead of Flask
  • When creating a blueprint instance, use APIBlueprint instead of Blueprint
  • The abort() function from APIFlask (apiflask.abort) returns JSON error response
  • The view class should be registered with the route decorator
see details at Migrating from Flask
from apiflask import Schema
from apiflask.fields import Integer, String
from apiflask.validators import Length, OneOf


class PetInSchema(Schema):
    name = String(required=True, validate=Length(0, 10))
    category = String(required=True, validate=OneOf(['dog', 'cat']))


class PetOutSchema(Schema):
    id = Integer()
    name = String()
    category = String()
from apiflask import APIFlask, input, output
from flask_sqlalchemy import SQLAlchemy

from app.models import Pet
from app.schemas import PetInSchema, PetOutSchema

app = APIFlask(__name__)
db = SQLAlchemy(app)


@app.get('/pets/<int:pet_id>')
@output(PetOutSchema)  # <-- HIGHLIGHT THIS LINE
def get_pet(pet_id):
    return Pet.query.get_or_404(pet_id)


@app.patch('/pets/<int:pet_id>')
@input(PetInSchema(partial=True))  # <-- HIGHLIGHT THIS LINE
@output(PetOutSchema)  # <-- AND THIS LINE
def update_pet(pet_id, data):
    pet = Pet.query.get_or_404(pet_id)
    for attr, value in data.items():
        setattr(pet, attr, value)
    db.session.commit()
    return pet

And More

  • Authentication
  • Pagination helpers
  • Class-based view support
  • Automatic JSON response for HTTP errors

PR is Welcome

suggestions/feedback -> issues/1

Credit

Grey Li

  • Maintainer of Flask
  • Author of APIFlask
  • Technical Writer
  • GitHub @greyli
  • Twitter @greylihui
find more on greyli.com