Decorator Reference

The core functionality of APIFairy is accessed through its five decorators, which are used to define what the inputs and outputs of each endpoint are.

@arguments

The arguments decorator specifies input arguments, given in the query string of the request URL. The only argument this decorator requires is the schema definition for the input data, which can be given as a schema class or instance:

from apifairy import arguments

class PaginationSchema(ma.Schema):
   page = ma.Int(missing=1)
   limit = ma.Int(missing=10)

@app.route('/api/user/<int:id>/followers')
@arguments(PaginationSchema)
def get_followers(pagination, id):
    page = pagination['page']
    limit = pagination['limit']
    # ...

The decorator will deserialize and validate the input data and will only invoke the view function when the arguments are valid. In the case of a validation error, the error handler is invoked to generate an error response to the client.

The deserialized input data is passed to the view function as a positional argument. Note that Flask passes path arguments as keyword arguments, so the argument from this decorator must be defined first, as seen in the example above. When multiple input decorators are used, the positional arguments are given in the same order as the decorators.

Using multiple inputs

The arguments decorator can be given multiple times, but in that case the schemas must have their unknown attribute set to EXCLUDE, so that the arguments from the different schemas are assigned properly:

class PaginationSchema(ma.Schema):
    page = ma.Int(missing=1)
    limit = ma.Int(missing=10)

class FilterSchema(ma.Schema):
    f = ma.Str()

@app.route('/api/user/<int:id>/followers')
@arguments(PaginationSchema(unknown=ma.EXCLUDE))
@arguments(FilterSchema(unknown=ma.EXCLUDE))
def get_followers(pagination, filter, id):
    page = pagination['page']
    limit = pagination['limit']
    f = filter.get('filter')
    # ...

Note that in this example the filter argument does not have missing or required attributes, so it will be considered optional. If the query string does not include it, then filter will be empty.

Lists

A list can be defined in the usual way using Marshmallow’s List field:

class Filter(ma.Schema):
    f = ma.List(ma.Str())

@app.route('/test')
@arguments(Filter())
def test(filter):
    f = filter.get('f', [])
    # ...

The client then must repeat the argument as many times as needed in the query string. For example, the URL http://localhost:5000/test?f=foo&f=bar would set the filter argument to {'f': ['foo', 'bar']}.

Advanced Usage

The arguments decorator is a thin wrapper around the use_args decorator from the webargs project with the location argument set to query. Any additional options are passed directly into use_args, which among other things allow the use of other locations for input arguments besides the query string.

@body

The body decorator defines the structure of the body of the request. The only required argument to this decorator is the schema definition for the request body, which can be given as a schema class or instance:

from apifairy import body

class UserSchema(ma.Schema):
    id = ma.Int()
    username = ma.Str(required=True)
    email = ma.Str(required=True)
    about_me = ma.Str(missing='')

@app.route('/users', methods=['POST'])
@body(UserSchema)
def create_user(user):
    # ...

The decorator will deserialize and validate the input data and will only invoke the view function when the data passes validation. In the case of a validation error, the error handler is invoked to generate an error response to the client.

The deserialized input data is passed to the view function as a positional argument. Note that Flask passes path arguments as keyword arguments, so the argument from this decorator must be defined first. When multiple input decorators are used, the positional arguments are given in the same order as the decorators.

Forms

This decorator can also be used to configure an endpoint to accept form data, by adding the optional location argument set to form:

from apifairy import body

class UserSchema(ma.Schema):
    id = ma.Int()
    username = ma.Str(required=True)
    email = ma.Str(required=True)
    about_me = ma.Str(missing='')

@app.route('/users', methods=['POST'])
@body(UserSchema, location='form')
def create_user(user):
    # ...

File uploads can be declared with the FileField field type, which returns a standard FileStorage object from Flask:

from apifairy import body
from apifairy.fields import FileField

class UserSchema(ma.Schema):
    id = ma.Int()
    username = ma.Str(required=True)
    avatar = FileField()

@app.route('/users', methods=['POST'])
@body(UserSchema, location='form')
def create_user(user):
    # ...

The FileField field type can also be combined with Marshmallow’s List to accept a list of files. But for this to work, the media_type argument needs to be added to the @body decorator to ensure that the request is parsed as a multipart form:

