Movatterモバイル変換


[0]ホーム

URL:


ChristopherGS
FastAPI course
The course: "FastAPI for Busy Engineers" is available if you prefer videos.

FastAPI vs Flask - The Complete Guide

Understand why FastAPI is taking the Python community by storm
Created: 16 June 2021
Last updated: 30 June 2021
Subscribe

I publish about developments in AI Engineering from time to time.Plus a free 10-page report on ML system best practices. No spam.

Introduction

More and more people are getting onboard the FastAPI train. Why is this? And what is it about this particular web framework that makes it worth switching away from your tried-and-tested Flask APIs?

This post compares and discusses code from an example Flask and FastAPI project. The sample projectis a JSON web token (JWT) auth API.Here is the full source code.

I’m willing to concede that a better title for this post would be “why use FastAPI instead of Flask”.

Contents

1.FastAPI’s Performance
2.HowFastAPI reduces your errors with Python type declarations and Pydantic
3.FastAPI’s elegant dependency injection
4.Automatic Documentation via Standards
5.TheFastAPI toolbox

FastAPI Github stars


1. FastAPI’s Performance

FastAPI’s name may lack subtlety, but it does what it says on the tin. With FastAPI, you getthe sort of high-performance you would expect from traditionally faster languages like NodeJSor Go.

FastAPI benchmarksNaturally, benchmarks should be taken with a pinch of salt, have alook at the source of these

How is this possible in slow old Python? Under the hood, FastAPI is leveraging Python’sasyncio library,which was added in Python 3.4 and allows you to write concurrent code. Asyncio is a great fit forIO-bound network code (which is most APIs), where you have to wait for something, for example:

  • Fetching data from other APIs
  • Receiving data over a network (e.g. from a client browser)
  • Querying a database
  • Reading the contents of a file

FastAPI is built on top ofStarlette, anASGIframework created byTom Christie (he is a Python community powerhouse who also created theDjango REST Framework).

In practice, this means declaring coroutine functions with theasync keyword, and using theawait keyword with any IO-bound parts of the code. In this regard, Flask (as of v2.x) and FastAPI are identical.(Both frameworks use decorators to mark endpoints):

Flask:

@app.route("/get-data")asyncdefget_data():data=awaitasync_db_query(...)returnjsonify(data)

FastAPI:

@app.get('/')asyncdefread_results():results=awaitsome_library()returnresults

However, Flask is fundamentally constrained in that it is a WSGI application. So whilst in newerversions of Flask (2.x) you can get a performance boost bymaking use of an event loopwithin path operations, your Flask server will still tie up a worker for each request.

FastAPI on the other hand implements the ASGI specification.ASGI is a standard interface positioned as a spiritual successor to WSGI.It enables interoperability within the whole Python async web stack:servers, applications, middleware, and individual components. See theawesome-asgigithub repo for some of these resources.

With FastAPI, your application will behave in a non-blocking way throughout the stack, concurrency applies at the request/response level. This leads to significant performance improvements.

Furthermore, ASGI servers and frameworks also give you access to inherently concurrent features (WebSockets, Server-Sent Events, HTTP/2) that are impossible (or at least requireworkarounds) to implement using sync/WSGI. You do need to use FastAPI together with an ASGIweb server -uvicorn is the recommended choice, although it’s possible to swap it out for alternativeslikeHypercorn

uvicorn logo

2. HowFastAPI reduces your errors with type declarations and Pydantic schemas

In FastAPI, a combination of Python type hints and Pydantic models define your endpointexpected data schemas (both inputs and outputs).

A simple example looks like this:

@api.get('/api/add')defcalculate(x:int,y:int):value=x+yreturn{'x':x,'y':y,'value':value}

We define the endpoint parameters (x andy) and their types asint and FastAPI will performvalidation on these values purely based on the Python types.

Pydantic is a library for data validation, it takes the idea of data classes a step further.FastAPI bakes Pydantic deeply into its ethos, using it for:

  • Defining API config
  • Defining API requests/responses (JSON schemas)

For example:

@api_router.get("/",response_model=schemas.Msg,status_code=200)defroot()->dict:return{"msg":"This is the Example API"}

Where theschemas.Msg looks like this:

frompydanticimportBaseModelclassMsg(BaseModel):msg:str

Obviously this is a simple example, but now we have a very clear schema for our response format.For complex responses (or requests), we can define large Pydantic classes (and fields can benested Pydantic models so the complexity can really ratchet up). This is great for things likemachine learning APIs where you might have complex inputs to your API -here’s an example

FastAPI also uses Pydantic classes for defining app config:

classLoggingSettings(BaseSettings):LOGGING_LEVEL:int=logging.INFO# logging levels are intsclassDBSettings(BaseSettings):SQLALCHEMY_DATABASE_URI:strclassSettings(BaseSettings):# 60 minutes * 24 hours * 8 days = 8 daysACCESS_TOKEN_EXPIRE_MINUTES:int=60*24*8# Metalogging:LoggingSettings=LoggingSettings()db:SQLLiteSettings=DBSettings()

I personally like FastAPI’s opinionated config approach. Flask let’s you do pretty much anything:

  • Define config in Python classes
  • Parse a file, e.g. yaml, JSON, toml

Meaning you’ll often see:

classBaseConfig:"""Base configuration."""SECRET_KEY=os.getenv('SECRET_KEY','my_precious')DEBUG=FalseBCRYPT_LOG_ROUNDS=13SQLALCHEMY_TRACK_MODIFICATIONS=FalseclassDevelopmentConfig(BaseConfig):"""Development configuration."""DEBUG=TrueBCRYPT_LOG_ROUNDS=4SQLALCHEMY_DATABASE_URI=postgres_local_base+database_name

