codeforboston / police-data-trust

A national archive of police data collected by journalists, lawyers, and activists around the country.
https://www.nationalpolicedata.org
MIT License
47 stars 81 forks source link

[FEATURE] OpenAPI Specification #111

Closed dwreeves closed 3 years ago

dwreeves commented 3 years ago

Feature

We have a Pydantic model for the incidents (pending approval of #110). Now what we want is automated API documentation through a Swagger UI (example here) that defines all our models and the request/response bodies of all the routes available in our API.

Solution

Solving this unfortunately requires either:

  1. Using this library and remove flask-pydantic from our codebase. The spectree routing implementation is not as elegant as flask_pydantic's, so either we (1a.) put up with that, or (1b.) steal a bit from flask_pydantic's code to implement their view function handlers.
  2. Continue to use flask-pydantic, and implement a sort of ad hoc solution via Flasgger.
  3. Continue to use flask-pydantic, and contribute to their source code a solution to implementing Swagger. That's... a lot of work. 😬 I am personally intrigued by doing this and may do it for fun, but I imagine others may find it daunting, and in any case it'd slow down production.

Other options that are not recommended, but available:

  1. Removing Pydantic from our code base entirely, and going to the marshmallow + flask-apispec stack. (Not recommended; Marshmallow is basically a legacy library at this point.)
  2. Swapping to FastAPI (Also not recommended; I like FastAPI, but Flask is a more venerated framework with a more fleshed out ecosystem, and it judiciously uses things like proxies and thread locals to make life easier for beginner coders. These two reasons make it an appropriate framework for a project like ours that needs to reliably implement lots of bells and whistles with volunteer labor.)

Background

The Python ecosystem right now is shifting away from a pre-3.6 era to a post-3.6 era, where Python's type hint system is increasingly being used for controlling the actual logic of how applications work, rather than just for type checking. (Additionally, 3.6 implemented asyncio with async/await keywords.)

Starlette/FastAPI (ASGI web framework) + Pydantic (serialization library) is the typical combination you see out in the wild. These are relatively modern frameworks that take very full use of Python 3.6 features (both async and type hints).

Flask is a WSGI web framework that's over 10 years old and predates Python 3.6. The serialization library you typically see with Flask is Marshallow. Marshmallow is very much a pre-3.6 era library, and it definitely shows its age.

tldr:

dwreeves commented 3 years ago

@mikeyavorsky pick option 1 for least frustration. The two realistic options I think are 1 and 2, and between them, 1 is easier.

alexjball commented 3 years ago

Option 1 looks pretty good. flask_pydantic and spectree look about equally popular and spectree is actively being developed.

Implementation will involve swapping the @flask_pydantic.validate decorator in our routes with the @api.validate decorator and initializing spectree. Here is an example flask app that I'll be following.

This will add swagger ui for reading the docs and calling the api at /apidocs/swagger

The goal is to derive our api spec from our schemas. We'll share the api spec with partners, and could potentially use it to generate the api client and/or mock api used by the frontend.