from apifairy import body
from apifairy.fields import FileField

class UserSchema(ma.Schema):
    id = ma.Int()
    username = ma.Str(required=True)
    files = ma.List(FileField())

@app.route('/users', methods=['POST'])
@body(UserSchema, location='form', media_type='multipart/form-data')
def create_user(user):
    # ...

Advanced Usage

The body decorator is a thin wrapper around the use_args decorator from the webargs project with the location argument set to json or form. Any additional options are passed directly into use_args.

@response

The response decorator specifies the structure of the endpoint response. The only required argument to this decorator is the schema that defines the response, which can be given as a schema class or instance:

from apifairy import response

@app.route('/users/<int:id>')
@response(UserSchema)
def get_user(id):
    return User.query.get_or_404(id)

The decorator performs the serialization of the returned object or dictionary to JSON through the schema’s jsonify() method.

This decorator accepts two optional arguments. The status_code argument is used to specify the HTTP status code for the response, when it is not the default of 200. The description argument is used to provide a text description of this response to be added to the documentation:

@app.route('/users', methods=['POST'])
@body(UserSchema)
@response(UserSchema, status_code=201, description='A user was created.')
def create_user(user):
    # ...

@other_responses

The other_responses decorator is used to specify additional responses the endpoint can return, usually as a result of an error condition. The only argument to this decorator is a dictionary with the keys set to numeric HTTP status codes. In its simplest form, the values of the dictionary are strings that describe each response:

from apifairy import response, other_responses

@app.route('/users/<int:id>')
@response(UserSchema)
@other_responses({400: 'Invalid request.', 404: 'User not found.'})
def get_user(id):
    # ...

If desired a schema can be provided for each response instead:

from apifairy import response, other_responses

@app.route('/users/<int:id>')
@response(UserSchema)
@other_responses({400: BadRequestSchema, 404: UserNotFoundSchema})
def get_user(id):
    # ...

Finally, a schema and a description can both be given as a tuple:

from apifairy import response, other_responses

@app.route('/users/<int:id>')
@response(UserSchema)
@other_responses({400: (BadRequestSchema, 'Invalid request.'),
                  404: (UserNotFoundSchema, 'User not found.')})
def get_user(id):
    # ...

This decorator does not perform any validation or formatting of error responses, it just adds the information provided to the documentation.

@authenticate

The authenticate decorator is used to specify the authentication and authorization requirements of the endpoint. The only required argument for this decorator is an authentication object from the Flask-HTTPAuth extension:

from flask_httpauth import HTTPBasicAuth
from apifairy import authenticate

auth = HTTPBasicAuth()

@app.route('/users/<int:id>')
@authenticate(auth)
@response(UserSchema)
def get_user(id):
    return User.query.get_or_404(id)

The decorator invokes the login_required method of the authentication object, and also adds an Authentication section to the documentation.

If the roles feature of Flask-HTTPAuth is used, the documentation will include the required role(s) for each endpoint. Any keyword arguments given to the authenticate decorator, including the role argument, are passed through to Flask-HTTPAuth.

@webhook

The webhook decorator is used to document a webhook, which is an endpoint that must be implemented by the API client for the server to invoke as a callback or notification. OpenAPI added support for webhooks in its 3.1.0 version.

Webhooks are defined with a dummy function that is never invoked. After the webhook decorator is applied, the arguments, body, response and other_responses decorators can be used to document the inputs and outputs.

Example:

from apifairy import webhook, body

@webhook
@body(ResultsSchema)
def results():
    pass

The webhook decorator accepts three optional arguments. The method argument is used to specify the HTTP method that the server will use to invoke the webhook. If this argument is not specified, GET is used.

The blueprint argument is used to optionally specify a blueprint with which this webhook should be grouped. This adds the a tag with the blueprint’s name, which will make most documentation renderers add the webhook definition in the same section as the endpoints in the blueprint.

The endpoint argument can be used to explicitly provide the endpoint name under which the webhook should be documented. If this argument is not given, the endpoint name is the name of the webhook function.

The next example shows webhook definition using a POST HTTP method, added to a users blueprint:

from apifairy import webhook, body

@webhook(method='POST', blueprint=users)
@body(ResultsSchema)
def results():
    pass