personal web log written by izabeera and dryobates

sphinx httpdomain rest

Documenting Flask REST API in Sphinx

by dryobates

Many REST frameworks have built in documentation for provided API. Many specialized software exists for centralized API documentation. But if all you need is to include documentation for your project in your regular sphinx documentation then sphinxcontrib-httpdomain can be helpful.

sphinxcontrib-httpdomain has a lot roles and directives for providing documentation for rest api. It has also autodoc support for Flask, Bottle and Tornado.

We use Flask, so here's quick example, how much you can get with it.

In conf.py:

extensions = [
    'sphinx.ext.autodoc',
    'sphinxcontrib.httpdomain',
    'sphinxcontrib.autohttp.flask',
    'sphinxcontrib.autohttp.flaskqref',
]

In documentation:

API Documentation
=================

Summary
-------

.. qrefflask:: myapp.api:make_port()
  :undoc-static:

API Details
-----------

.. autoflask:: myapp.api:make_port()
  :undoc-static:

In code:

...

class PostsCollection(ActionResource):
    """Posts collection."""

    def get(self):
        """Return collection of posts.

        .. :quickref: Posts Collection; Get collection of posts.

        **Example request**:

        .. sourcecode:: http

          GET /posts/ HTTP/1.1
          Host: example.com
          Accept: application/json

        **Example response**:

        .. sourcecode:: http

          HTTP/1.1 200 OK
          Vary: Accept
          Content-Type: application/json

          [
            {
              "post_id": 12345,
              "author": "/author/123/",
              "tags": ["sphinx", "rst", "flask"],
              "title": "Documenting API in Sphinx with httpdomain",
              "body": "How to..."
            },
            {
              "post_id": 12346,
              "author": "/author/123/",
              "tags": ["python3", "typehints", "annotations"],
              "title": "To typehint or not to typehint that is the question",
              "body": "Static checking in python..."
            }
          ]

        :query sort: sorting order e.g. sort=author,-pub_date
        :query q: full text search query
        :resheader Content-Type: application/json
        :status 200: posts found
        :returns: :class:`myapp.objects.Post`
        """
        schema = PostSchema(many=True, strict=True)
        return schema.dump(self.do_get_post_collection())

    def post(self):
        """Add new post.

        .. :quickref: Posts Collection; Add new post to collection.

        :reqheader Accept: application/json
        :<json string title: post title
        :<json string body: post body
        :<json string author: author id
        :<json List[string] tags: tags list
        :>json int: id
        :resheader Content-Type: application/json
        :resheader Location: post url
        :status 201: post created
        :status 400: malformed request
        :status 422: invalid parameters
        """
        try:
            post_data = json.loads(request.data.decode('utf-8'))
        except json.JSONDecodeError as e:
            return {'errors': [e]}, 400
        try:
            post_id = self.do_add_post(post_data)
        except ValidationError as e:
            return {'errors': e.args[0]}, 422
        else:
            return {'id': post_id}, 201, {'Location': self.api.url_for(PostItem, post_id=post_id)}


def make_port():
    """Creates api access port."""

    app = Flask(__name__)
    api = Api(app)
    post_collection_actions = {
        'api': api,
        'add_post': AddPost(),
        'get_post_collection': GetPostCollection(),
    }

    api.add_resource(PostsCollection, '/posts/',
        resource_class_kwargs=post_collection_actions)
    ...
    return app

And the result is:

https://stolarscy.com/static/uploads/httpdomain.png

In the above source code I have written examples by hand. That's only to show what can be done. Personally I'd prefer to pass link to source code of test. Probably doctest could be even better. I have to explore that idea for API requests.

One thing that doesn't look good for me is description of JSON. We use jsonschema and can generate it on the fly. I don't know how to include it in documentation. The only extension for jsonschema I found is sphinxcontrib-jsonschema but it can only read schema from file :( Do you know something better?

dryobates
dryobates
Jakub Stolarski. Software engineer. I work professionally as programmer since 2005. Speeding up software development with Test Driven Development, task automation and optimization for performance are things that focus my mind from my early career up to now. If you ask me for my religion: Python, Vim and FreeBSD are my trinity ;) Email: jakub@stolarscy.com

Archive

Tag cloud