FastAPI Custom Validations Using Pydantic

fastapi custom validators

FastAPI, a modern, high-performance web framework for building APIs with Python, leverages Pydantic for robust data validation and serialization. Pydantic’s powerful type-hinting and validation capabilities make it an ideal choice for ensuring data integrity in FastAPI applications. While basic validations like type checking and required fields are straightforward, custom validations allow developers to enforce complex business rules and constraints. In this article, we’ll explore how to implement custom validations in FastAPI using Pydantic.

Introduction to FastAPI and Pydantic

FastAPI is built on top of Starlette and Pydantic, combining asynchronous programming with automatic data validation and OpenAPI documentation. Pydantic, a data validation library, uses Python type annotations to define data models and enforce validation rules. It integrates seamlessly with FastAPI, enabling developers to define request and response models with built-in validation.

While Pydantic provides out-of-the-box validations for common scenarios (e.g., string length, number ranges, or email formats), advanced use cases often require custom validation logic. For example, you might need to validate that a user’s input adheres to specific business rules, such as ensuring a date range is valid or that a username is unique in a database.

In this article, we’ll cover:

  • Setting up a FastAPI project with Pydantic
  • Creating custom validators with Pydantic
  • Using @model_validator for cross-field validations
  • Implementing custom validation classes
  • Handling complex business logic with external dependencies
  • Best practices and error handling

Setting Up a FastAPI Project with Pydantic

Before diving into custom validations, let’s set up a basic FastAPI project. Ensure you have Python 3.7+ installed, then install the required dependencies:

Here’s a minimal FastAPI application to get started:

Creating Custom Validators with Pydantic

Pydantic allows you to define custom validators using the @validator decorator. This is useful for validating individual fields with custom logic. Let’s extend the Item model to enforce that the price is positive and the name is not a reserved keyword:

In this example:

  • The @validator(“price”) ensures the price field is positive.
  • The @validator(“name”) checks that the name is not in a list of reserved keywords.
  • If validation fails, Pydantic raises a ValueError, which FastAPI converts into a 422 Unprocessable Entity response with a detailed error message.

Try sending a request with curl or Postman:

The response will indicate validation errors for both name and price.

Cross-Field Validations with @model_validator

Sometimes, validation logic depends on multiple fields. For example, you might want to ensure that the quantity is provided only if the price is above a certain threshold. Pydantic’s @model_validator allows you to validate the entire model.

Here’s an example:

In this example:

  • The @model_validator checks if the price exceeds 100 and ensures quantity is greater than 10 is such cases.
  • The ‘self’ contains all fields.
  • The validator returns ‘self’.

This approach is powerful for enforcing complex business rules that involve multiple fields.

Implementing Custom Validation Classes

For reusable or complex validation logic, you can create custom Pydantic types. Suppose you want to validate that a field contains a valid ISBN-13 number. You can define a custom type with its own validation logic.

To test, after running the project, execute this command:

In this example:

  • The ISBN13 class inherits from str and implements custom validation logic.
  • The __get_validators__ method registers the validate method as a Pydantic validator.
  • The validate method checks the ISBN-13 format and checksum.
  • The Book model uses ISBN13 as a type for the isbn field.

This approach is ideal for reusable validations that can be applied across multiple models.

Best Practices for Custom Validations

  1. Keep Validators Focused: Each validator should handle a specific rule to maintain clarity and reusability.
  2. Use Descriptive Error Messages: Clear error messages help API consumers understand what went wrong.
  3. Leverage @model_validator for Cross-Field Logic: Use @model_validator when validation depends on multiple fields to avoid redundant checks.
  4. Handle Edge Cases: Account for None values, type mismatches, and unexpected inputs in your validators.
  5. Reuse Custom Types: Create custom Pydantic types for reusable validations like ISBN, phone numbers, or custom formats.
  6. Test Validators Thoroughly: Write unit tests for your Pydantic models to ensure validation logic works as expected.

Conclusion

Custom validations in FastAPI using Pydantic enable developers to enforce complex business rules with ease. By leveraging @validator, @model_validator, custom types, and dependency injection, you can build robust APIs that ensure data integrity. Whether you’re validating individual fields, cross-field relationships, or external dependencies, Pydantic’s flexibility makes it a powerful tool for FastAPI applications.

By following the best practices outlined in this article, you can create maintainable, reusable, and error-resistant validation logic. Experiment with these techniques in your FastAPI projects to build APIs that are both powerful and reliable.

For further exploration, check out the FastAPI documentation and Pydantic documentation for more advanced features and use cases.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *