DFO-Ocean-Navigator/Ocean-Data-Map-Project

Replacing Flask with FastAPI

htmlboss opened this issue · 3 comments

Starting an issue to discuss replacing our Flask-based API implementation with FastAPI.

I've been looking into ways of automatically generating useful API documentation based on schemas defined in the routes/schemas/ folder, in addition to in-line doc strings declared in api_v1_0.py. This led me to discovering FastAPI and how it appears to be a next-gen framework designed to address some of the parts of Flask that aren't keeping up with evolving API standards.

Some high-level benefits of FastAPI over Flask:

  • Up to 7x faster due to its internal usage of Python's asyncio library. Newer versions of Flask support this also.
  • FastAPI implements the ASGI server standard, while Flask is a WSGI implementation. One of the main issues encountered with WSGI was it's constraint of tying up one worker thread per incoming API request (we've been running into this, with Dwayne having to tweak our gunicorn threads very often), thus making API scaling much more difficult. ASGI was developed as the natural successor to WSGI to solve this problem by enabling an application to respond to requests in a non-blocking way (async). ASGI enables interoperability within the whole Python async web stack: servers, applications, middleware, and individual components. In our case, we could execute our plotting code (very slow) in async, thus freeing up worker threads for 2-3 seconds to queue up other incoming requests for processing. This will improve the perceived API response time on the client-side.
  • FastAPI unlocks the full feature set of HTTP/2 (e.g. WebSockets, Server-Sent Events, etc.). Our Flask-based API can only support the HTTP/1.1 standard (20+ years old!). HTTP/2 improves the way data is transmitted over the internet to take advantage of faster computers, standardized web browsers, and high throughput low latency applications. Since FastAPI leverages ASGI, gunicorn must also be replaced by uvicorn. Same thing but for ASGI. There's also HTTP/3 but that's very experimental at this time so we won't worry about that for a couple of years.
  • FastAPI offers full integration with pydantic to offer extremely fast schema validation (it's compiled with cython) compared to marshmallow.
  • Like Flask, FastAPI offers type hinting in the route definitions.
  • Most importantly, FastAPI was built around the OpenAPI standard and therefore offers built-in support for automatic generation of API docs that are viewable in SwaggerUI, or ReDoc. By migrating our REST API to be conformant to the OpenAPI standard, we'll enjoy access to standardized testing frameworks, best practices, and enable clients to easily integrate with our API (adding support for pygeoapi for example is a separate topic). ReDoc example:
    image
  • FastAPI is very extensible and thus allows easy usage of CORS or response streaming.

Also, 1-1 migration would be fairly straight forward since FastAPI and Flask have very similar APIs:
Flask:

@app.route("/get-data")
def get_data():
    data = db_query(...)
    return jsonify(data)

FastAPI:

@app.get('/')
def read_results():
    results = some_library()
    return results

Article with more technical details: https://christophergs.com/python/2021/06/16/python-flask-fastapi/

I agree we need to maintain our current API. My suggestion with regard to pygeoapi is look at how that project has leveraged OpenAPI to build a project that uses FastAPI as the external facing library.

As an important step, cleanly documenting our API is important. Noah had done this kind of work previously but that would be need to be updated. I love the idea of "automatically" generating the API code (and related documentation!)

Do we have evidence that changing the library from Flask to FastAPI we use will actually help with performance? My suspicion is the delays occur within our python code that fulfills the API requests are a much larger factor than the specific front end API framework.

Migrated to FastAPI in #1024.