OpenAPI with Python — a state of the art and our latest contribution

At OVHcloud, we love using and building APIs. And to build good software, the first thing you need to do is look at the state of the art in your domain. As a matter of fact, there are more and more tools available and it’s often hard to make a choice without comparisons. Maybe you’ll be tempted to build a new module instead of contributing to an existing one.

OpenAPI with Python — a state of the art and our latest contribution

We have just open-sourced a Python module, apispec-fromfile, to simplify the usage of OpenAPI in Python by  importing OpenAPI specifications  from files. And to better explain why we do it, here you have a state of the art about OpenAPI with Python (and more specifically with Flask).

What is OpenAPI?

As you can read on the official website, the OpenAPI Specification is “a broadly adopted industry standard for describing modern APIs”. This standard, formerly named Swagger, is used to describe, produce, consume, and visualize APIs in a vendor neutral format. This format is based on JSON Schema and specification files can be either in YAML or JSON format.

Swagger & OpenAPI Initiative

Nowadays, there are two versions in the wild: 2 and 3. Version 2 is the Swagger specification and is quite common thanks to the many tools available. Version 3 is the latest one, the first one from the OpenAPI Initiative (OAI).

openapi: 3.0.3
info:
  title: My Cutie Marks Catalog
  description: This is a sample server for a cutie marks catalog.
  termsOfService: http://example.com/terms/
  contact:
    name: API Support
    url: http://www.example.com/support
    email: support@example.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
  version: 1.0.1

Related links:

Why would you use it?

Well, there are two main purposes:

  • to describe your API, with nice documentation (and potentially try your endpoints with it directly)
  • to generate your API.

Moreover, you will want to use the latest version of the OpenAPI Specification to take advantage of the new features: the version 3.

Related links:

Scenarios

When you want to use OpenAPI Specification, you will fall into one of these three scenarios:

  • Contract-first driven API: when you start from the specification and get an API as a result
  • Server-first driven API: when you start from an existing or a new API and get the specification as a result
  • Legacy API: when you already have an API and want the OpenAPI Specification.

In the first case, you can choose the technology you will build your API with. Many tools allow you to generate your API quickly and easily, like Swagger tools (codegen, editor). The Swagger Editor even encourages you to use specific technologies to start your project with, like Connexion in Python. This is the more convenient way of generating an API with OpenAPI Specification.

In the second case, you have two possibilities:

  • starting from scratch: you can choose a technology that can generate the OpenAPI Specification
  • using a legacy API: you may need to adapt your code to generate the OpenAPI Specification.

In the third case: no generation, no choice of technology, you just have to write your specification manually. 🎈Easy peasy, puddin’ in the freezy 🎈 🎊 🧁 🐊.

So, two things need to be clarified when you are using Python and Flask for your API:

  • which technology to build your new API with, in order to to generate your OpenAPI Specification
  • which technology to generate your OpenAPI Specification with, from a legacy API.

Here is a graph to help us to answer to those questions:

API code (→ specification file) → documentation / Swagger UI

Related links:

Python code to specification file

marshmallow

To get the specification file from your code, you would probably want to use docstrings. Then, one solution is to use the apispec library with its ecosystem. It is a pluggable API specification generator with built-in support for marshmallow. And if you are using Voluptuous, you can use that too (clin d'œil). And it supports OAS v2 and v3!

from apispec import APISpec
from apispec_fromfile import FromFilePlugin
 
 
# Create an APISpec
spec = APISpec(
    title="My Cutie Marks Catalog",
    version="1.0.1",
    openapi_version="3.0.3",
    plugins=[FromFilePlugin("resource")],
)
print(spec.to_yaml())
Flask

You can use it directly, with its web frameworks plugin and its other plugins. Or you can use Flask extensions.
Some frameworks are based on this library to do more things easily (and then support OAS v2 and v3):

  • flask-smorest: based on apispec and marshmallow, use decorators a lot
  • flask-apispec: same as flask-smorest, inspired by Flask-RESTful.

Other frameworks are not (totally) based on it, and they often support the OpenAPI Specification v2 :

  • flask-swagger (OAS v2): as said in its description, it is “a Swagger 2.0 specification extractor for Flask”, compatible with Flask-RESTful
  • flasgger (OAS v2 & v3): a complete fork of flask-swagger, compatible with apispec, with experimental support for OpenAPI v3.


At OVHcloud, some of our APIs are using apispec with our new plugin apispec-fromfile, to avoid putting YAML into docstrings.

from apispec_fromfile import from_file
from extensions import spec
 
 
# Create an endpoint
@from_file("my/spec/file.yml")
def hello():
return {"hello"}
 
# Register entities and paths
spec.path(resource=hello)

Related links:

Specification file to documentation

sphynx

Now that you have your specification file, in JSON or YAML format, you want to use it to describe your API. One way is to use Sphinx to generate a documentation in Python. Those extensions will help you to do that:

Specification file to Swagger UI

Another cool thing you can do with your specification file is to expose your API over the Swagger UI. This can even be used as a quick dynamic documentation. To do that, you can either spawn a Swagger UI in a container (you may need to expose your specification file) or if you are using one of the following frameworks, it is already embedded:

Python code to Swagger UI

If you choose to start with a framework, some of them can do all the graph traversal and expose an endpoint to a Swagger UI:

Which tool can I use?

After all this reading, you may wonder which tool to use. Let’s complete the three scenarios, with some suggestions:

Swagger
  • Contract-first driven API: you can use Connexion (Swagger Editor is using it to generate Python code) or another framework
  • Server-first driven API: you can either start with a framework (for example flask-smorest) or complete your code with apispec or flasgger
  • Legacy API: you can complete your code with apispec or flasgger.

And for the documentation, you can use Swagger UI and / or ReDoc.

One thing you need to pay attention to is the version of the OpenAPI Specification. It could be a good thing to start with the latest version (v3).

What is our Python module for: apispec-fromfile?

For one our API, we were in the “server-first driven API” scenario, with an existing API based on Voluptuous and Flask. We wanted to generate the OpenAPI Specification version 3 and documentation from the code, even if we needed to adapt the code a bit.

Flasgger were a good starting point, with its decorator, but the support of apispec is still experimental and we are not using Marshmallow. The flask-swagger library uses a keyword in docstrings to import specification files for each endpoint, but it only supports OpenAPI v2.

Therefore, we kept the idea of using a decorator instead of putting YAML into docstrings, and we built an apispec plugin, which supports OpenAPI v2 and v3: https://github.com/ovh/python-apispec-fromfile ✨ 🍰 🎉. Then we just have to write small specification files gradually, and add a decorator to our functions.

Software Developer at OVHcloud