Skip to main content

generate OpenAPI document and validate request&response with Python annotations.

Project description

Spectree

GitHub Actions pypi versions Language grade: Python Python document

Yet another library to generate OpenAPI document and validate request & response with Python annotations.

Features

  • Less boilerplate code, only annotations, no need for YAML :sparkles:
  • Generate API document with Redoc UI or Swagger UI :yum:
  • Validate query, JSON data, response data with pydantic :wink:
  • Current support:

Quick Start

install with pip: pip install spectree

Examples

Check the examples folder.

Step by Step

  1. Define your data structure used in (query, json, headers, cookies, resp) with pydantic.BaseModel
  2. create spectree.SpecTree instance with the web framework name you are using, like api = SpecTree('flask')
  3. api.validate decorate the route with
    • query
    • json
    • headers
    • cookies
    • resp
    • tags
  4. access these data with context(query, json, headers, cookies) (of course, you can access these from the original place where the framework offered)
    • flask: request.context
    • falcon: req.context
    • starlette: request.context
  5. register to the web application api.register(app)
  6. check the document at URL location /apidoc/redoc or /apidoc/swagger

If the request doesn't pass the validation, it will return a 422 with JSON error message(ctx, loc, msg, type).

How To

How to add summary and description to endpoints?

Just add docs to the endpoint function. The 1st line is the summary, and the rest is the description for this endpoint.

How to add description to parameters?

Check the pydantic document about description in Field.

Any config I can change?

Of course. Check the config document.

You can update the config when init the spectree like:

SpecTree('flask', title='Demo API', version='v1.0', path='doc')

What is Response and how to use it?

To build a response for the endpoint, you need to declare the status code with format HTTP_{code} and corresponding data (optional).

Response(HTTP_200=None, HTTP_403=ForbidModel)
Response('HTTP_200') # equals to Response(HTTP_200=None)

What should I return when I'm using the library?

No need to change anything. Just return what the framework required.

How to logging when the validation failed?

Validation errors are logged with INFO level. Details are passed into extra. Check the falcon example for details.

How can I write a customized plugin for another backend framework?

Inherit spectree.plugins.base.BasePlugin and implement the functions you need. After that, init like api = SpecTree(backend=MyCustomizedPlugin).

How can I change the response when there is a validation error? Can I record some metrics?

This library provides before and after hooks to do these. Check the doc or the test case. You can change the handlers for SpecTree or for a specific endpoint validation.

Demo

Try it with http post :8000/api/user name=alice age=18. (if you are using httpie)

Flask

from flask import Flask, request, jsonify
from pydantic import BaseModel, Field, constr
from spectree import SpecTree, Response


class Profile(BaseModel):
    name: constr(min_length=2, max_length=40) # Constrained Str
    age: int = Field(
        ...,
        gt=0,
        lt=150,
        description='user age(Human)'
    )

    class Config:
        schema_extra = {
            # provide an example
            'example': {
                'name': 'very_important_user',
                'age': 42,
            }
        }


class Message(BaseModel):
    text: str


app = Flask(__name__)
api = SpecTree('flask')


@app.route('/api/user', methods=['POST'])
@api.validate(json=Profile, resp=Response(HTTP_200=Message, HTTP_403=None), tags=['api'])
def user_profile():
    """
    verify user profile (summary of this endpoint)

    user's name, user's age, ... (long description)
    """
    print(request.context.json) # or `request.json`
    return jsonify(text='it works')


if __name__ == "__main__":
    api.register(app) # if you don't register in api init step
    app.run(port=8000)

Falcon

import falcon
from wsgiref import simple_server
from pydantic import BaseModel, Field, constr
from spectree import SpecTree, Response


class Profile(BaseModel):
    name: constr(min_length=2, max_length=40)  # Constrained Str
    age: int = Field(
        ...,
        gt=0,
        lt=150,
        description='user age(Human)'
    )


class Message(BaseModel):
    text: str


api = SpecTree('falcon')


class UserProfile:
    @api.validate(json=Profile, resp=Response(HTTP_200=Message, HTTP_403=None), tags=['api'])
    def on_post(self, req, resp):
        """
        verify user profile (summary of this endpoint)

        user's name, user's age, ... (long description)
        """
        print(req.context.json)  # or `req.media`
        resp.media = {'text': 'it works'}


if __name__ == "__main__":
    app = falcon.API()
    app.add_route('/api/user', UserProfile())
    api.register(app)

    httpd = simple_server.make_server('localhost', 8000, app)
    httpd.serve_forever()

Starlette

import uvicorn
from starlette.applications import Starlette
from starlette.routing import Route, Mount
from starlette.responses import JSONResponse
from pydantic import BaseModel, Field, constr
from spectree import SpecTree, Response


class Profile(BaseModel):
    name: constr(min_length=2, max_length=40)  # Constrained Str
    age: int = Field(
        ...,
        gt=0,
        lt=150,
        description='user age(Human)'
    )


class Message(BaseModel):
    text: str


api = SpecTree('starlette')


@api.validate(json=Profile, resp=Response(HTTP_200=Message, HTTP_403=None), tags=['api'])
async def user_profile(request):
    """
    verify user profile (summary of this endpoint)

    user's name, user's age, ... (long description)
    """
    print(request.context.json)  # or await request.json()
    return JSONResponse({'text': 'it works'})


if __name__ == "__main__":
    app = Starlette(routes=[
        Mount('api', routes=[
            Route('/user', user_profile, methods=['POST']),
        ])
    ])
    api.register(app)

    uvicorn.run(app)

FAQ

ValidationError: missing field for headers

The HTTP headers' keys in Flask are capitalized, in Falcon are upper cases, in Starlette are lower cases. You can use pydantic.root_validators(pre=True) to change all the keys into lower cases or upper cases.

ValidationError: value is not a valid list for query

Since there is no standard for HTTP query with multiple values, it's hard to find the way to handle this for different web frameworks. So I suggest not to use list type in query until I find a suitable way to fix it.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spectree-0.3.15.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

spectree-0.3.15-py3-none-any.whl (23.7 kB view details)

Uploaded Python 3

File details

Details for the file spectree-0.3.15.tar.gz.

File metadata

  • Download URL: spectree-0.3.15.tar.gz
  • Upload date:
  • Size: 19.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.54.1 CPython/3.8.6

File hashes

Hashes for spectree-0.3.15.tar.gz
Algorithm Hash digest
SHA256 596655aa6dae4b0cde445f17ba53d1a18aa2380f9615161cdf6c2224fe862e87
MD5 109664eed4d3cd24466f64cec9967c88
BLAKE2b-256 3cf9e6e59276e78a4f18a721a3265994d1c53d8c116f7a948e669d29db7b6135

See more details on using hashes here.

File details

Details for the file spectree-0.3.15-py3-none-any.whl.

File metadata

  • Download URL: spectree-0.3.15-py3-none-any.whl
  • Upload date:
  • Size: 23.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.54.1 CPython/3.8.6

File hashes

Hashes for spectree-0.3.15-py3-none-any.whl
Algorithm Hash digest
SHA256 362b94c0233c845b44d0ed389456082988143f8616fb4a629faff46f5d4693a2
MD5 2a2d3dcf9f631d29862936a38060a3f6
BLAKE2b-256 d6985b457e72a23e4a9909316d5eccb2b803bced093bdb7482a716da9975a7b4

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page