Querystring Validation#

The querystring is an optional part of the URL that allows for parameters to be specified. It makes sense to check that they’ve sent it in a format you understand. This is done by validating them against a schema you define. Quart-Schema allows validation via decorating the route handler, as so,

from attrs import define
from quart_schema import validate_querystring

@define
class Query:
    count_le: int | None = None
    count_gt: int | None = None

@app.route("/")
@validate_querystring(Query)
async def index(query_args: Query):
    ...

this will allow the client to add a count_le, count_gt, or both parameters to the URL e,g. /?count_le=2&count_gt=0.

If the client doesn’t supply correctly structured data they will receive a 400 (bad request) response without your route handler running. If the client does supply correctly structured data it will be passed into your route handler as the query_args argument.

Note

Querystring parameters must be optional defaulting to None (as querystrings are optional).

Handling validation errors#

By default if the client sends a body that doesn’t satisfy the schema a 400 bad request response will be sent. You can alter this by adding an error handler, for example for a JSON error response,

from quart_schema import RequestSchemaValidationError

@app.errorhandler(RequestSchemaValidationError)
async def handle_request_validation_error():
    return {"error": "VALIDATION"}, 400

List values#

You may want to allow a repeated, multiple, or list query string parameter e.g. /?key=foo&key=bar. Which can be done using list[str] for example.

Care must be taken for the case where only a single parameter is given (as this is not as list). In this situation you can either expand the type to list[str] | str for example, or to convert the single value to a list using a BeforeValidator,

from typing import Annotated

from pydantic import BaseModel
from pydantic.functional_validators import BeforeValidator
from quart_schema import validate_querystring

def _to_list(value: str | list[str]) -> list[str]:
    if isinstance(value, list):
        return value
    else:
        return [value]

class Query(BaseModel):
    keys: Annotated[Optional[List[str]], BeforeValidator(_to_list)] = Non

@app.route("/")
@validate_querystring(Query)
async def index(query_args: Query):
    ...

Warning

This currently only works with Pydantic types and validation.