Response Validation#

When sending a JSON response to the client it makes sense to check that the structure matches a predefined structure. This is a form of Design by Contract and helps ensure that your software works as expected. This is done by validating the response data is correct against a schema you define. Quart-Schema allows validation of JSON data via decorating the route handler, as so,

from attrs import define
from quart_schema import validate_response

@define
class Todo:
    effort: int
    task: str

@app.route("/")
@validate_response(Todo)
async def index():
    return data

will ensure that data represents or is a Todo object, e.g. these responses are allowed. Note that the typical Quart response return values are allowed, including status and headers,

return {"effort": 2, "task": "Finish the docs"}
return Todo(effort=2, task="Finish the docs")
return {"effort": 2, "task": "Finish the docs"}, 200
return {"effort": 2, "task": "Finish the docs"}, {"X-Header": "value"}

Handling multiple/different status codes#

The validate_response() decorator’s second argument is the status code the validation applies too. By default this is assumed to be 200, but can be changed to validate responses that are sent with a different status code.

To validate a route that returns different different responses by status code the decorator can be used multiple times,

@app.route("/")
@validate_response(Todo, 200)
@validate_response(CreatedTodo, 201)
async def index():
    if ...:
        return Todo(), 200
    else:
        return CreatedTodo(), 201

Handling validation errors#

By default if the response result doesn’t satisfy the schema a ResponseSchemaValidationError error is raised and not handled, resulting in a 500 internal server error response. You can alter this by adding an error handler, for example,

from quart_schema import ResponseSchemaValidationError

@app.errorhandler(ResponseSchemaValidationError)
async def handle_response_validation_error():
    return {"error": "VALIDATION"}, 500