FastAPI has become a popular choice for building APIs, largely due to its foundational architecture atop the Starlette framework and Pydantic library. While it provides impressive speed and an array of features, one of its standout functionalities is robust request validation using Pydantic.

However, sometimes the out-of-the-box validation mechanisms aren’t enough to cover more complex requirements, such as conditional validation. Fortunately, FastAPI provides powerful tools for extending these capabilities, like Field and Model validators.

Although FastAPI’s built in (Pydantic) validation is enough in most cases, occassionally additional validation is required - for example, when dealing conditional validation. Fortunately, this can be achieved using Field and Model validators.

However, the default behaviour of FastAPI (Pydantic) in a custom validator is to surface an exception immediately after encountering it. When building a backend for a web service, etc., it can be useful to collect all errors before returning to the user. To validate everything before returning we can utilise a model_validator with mode='wrap', and validation_error.from_exception_data().

Solution

Obvious improvement can be made to the following code - the use of enums for height-type, for example. However, this is a bare-bones example of how to collect and return validation errors in one go.

When making a request that contains only height_type='cms', the following is returned.

{
  "detail": [
    {
      "type": "missing",
      "loc": [
        "body",
        "height_cms"
      ],
      "msg": "Field required",
      "input": null,
      "ctx": {},
      "url": "https://errors.pydantic.dev/2.3/v/missing"
    },
    {
      "type": "missing",
      "loc": [
        "body",
        "gender"
      ],
      "msg": "Field required",
      "input": {
        "height_type": "cms"
      },
      "url": "https://errors.pydantic.dev/2.3/v/missing"
    }
  ]
}