API Schemas

Flama includes optional support for generating OpenAPI schemas, using apispec and pyyaml libraries.

The Schema generator gathers all the API information needed directly from your code and infers the schema that represents your API based on OpenAPI standard. The schema will be also served under /schema/ route by default, but it is absolutety configurable.

The Schema Generation

Let's take a look at how the API schema is generated with an example that includes all the pieces involved in the process.

The API used for this example will consist of a collection of puppies and some methods for handling it.

Data Schemas

First we define the data schemas that will be used to validate the inputs in our API.

In our case we only need a single schema for defining a Puppy with a small set of attributes.

from marshmallow import Schema, fields, validate


class Puppy(Schema):
    id = fields.Integer()
    name = fields.String()
    age = fields.Integer(validate=validate.Range(min=0))

Routes

Now that we have a proper input validation we can use those schemas to define the functions associated to each route in our API.

In that case we are going to define a couple of endpoints, a first one for listing a collection and a second for creating a new resource in that collection.

Our goal is to describe our API using a schema inferred directly from our codebase so our functions needs to be well documented using docstrings, that will be appended to our API schema following OpenAPI format.

Regarding to our data schemas previously defined and used as annotations in our functions, it will be automatically inspected and will be part of the information of our endpoints.

from . import schemas


def list_puppies(name: str = None) -> schemas.Puppy(many=True):
    """
    tags:
        - puppy
    summary:
        List puppies.
    description:
        List the puppies collection. There is an optional query parameter that 
        specifies a name for filtering the collection based on it.
    responses:
        200:
            description: List puppies.
    """
    ...


def create_puppy(puppy: schemas.Puppy) -> schemas.Puppy:
    """
    tags:
        - puppy
    summary:
        Create a new puppy.
    description:
        Create a new puppy using data validated from request body and add it 
        to the collection.
    responses:
        200:
            description: Puppy created successfully.
    """
    ...

Application

The last step is to define our main application and configure the highest level information of our schema, such as the title, version and a description, as well as the path use to serve it.

from flama.applications import Flama

from . import views


app = Flama(
    title="Puppy Register",               # API title
    version="0.1",                        # API version
    description="A register of puppies",  # API description
    schema="/schema/",                    # Path to expose OpenAPI schema
)


app.add_route("/", views.list_puppies, methods=["GET"])
app.add_route("/", views.create_puppy, methods=["POST"])

Schema

And here is the result schema of our example.

components:
  schemas:
    APIError:
      properties:
        detail:
          description: Error detail
          title: detail
          type: string
        error:
          description: Exception or error type
          title: type
          type: string
        status_code:
          description: HTTP status code
          format: int32
          title: status_code
          type: integer
      required:
      - detail
      - status_code
      type: object
    Puppy:
      properties:
        age:
          format: int32
          minimum: 0
          type: integer
        id:
          format: int32
          type: integer
        name:
          type: string
      type: object
info:
  description: A register of puppies
  title: Puppy Register
  version: '0.1'
openapi: 3.0.0
paths:
  /:
    get:
      description: List the puppies collection. There is an optional query parameter
        that specifies a name for filtering the collection based on it.
      parameters:
      - in: query
        name: name
        required: false
        schema:
          default: null
          nullable: true
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/Puppy'
                type: array
          description: List puppies.
        default:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
          description: Unexpected error.
      summary: List puppies.
      tags:
      - puppy
    post:
      description: Create a new puppy using data validated from request body and add
        it to the collection.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Puppy'
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Puppy'
          description: Puppy created successfully.
        default:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIError'
          description: Unexpected error.
      summary: Create a new puppy.
      tags:
      - puppy

Disable Schema

You can disable the schema generation by using None value for the schema argument.

from flama.applications import Flama

app = Flama(
    title="Puppy Register",               # API title
    version="0.1",                        # API version
    description="A register of puppies",  # API description
    schema=None,                          # Disable api schema generation
)

Swagger UI

Swagger UI is a collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API. It's fully integrated with your application and can be served under an specific path simply configuring a single parameter.

from flama.applications import Flama

app = Flama(
    title="Puppy Register",               # API title
    version="0.1",                        # API version
    description="A register of puppies",  # API description
    schema="/schema/",                    # Path to expose OpenAPI schema
    docs="/docs/",                        # Path to expose SwaggerUI application
)

ReDoc

ReDoc is an OpenAPI/Swagger-generated API Reference Documentation, a well-built application to serve your API docs based on your API schema. It's fully integrated with your application and can be served under an specific path simply configuring a single parameter.

from flama.applications import Flama

app = Flama(
    title="Puppy Register",               # API title
    version="0.1",                        # API version
    description="A register of puppies",  # API description
    schema="/schema/",                    # Path to expose OpenAPI schema
    redoc="/redoc/",                      # Path to expose ReDoc application
)