Build and document REST APIs with aiohttp and apispec
Switch branches/tags
Clone or download
Latest commit a17a68f Nov 12, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
aiohttp_apispec Merge pull request #31 from ttyridal/custom_responses_in_docs Nov 12, 2018
docs theeming Aug 24, 2018
tests Adding test for path parameter description added twice bug #28 Nov 9, 2018
.gitignore title and version kwargs Oct 23, 2018
.travis.yml python 3.7 travis Aug 31, 2018
LICENSE Initial commit Dec 5, 2017
README.md readme refactoring Oct 17, 2018
dev-requirements.txt docs fix Oct 23, 2018
setup.py version 0.7.6 Nov 12, 2018

README.md

aiohttp-apispec

Build and document REST APIs with aiohttp and apispec

Pypi build status [codcov]

[docs] Code style: black Contributors

Python 3.5 Python 3.6 Python 3.7

img

aiohttp-apispec key features:

  • docs, use_kwargs and marshal_with decorators to add swagger spec support out of the box
  • validation_middleware middleware to enable validating with marshmallow schemas from those decorators

aiohttp-apispec api is fully inspired by flask-apispec library

Contents

Install

pip install aiohttp-apispec

Quickstart

from aiohttp_apispec import docs, use_kwargs, marshal_with, setup_aiohttp_apispec
from aiohttp import web
from marshmallow import Schema, fields


class RequestSchema(Schema):
    id = fields.Int()
    name = fields.Str(description="name")


class ResponseSchema(Schema):
    msg = fields.Str()
    data = fields.Dict()


@docs(tags=["mytag"], 
      summary="Test method summary", 
      description="Test method description")
@use_kwargs(RequestSchema(strict=True))
@marshal_with(ResponseSchema(), 200)
async def index(request):
    return web.json_response({"msg": "done", "data": {}})


app = web.Application()
app.router.add_post("/v1/test", index)

# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(
    app=app, title="My Documentation", version="v1", url="/api/docs/api-docs"
)

# now we can find it on 'http://localhost:8080/api/docs/api-docs'
web.run_app(app)

Class based views are also supported:

class TheView(web.View):
    @docs(
        tags=["mytag"],
        summary="View method summary",
        description="View method description",
    )
    @use_kwargs(RequestSchema(strict=True))
    @marshal_with(ResponseSchema(), 200)
    def delete(self):
        return web.json_response(
            {"msg": "done", "data": {"name": self.request["data"]["name"]}}
        )


app.router.add_view("/v1/view", TheView)

Adding validation middleware

from aiohttp_apispec import validation_middleware

...

app.middlewares.append(validation_middleware)

Now you can access all validated data in route from request['data'] like so:

@docs(
    tags=['mytag'],
    summary='Test method summary',
    description='Test method description',
)
@use_kwargs(RequestSchema(strict=True))
@marshal_with(ResponseSchema(), 200)
async def index(request):
    uid = request['data']['id']
    name = request['data']['name']
    return web.json_response(
        {'msg': 'done', 'data': {'info': f'name - {name}, id - {uid}'}}
    )

You can change Request's 'data' param to another with request_data_name argument of setup_aiohttp_apispec function:

setup_aiohttp_apispec(app=app,
                      request_data_name='validated_data',
                      title='My Documentation',
                      version='v1',
                      url='/api/docs/api-docs')
                      
...                  

@use_kwargs(RequestSchema(strict=True))
async def index(request):
    uid = request['validated_data']['id']
        ...

If you want to catch validation errors you should write your own middleware and catch web.HTTPClientError, json.JSONDecodeError and so on. Like this:

@web.middleware
async def my_middleware(request, handler):
    try:
        return await handler(request)
    except web.HTTPClientError:
        return web.json_response(status=400)
        
app.middlewares.extend([
    my_middleware,  # Catch exception by your own, format it and respond to client
    validation_middleware,
])

Build swagger web client

aiohttp-apispec adds swagger_dict parameter to aiohttp web application after initialization. So you can use it easily with aiohttp_swagger library:

from aiohttp_swagger import setup_swagger

...

async def swagger(app):
    setup_swagger(
        app=app, swagger_url='/api/doc', swagger_info=app['swagger_dict']
    )
app.on_startup.append(swagger)
# now we can access swagger client on /api/doc url