DEV Community

A0mineTV
A0mineTV

Posted on

πŸš€ Validating User Input with FastAPI: An Example with Custom Validators

When building an API, validating user input is critical for maintaining data integrity and providing a great user experience. With FastAPI and Pydantic, you can easily validate incoming data and provide clear feedback to users.

In this article, we'll build a simple User Management API with custom validators for:

  • Ensuring the email field is a valid email.
  • Checking that name contains at least 3 characters.
  • Verifying that age is at least 18 years.

Let's dive in! πŸŠβ€β™‚οΈ


πŸ— Prerequisites

Before you start, ensure you have the following:

  • Python 3.8+
  • pip (Python package manager)

Install the required libraries:

pip install fastapi uvicorn pydantic
Enter fullscreen mode Exit fullscreen mode

πŸ“œ The API Features

We'll build an API that:

  1. Accepts user data: Name, Email, and Age.
  2. Validates input data:
    • Name must have at least 3 characters.
    • Email must be a valid email address.
    • Age must be 18 or older.
  3. Stores users: In-memory for simplicity.
  4. Provides feedback: Returns clear error messages when validation fails.

πŸ›  Code Implementation

1️⃣ Define the User Model with Validators

We’ll use Pydantic to define the input model and implement custom validation.

from pydantic import BaseModel, EmailStr, validator

class UserCreate(BaseModel):
    name: str
    email: EmailStr  # Automatically validates email format
    age: int

    # Validator to ensure the name has at least 3 characters
    @validator("name")
    def name_must_be_valid(cls, value):
        if len(value) < 3:
            raise ValueError("Name must contain at least 3 characters.")
        return value

    # Validator to ensure the age is at least 18
    @validator("age")
    def age_must_be_adult(cls, value):
        if value < 18:
            raise ValueError("Age must be at least 18 years old.")
        return value
Enter fullscreen mode Exit fullscreen mode

2️⃣ Initialize the FastAPI App and User Store

We'll create an in-memory list to store users and define the API endpoints.

from fastapi import FastAPI, HTTPException
from typing import List

app = FastAPI()

# In-memory user storage
users = []

@app.post("/users/", response_model=UserCreate)
def create_user(user: UserCreate):
    # Check if email is already registered
    if any(u['email'] == user.email for u in users):
        raise HTTPException(status_code=400, detail="Email already registered")

    # Add user to the in-memory database
    users.append(user.dict())
    return user


@app.get("/users/", response_model=List[UserCreate])
def get_users():
    return users
Enter fullscreen mode Exit fullscreen mode

Β 3️⃣ Run the Application

Run the app using Uvicorn:

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode

Visit the interactive API documentation at http://127.0.0.1:8000/docs.


🎯 Testing the API

Here’s how you can test the API using cURL or any HTTP client like Postman or Thunder Client.

βœ… Valid Request

curl -X POST "http://127.0.0.1:8000/users/" \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "john@example.com", "age": 25}'
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "name": "John Doe",
  "email": "john@example.com",
  "age": 25
}
Enter fullscreen mode Exit fullscreen mode

❌ Invalid Email

curl -X POST "http://127.0.0.1:8000/users/" \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "invalid-email", "age": 25}'
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "detail": [
    {
      "loc": ["body", "email"],
      "msg": "value is not a valid email address",
      "type": "value_error.email"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

❌ Name Too Short

curl -X POST "http://127.0.0.1:8000/users/" \
-H "Content-Type: application/json" \
-d '{"name": "Jo", "email": "john@example.com", "age": 25}'
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "detail": [
    {
      "loc": ["body", "name"],
      "msg": "Name must contain at least 3 characters.",
      "type": "value_error"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

❌ Age Below 18

curl -X POST "http://127.0.0.1:8000/users/" \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "john@example.com", "age": 15}'
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "detail": [
    {
      "loc": ["body", "age"],
      "msg": "Age must be at least 18 years old.",
      "type": "value_error"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

 ❌ Email Already Registered

First, create a user with the following request:

curl -X POST "http://127.0.0.1:8000/users/" \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "email": "john@example.com", "age": 25}'
Enter fullscreen mode Exit fullscreen mode

Then, try creating another user with the same email:

curl -X POST "http://127.0.0.1:8000/users/" \
-H "Content-Type: application/json" \
-d '{"name": "Jane Doe", "email": "john@example.com", "age": 30}'
Enter fullscreen mode Exit fullscreen mode

Β Response:

{
  "detail": "Email already registered"
}
Enter fullscreen mode Exit fullscreen mode

🌟 Key Features of This Example

  1. Validation with Pydantic:

    • Automatic validation for email format.
    • Custom validators for name and age.
  2. Clear Error Messages:

    • Each validation error is returned with a specific message, making it easier to debug and fix input issues.
  3. Scalable Design:

    • The approach can be extended to include more fields, validations, or even integrate a database.

πŸš€ Wrapping Up

Using FastAPI and Pydantic, you can implement powerful validation logic with minimal effort. This approach not only ensures data integrity but also improves the developer and user experience with clear and actionable error messages.

What custom validators would you add to this API? Share your thoughts in the comments! πŸŽ‰

Happy coding! πŸ‘¨β€πŸ’»πŸ‘©β€πŸ’»

Top comments (1)

Collapse
 
stevendev0822 profile image
Steven

Thanks for sharing article.