aiohttp-apispec
Build and document REST APIs with aiohttp and apispec
aiohttp-apispec
key features:
docs
and request_schema
decorators
to add swagger spec support out of the box;validation_middleware
middleware to enable validating
with marshmallow schemas from those decorators;- SwaggerUI support.
- New from version 2.0 -
match_info_schema
, querystring_schema
,
form_schema
, json_schema
, headers_schema
and cookies_schema
decorators for specific request parts validation.
Look here for more info.
aiohttp-apispec
api is fully inspired by flask-apispec
library
Contents
Install
pip install aiohttp-apispec
Quickstart
Also you can read blog post about quickstart with aiohttp-apispec
from aiohttp_apispec import (
docs,
request_schema,
setup_aiohttp_apispec,
)
from aiohttp import web
from marshmallow import Schema, fields
class RequestSchema(Schema):
id = fields.Int()
name = fields.Str(description="name")
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
)
@request_schema(RequestSchema(strict=True))
async def index(request):
return web.json_response({"msg": "done", "data": {}})
app = web.Application()
app.router.add_post("/v1/test", index)
setup_aiohttp_apispec(
app=app,
title="My Documentation",
version="v1",
url="/api/docs/swagger.json",
swagger_path="/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",
)
@request_schema(RequestSchema(strict=True))
@response_schema(ResponseSchema(), 200)
def delete(self):
return web.json_response(
{"msg": "done", "data": {"name": self.request["data"]["name"]}}
)
app.router.add_view("/v1/view", TheView)
As alternative you can add responses info to docs
decorator, which is more compact way.
And it allows you not to use schemas for responses documentation:
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
responses={
200: {
"schema": ResponseSchema,
"description": "Success response",
},
404: {"description": "Not found"},
422: {"description": "Validation error"},
},
)
@request_schema(RequestSchema(strict=True))
async def index(request):
return web.json_response({"msg": "done", "data": {}})
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",
)
@request_schema(RequestSchema(strict=True))
@response_schema(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",
)
...
@request_schema(RequestSchema(strict=True))
async def index(request):
uid = request["validated_data"]["id"]
...
Also you can do it for specific view using put_into
parameter (beginning from version 2.0):
@request_schema(RequestSchema(strict=True), put_into="validated_data")
async def index(request):
uid = request["validated_data"]["id"]
...
More decorators
Starting from version 2.0 you can use shortenings for documenting and validating
specific request parts like cookies, headers etc using those decorators:
Decorator name | Default put_into param |
---|
match_info_schema | match_info |
querystring_schema | querystring |
form_schema | form |
json_schema | json |
headers_schema | headers |
cookies_schema | cookies |
And example:
@docs(
tags=["users"],
summary="Create new user",
description="Add new user to our toy database",
responses={
200: {"description": "Ok. User created", "schema": OkResponse},
401: {"description": "Unauthorized"},
422: {"description": "Validation error"},
500: {"description": "Server error"},
},
)
@headers_schema(AuthHeaders)
@json_schema(UserMeta)
@querystring_schema(UserParams)
async def create_user(request: web.Request):
headers = request["headers"]
json_data = request["json"]
query_params = request["querystring"]
...
Custom error handling
If you want to catch validation errors by yourself you
could use error_callback
parameter and create your custom error handler. Note that
it can be one of coroutine or callable and it should
have interface exactly like in examples below:
from marshmallow import ValidationError, Schema
from aiohttp import web
from typing import Optional, Mapping, NoReturn
def my_error_handler(
error: ValidationError,
req: web.Request,
schema: Schema,
error_status_code: Optional[int] = None,
error_headers: Optional[Mapping[str, str]] = None,
) -> NoReturn:
raise web.HTTPBadRequest(
body=json.dumps(error.messages),
headers=error_headers,
content_type="application/json",
)
setup_aiohttp_apispec(app, error_callback=my_error_handler)
Also you can create your own exceptions and create
regular Request in middleware like so:
class MyException(Exception):
def __init__(self, message):
self.message = message
async def my_error_handler(
error, req, schema, error_status_code, error_headers
):
await req.app["db"].do_smth()
raise MyException({"errors": error.messages, "text": "Oops"})
@web.middleware
async def intercept_error(request, handler):
try:
return await handler(request)
except MyException as e:
return web.json_response(e.message, status=400)
setup_aiohttp_apispec(app, error_callback=my_error_handler)
app.middlewares.extend([intercept_error, validation_middleware])
Build swagger web client
3.X SwaggerUI version
Just add swagger_path
parameter to setup_aiohttp_apispec
function.
For example:
setup_aiohttp_apispec(app, swagger_path="/docs")
Then go to /docs
and see awesome SwaggerUI
2.X SwaggerUI version
If you prefer older version you can use
aiohttp_swagger library.
aiohttp-apispec
adds swagger_dict
parameter to aiohttp web application
after initialization (with setup_aiohttp_apispec
function).
So you can use it easily like:
from aiohttp_apispec import setup_aiohttp_apispec
from aiohttp_swagger import setup_swagger
def create_app(app):
setup_aiohttp_apispec(app)
async def swagger(app):
setup_swagger(
app=app, swagger_url="/api/doc", swagger_info=app["swagger_dict"]
)
app.on_startup.append(swagger)
...
return app
Versioning
This software follows Semantic Versioning.
Please star this repository if this project helped you!