In your other Python API projects you may have found yourself making use of serialization Python libraries likeMarshmallow, or digging intothe docs ofDjango REST Framework serializers.

With FastAPI, there’s no need for this. Pydantic does the job and is super intuitive (there’s no new syntax to learn).This all results in faster development speed.


3.FastAPI’s Elegant dependency injection

DI meme

What I love the most about FastAPI is its dependency injection mechanism. Dependency injectionis a fancy way of saying your code has certain requirements to work. FastAPI allows you todo this at the level ofpath operation functions, i.e. your API routes.

This is an area where Flask is very weak. With Flask, you will often find yourself exporting globals,or hanging values onflask.g (which is just another global). Let’s compare the case of accessingthe database in a user auth example:

Typical Flask Approach to Dependencies

In our app__init__.py file (visit source), we define adb global:

importosfromflaskimportFlaskfromflask_sqlalchemyimportSQLAlchemyapp=Flask(__name__)app_settings=os.getenv('APP_SETTINGS','app.config.DevelopmentConfig')app.config.from_object(app_settings)db=SQLAlchemy(app)# ...truncated

And then in our API operations (using Flask blueprints), we import thatdb object:

fromflaskimportBlueprint,request,make_response,jsonifyfromflask.viewsimportMethodViewfromappimportbcrypt,db,appfromapp.modelsimportUserauth_blueprint=Blueprint('auth',__name__)classRegisterAPI(MethodView):"""    User Registration Resource"""defpost(self):# get the post datapost_data=request.get_json()# check if user already existsuser=User.query.filter_by(email=post_data.get('email')).first()ifnotuser:try:user=User(email=post_data.get('email'),password=post_data.get('password'))# insert the userdb.session.add(user)db.session.commit()# generate the auth tokenauth_token=user.encode_auth_token(user.id)responseObject={'status':'success','message':'Successfully registered.','auth_token':auth_token.decode()}returnmake_response(jsonify(responseObject)),201exceptExceptionase:...# truncated...

FastAPI Dependency Injection Example

fromfastapiimportDepends@api_router.post("/signup",response_model=schemas.User,status_code=201)defcreate_user_signup(*,db:Session=Depends(deps.get_db),user_in:schemas.CreateUser,)->Any:"""    Create new user without the need to be logged in."""user=db.query(User).filter(User.email==user_in.email).first()...

Notice theDepends line, where we specify a database dependency. That dependency is a functionwhich looks like this:

defget_db()->t.Generator:db=SessionLocal()# SQLAlchemy ORM sessiontry:yielddbfinally:db.close()

And that’s it. You can easily inject your database (and any other dependency you can think of) usingthis approach. This is a joy to test. When you set up your test app you can mock/stub dependencies with trivial adjustments:

asyncdefoverride_route53_dependency()->MagicMock:mock=MagicMock()returnmock@pytest.fixture()defclient()->Generator:withTestClient(app)asc:app.dependency_overrides[deps.get_route53_client]=override_route53_dependencyyieldcapp.dependency_overrides={}

There are workarounds for Flask’s dependency injection shortcomings, I wrote a post about how to use theFlask-Injector library to create the same effect - but it’s so much more work and way more complexity to keep in your head than what FastAPI offers.


4. Automatic Documentation via Standards

By writing your endpoints, you are automatically writing your API documentation.

FastAPI is carefully built around theOpenAPI Specification (formerly known as swagger) standards, which means that you get great API documentation just bywriting your application. No extra work. This includes:

  • Path operations
  • parameters
  • body requests
  • security

You can choose what your preferred documentation UI display as either:

Swagger UI documentation

Both of these options offer interactive documentation pages where you can input requestdata and trigger responses which is handy for bits of manual QA.

There are added benefits such as automatic client generation.


5. TheFastAPI toolbox

FastAPI is able to borrow from Starlette for more advanced functionality that you’ll often findyourself looking for in your APIs. In-process background tasks are extremely easy to implement:

@router.post("/send-password-reset",status_code=200)defsend_password_reset(background_tasks:BackgroundTasks,user_in:schemas.UserPasswordResetEmail,)->Any:# Trigger email (asynchronous)background_tasks.add_task(send_password_reset_email,user=user_in,)

Note that the FastAPI docs make it clear these background tasks shouldn’t be used for intensiveworkloads, they are designed for operations that take up to a few seconds (such as sending an email).

Thanks to Starlette, you also get:

  • WebSocket support
  • GraphQL support
  • CORS, GZip, Static Files, Streaming responses
  • Session and Cookie support

Although it’s not really FastAPI’s forte, you can also use Jinja2 templates to serve dynamic HTMLpages when needed. So whilst the framework is best for backend REST APIs, you have the optionto serve web pages if needed.

Conclusion

If you’re looking to build APIs (especially for microservices), FastAPI is a better choice than Flask. The only reasonnot to use it would be if your organization already has a lot of tooling built around Flask.

If you’re building something that is a lot of server-side rendered HTML, or a CMS, then Django is probablystill the way to go.

More FastAPI

Tiangolo (Sebastián Ramírez) shoutout:If you look at some of theearly reddit announcements of FastAPI in early 2019, you can see there was a lot of criticism for the project. Thankfully,Tiangolo ignored the haters and just kept building. He’s made a huge contribution to the Pythonecosystem.

Share this article
Subscribe

I publish about developments in AI Engineering from time to time.Plus a free 10-page report on ML system best practices. No spam.

Category


[8]ページ先頭

©2009-2026 Movatter.jp