<h1 align="center">aiohttp-apispec</h1>
<p align="center">Build and document REST APIs with <a href="https://github.com/aio-libs/aiohttp">aiohttp</a> and <a href="https://github.com/marshmallow-code/apispec">apispec</a></p>
<p align="center">
<a href="https://pypi.python.org/pypi/aiohttp-apispec"><img src="https://badge.fury.io/py/aiohttp-apispec.svg" alt="Pypi"></a>
<a href="https://github.com/maximdanilchenko/aiohttp-apispec/graphs/contributors"><img src="https://img.shields.io/github/contributors/maximdanilchenko/aiohttp-apispec.svg" alt="Contributors"></a>
<a href="https://pepy.tech/project/aiohttp-apispec"><img src="https://pepy.tech/badge/aiohttp-apispec" alt="Downloads"></a>
</p>
<p align="center">
<a href="https://travis-ci.org/maximdanilchenko/aiohttp-apispec"><img src="https://travis-ci.org/maximdanilchenko/aiohttp-apispec.svg" alt="build status"></a>
<a href="https://aiohttp-apispec.readthedocs.io/en/latest/?badge=latest"><img src="https://readthedocs.org/projects/aiohttp-apispec/badge/?version=latest" alt="[docs]"></a>
<a href="https://codecov.io/gh/maximdanilchenko/aiohttp-apispec"><img src="https://codecov.io/gh/maximdanilchenko/aiohttp-apispec/branch/master/graph/badge.svg" alt="[codcov]"></a>
<a href="https://github.com/ambv/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Code style: black"></a>
</p>
<p>
```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](#more-decorators) for more info.
```aiohttp-apispec``` api is fully inspired by ```flask-apispec``` library
## Contents
- [Install](#install)
- [Quickstart](#quickstart)
- [Adding validation middleware](#adding-validation-middleware)
- [More decorators](#more-decorators)
- [Custom error handling](#custom-error-handling)
- [Build swagger web client](#build-swagger-web-client)
- [Versioning](#versioning)
## Install
```
pip install aiohttp-apispec
```
## Quickstart
*Also you can read [blog post](https://dmax.blog/how_to_easily_build_modern_web_apis_with_python_and_aiohttp) about quickstart with aiohttp-apispec*
```Python
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)
# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(
app=app,
title="My Documentation",
version="v1",
url="/api/docs/swagger.json",
swagger_path="/api/docs",
)
# Now we can find spec on 'http://localhost:8080/api/docs/swagger.json'
# and docs on 'http://localhost:8080/api/docs'
web.run_app(app)
```
Class based views are also supported:
```python
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:
```python
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
responses={
200: {
"schema": ResponseSchema,
"description": "Success response",
}, # regular response
404: {"description": "Not found"}, # responses without schema
422: {"description": "Validation error"},
},
)
@request_schema(RequestSchema(strict=True))
async def index(request):
return web.json_response({"msg": "done", "data": {}})
```
## Adding validation middleware
```Python
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:
```Python
@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:
```python
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):
```python
@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:
```python
@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) # <- schema for headers validation
@json_schema(UserMeta) # <- schema for json body validation
@querystring_schema(UserParams) # <- schema for querystring params validation
async def create_user(request: web.Request):
headers = request["headers"] # <- validated headers!
json_data = request["json"] # <- validated json!
query_params = request["querystring"] # <- validated 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:
```python
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:
```python
class MyException(Exception):
def __init__(self, message):
self.message = message
# It can be coroutine as well:
async def my_error_handler(
error, req, schema, error_status_code, error_headers
):
await req.app["db"].do_smth() # So you can use some async stuff
raise MyException({"errors": error.messages, "text": "Oops"})
# This middleware will handle your own exceptions:
@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)
# Do not forget to add your own middleware before validation_middleware
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:
```python
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](https://github.com/cr0hn/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:
```Python
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)
# now we can access swagger client on '/api/doc' url
...
return app
```
## Versioning
This software follows [Semantic Versioning](http://semver.org/).
------
Please star this repository if this project helped you!
Raw data
{
"_id": null,
"home_page": "https://github.com/maximdanilchenko/aiohttp-apispec",
"name": "aiohttp-apispec",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.5",
"maintainer_email": "",
"keywords": "aiohttp marshmallow apispec swagger",
"author": "Danilchenko Maksim",
"author_email": "dmax.dev@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/4a/47/72e2a08bb9990264cd058d1fef90b7378b459ad5d530aabcf10fe837d8e2/aiohttp-apispec-2.2.3.tar.gz",
"platform": "",
"description": "<h1 align=\"center\">aiohttp-apispec</h1>\n<p align=\"center\">Build and document REST APIs with <a href=\"https://github.com/aio-libs/aiohttp\">aiohttp</a> and <a href=\"https://github.com/marshmallow-code/apispec\">apispec</a></p>\n\n<p align=\"center\">\n <a href=\"https://pypi.python.org/pypi/aiohttp-apispec\"><img src=\"https://badge.fury.io/py/aiohttp-apispec.svg\" alt=\"Pypi\"></a>\n <a href=\"https://github.com/maximdanilchenko/aiohttp-apispec/graphs/contributors\"><img src=\"https://img.shields.io/github/contributors/maximdanilchenko/aiohttp-apispec.svg\" alt=\"Contributors\"></a>\n <a href=\"https://pepy.tech/project/aiohttp-apispec\"><img src=\"https://pepy.tech/badge/aiohttp-apispec\" alt=\"Downloads\"></a>\n</p>\n\n<p align=\"center\">\n <a href=\"https://travis-ci.org/maximdanilchenko/aiohttp-apispec\"><img src=\"https://travis-ci.org/maximdanilchenko/aiohttp-apispec.svg\" alt=\"build status\"></a>\n <a href=\"https://aiohttp-apispec.readthedocs.io/en/latest/?badge=latest\"><img src=\"https://readthedocs.org/projects/aiohttp-apispec/badge/?version=latest\" alt=\"[docs]\"></a>\n <a href=\"https://codecov.io/gh/maximdanilchenko/aiohttp-apispec\"><img src=\"https://codecov.io/gh/maximdanilchenko/aiohttp-apispec/branch/master/graph/badge.svg\" alt=\"[codcov]\"></a>\n <a href=\"https://github.com/ambv/black\"><img src=\"https://img.shields.io/badge/code%20style-black-000000.svg\" alt=\"Code style: black\"></a>\n</p>\n\n<p>\n\n```aiohttp-apispec``` key features:\n- ```docs``` and ```request_schema``` decorators \nto add swagger spec support out of the box;\n- ```validation_middleware``` middleware to enable validating \nwith marshmallow schemas from those decorators;\n- **SwaggerUI** support.\n- *New from version 2.0* - ```match_info_schema```, ```querystring_schema```, \n```form_schema```, ```json_schema```, ```headers_schema``` and ```cookies_schema``` \ndecorators for specific request parts validation. \nLook [here](#more-decorators) for more info.\n\n```aiohttp-apispec``` api is fully inspired by ```flask-apispec``` library\n\n## Contents\n\n- [Install](#install)\n- [Quickstart](#quickstart)\n- [Adding validation middleware](#adding-validation-middleware)\n- [More decorators](#more-decorators)\n- [Custom error handling](#custom-error-handling)\n- [Build swagger web client](#build-swagger-web-client)\n- [Versioning](#versioning)\n\n\n## Install\n\n```\npip install aiohttp-apispec\n```\n\n## Quickstart\n\n*Also you can read [blog post](https://dmax.blog/how_to_easily_build_modern_web_apis_with_python_and_aiohttp) about quickstart with aiohttp-apispec*\n\n```Python\nfrom aiohttp_apispec import (\n docs,\n request_schema,\n setup_aiohttp_apispec,\n)\nfrom aiohttp import web\nfrom marshmallow import Schema, fields\n\n\nclass RequestSchema(Schema):\n id = fields.Int()\n name = fields.Str(description=\"name\")\n\n@docs(\n tags=[\"mytag\"],\n summary=\"Test method summary\",\n description=\"Test method description\",\n)\n@request_schema(RequestSchema(strict=True))\nasync def index(request):\n return web.json_response({\"msg\": \"done\", \"data\": {}})\n\n\napp = web.Application()\napp.router.add_post(\"/v1/test\", index)\n\n# init docs with all parameters, usual for ApiSpec\nsetup_aiohttp_apispec(\n app=app, \n title=\"My Documentation\", \n version=\"v1\",\n url=\"/api/docs/swagger.json\",\n swagger_path=\"/api/docs\",\n)\n\n# Now we can find spec on 'http://localhost:8080/api/docs/swagger.json'\n# and docs on 'http://localhost:8080/api/docs'\nweb.run_app(app)\n```\nClass based views are also supported:\n```python\nclass TheView(web.View):\n @docs(\n tags=[\"mytag\"],\n summary=\"View method summary\",\n description=\"View method description\",\n )\n @request_schema(RequestSchema(strict=True))\n @response_schema(ResponseSchema(), 200)\n def delete(self):\n return web.json_response(\n {\"msg\": \"done\", \"data\": {\"name\": self.request[\"data\"][\"name\"]}}\n )\n\n\napp.router.add_view(\"/v1/view\", TheView)\n```\n\nAs alternative you can add responses info to `docs` decorator, which is more compact way. \nAnd it allows you not to use schemas for responses documentation:\n\n```python\n@docs(\n tags=[\"mytag\"],\n summary=\"Test method summary\",\n description=\"Test method description\",\n responses={\n 200: {\n \"schema\": ResponseSchema,\n \"description\": \"Success response\",\n }, # regular response\n 404: {\"description\": \"Not found\"}, # responses without schema\n 422: {\"description\": \"Validation error\"},\n },\n)\n@request_schema(RequestSchema(strict=True))\nasync def index(request):\n return web.json_response({\"msg\": \"done\", \"data\": {}})\n```\n\n## Adding validation middleware\n\n```Python\nfrom aiohttp_apispec import validation_middleware\n\n...\n\napp.middlewares.append(validation_middleware)\n```\nNow you can access all validated data in route from ```request['data']``` like so:\n\n```Python\n@docs(\n tags=[\"mytag\"],\n summary=\"Test method summary\",\n description=\"Test method description\",\n)\n@request_schema(RequestSchema(strict=True))\n@response_schema(ResponseSchema, 200)\nasync def index(request):\n uid = request[\"data\"][\"id\"]\n name = request[\"data\"][\"name\"]\n return web.json_response(\n {\"msg\": \"done\", \"data\": {\"info\": f\"name - {name}, id - {uid}\"}}\n )\n```\n\n\nYou can change ``Request``'s ``'data'`` param to another with ``request_data_name`` argument of \n``setup_aiohttp_apispec`` function:\n\n```python\nsetup_aiohttp_apispec(\n app=app,\n request_data_name=\"validated_data\",\n)\n\n...\n\n\n@request_schema(RequestSchema(strict=True))\nasync def index(request):\n uid = request[\"validated_data\"][\"id\"]\n ...\n```\n\nAlso you can do it for specific view using ```put_into``` \nparameter (beginning from version 2.0):\n\n```python\n@request_schema(RequestSchema(strict=True), put_into=\"validated_data\")\nasync def index(request):\n uid = request[\"validated_data\"][\"id\"]\n ...\n```\n\n## More decorators\n\nStarting from version 2.0 you can use shortenings for documenting and validating \nspecific request parts like cookies, headers etc using those decorators:\n\n| Decorator name | Default put_into param |\n|:----------|:-----------------|\n| match_info_schema | match_info |\n| querystring_schema | querystring |\n| form_schema | form |\n| json_schema | json |\n| headers_schema | headers |\n| cookies_schema | cookies | \n\nAnd example:\n\n```python\n@docs(\n tags=[\"users\"],\n summary=\"Create new user\",\n description=\"Add new user to our toy database\",\n responses={\n 200: {\"description\": \"Ok. User created\", \"schema\": OkResponse},\n 401: {\"description\": \"Unauthorized\"},\n 422: {\"description\": \"Validation error\"},\n 500: {\"description\": \"Server error\"},\n },\n)\n@headers_schema(AuthHeaders) # <- schema for headers validation\n@json_schema(UserMeta) # <- schema for json body validation\n@querystring_schema(UserParams) # <- schema for querystring params validation\nasync def create_user(request: web.Request):\n headers = request[\"headers\"] # <- validated headers!\n json_data = request[\"json\"] # <- validated json!\n query_params = request[\"querystring\"] # <- validated querystring!\n ...\n```\n\n## Custom error handling\n\nIf you want to catch validation errors by yourself you \ncould use `error_callback` parameter and create your custom error handler. Note that\nit can be one of coroutine or callable and it should \nhave interface exactly like in examples below:\n\n```python\nfrom marshmallow import ValidationError, Schema\nfrom aiohttp import web\nfrom typing import Optional, Mapping, NoReturn\n\n\ndef my_error_handler(\n error: ValidationError,\n req: web.Request,\n schema: Schema,\n error_status_code: Optional[int] = None,\n error_headers: Optional[Mapping[str, str]] = None,\n) -> NoReturn:\n raise web.HTTPBadRequest(\n body=json.dumps(error.messages),\n headers=error_headers,\n content_type=\"application/json\",\n )\n\nsetup_aiohttp_apispec(app, error_callback=my_error_handler)\n```\nAlso you can create your own exceptions and create \nregular Request in middleware like so:\n\n```python\nclass MyException(Exception):\n def __init__(self, message):\n self.message = message\n\n# It can be coroutine as well:\nasync def my_error_handler(\n error, req, schema, error_status_code, error_headers\n):\n await req.app[\"db\"].do_smth() # So you can use some async stuff\n raise MyException({\"errors\": error.messages, \"text\": \"Oops\"})\n\n# This middleware will handle your own exceptions:\n@web.middleware\nasync def intercept_error(request, handler):\n try:\n return await handler(request)\n except MyException as e:\n return web.json_response(e.message, status=400)\n\n\nsetup_aiohttp_apispec(app, error_callback=my_error_handler)\n\n# Do not forget to add your own middleware before validation_middleware\napp.middlewares.extend([intercept_error, validation_middleware])\n```\n\n## Build swagger web client\n\n#### 3.X SwaggerUI version\n\nJust add `swagger_path` parameter to `setup_aiohttp_apispec` function.\n\nFor example:\n\n```python\nsetup_aiohttp_apispec(app, swagger_path=\"/docs\")\n```\n\nThen go to `/docs` and see awesome SwaggerUI\n\n#### 2.X SwaggerUI version\n\nIf you prefer older version you can use \n[aiohttp_swagger](https://github.com/cr0hn/aiohttp-swagger) library.\n`aiohttp-apispec` adds `swagger_dict` parameter to aiohttp web application \nafter initialization (with `setup_aiohttp_apispec` function). \nSo you can use it easily like:\n\n```Python\nfrom aiohttp_apispec import setup_aiohttp_apispec\nfrom aiohttp_swagger import setup_swagger\n\n\ndef create_app(app):\n setup_aiohttp_apispec(app)\n\n async def swagger(app):\n setup_swagger(\n app=app, swagger_url=\"/api/doc\", swagger_info=app[\"swagger_dict\"]\n )\n\n app.on_startup.append(swagger)\n # now we can access swagger client on '/api/doc' url\n ...\n return app\n```\n\n## Versioning\n\nThis software follows [Semantic Versioning](http://semver.org/).\n\n------\n\nPlease star this repository if this project helped you!\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Build and document REST APIs with aiohttp and apispec",
"version": "2.2.3",
"project_urls": {
"Homepage": "https://github.com/maximdanilchenko/aiohttp-apispec"
},
"split_keywords": [
"aiohttp",
"marshmallow",
"apispec",
"swagger"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "4a4772e2a08bb9990264cd058d1fef90b7378b459ad5d530aabcf10fe837d8e2",
"md5": "2c7c46684f908b8bb53c557d8d053f23",
"sha256": "d70431e5f3ef5c6dc96dc9180ce10ddfd78fa054f178af8259707eb6d421ed05"
},
"downloads": -1,
"filename": "aiohttp-apispec-2.2.3.tar.gz",
"has_sig": false,
"md5_digest": "2c7c46684f908b8bb53c557d8d053f23",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.5",
"size": 2653064,
"upload_time": "2022-03-01T14:35:30",
"upload_time_iso_8601": "2022-03-01T14:35:30.973499Z",
"url": "https://files.pythonhosted.org/packages/4a/47/72e2a08bb9990264cd058d1fef90b7378b459ad5d530aabcf10fe837d8e2/aiohttp-apispec-2.2.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-03-01 14:35:30",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "maximdanilchenko",
"github_project": "aiohttp-apispec",
"travis_ci": true,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "aiohttp",
"specs": [
[
"<",
"4.0"
],
[
">=",
"3.0.1"
]
]
},
{
"name": "apispec",
"specs": [
[
">=",
"5.1.1"
]
]
},
{
"name": "webargs",
"specs": [
[
">=",
"8.0.1"
]
]
},
{
"name": "jinja2",
"specs": []
}
],
"lcname": "aiohttp-apispec"
}