Python Snippets

FastAPI Application with Pydantic Validation for User Registration

This code snippet demonstrates a modern FastAPI application implementing user registration with Pydantic validation, password hashing, and secure practices. It’s useful for building secure web APIs with automatic API documentation.

from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field, validator
import hashlib
import secrets
from datetime import datetime
from typing import Optional

app = FastAPI(title="User Registration API", version="1.0.0")

# In-memory storage (use a database in production)
users_db = {}

class UserRegistration(BaseModel):
    username: str = Field(..., min_length=3, max_length=20, pattern=r"^[a-zA-Z0-9_]+$")
    email: str = Field(..., pattern=r"^[^@]+@[^@]+\.[^@]+$")
    password: str = Field(..., min_length=8)
    full_name: Optional[str] = Field(None, max_length=50)
    
    @validator('password')
    def validate_password(cls, v):
        if not any(c.isupper() for c in v):
            raise ValueError('Password must contain at least one uppercase letter')
        if not any(c.islower() for c in v):
            raise ValueError('Password must contain at least one lowercase letter')
        if not any(c.isdigit() for c in v):
            raise ValueError('Password must contain at least one digit')
        return v

class UserResponse(BaseModel):
    username: str
    email: str
    full_name: Optional[str]
    created_at: datetime

def hash_password(password: str, salt: str) -> str:
    """Hash password with salt using SHA-256"""
    return hashlib.sha256((password + salt).encode()).hexdigest()

@app.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def register_user(user: UserRegistration):
    # Check if username or email already exists
    if user.username in users_db:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Username already registered"
        )
    
    for existing_user in users_db.values():
        if existing_user["email"] == user.email:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Email already registered"
            )
    
    # Hash password with salt
    salt = secrets.token_hex(16)
    hashed_password = hash_password(user.password, salt)
    
    # Store user (in production, use a real database)
    user_id = len(users_db) + 1
    users_db[user.username] = {
        "id": user_id,
        "username": user.username,
        "email": user.email,
        "full_name": user.full_name,
        "hashed_password": hashed_password,
        "salt": salt,
        "created_at": datetime.now()
    }
    
    return UserResponse(
        username=user.username,
        email=user.email,
        full_name=user.full_name,
        created_at=users_db[user.username]["created_at"]
    )

@app.get("/users/{username}", response_model=UserResponse)
async def get_user(username: str):
    if username not in users_db:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found"
        )
    user = users_db[username]
    return UserResponse(
        username=user["username"],
        email=user["email"],
        full_name=user["full_name"],
        created_at=user["created_at"]
    )

# For development only - DO NOT expose this in production
@app.get("/debug/users")
async def debug_list_users():
    return users_db

What This Code Does

This FastAPI application provides a secure user registration system with the following features:

  1. User Registration Endpoint (/register) - Accepts user details with robust validation:
    • Username must be 3-20 characters, alphanumeric with underscores only
    • Email must follow standard format
    • Password must be at least 8 characters with uppercase, lowercase, and digit
    • Optional full name field with max length
  2. Pydantic Validation - Automatic request validation and clear error messages:
    • Field constraints ensure data quality
    • Custom password validator enforces security requirements
    • Automatic OpenAPI/Swagger documentation generation
  3. Security Features:
    • Password hashing with salt using SHA-256
    • Duplicate username/email checking
    • Secure random salt generation for each user
  4. Additional Endpoints:
    • User lookup by username
    • Debug endpoint to view all users (development only)

Why This is Useful

How to Run

  1. Install dependencies:
    pip install fastapi uvicorn
    
  2. Save the code in main.py

  3. Run the server:
    uvicorn main:app --reload
    
  4. Visit http://127.0.0.1:8000/docs to interact with the API

Example Usage

Register a new user:

POST /register
{
  "username": "johndoe",
  "email": "john@example.com",
  "password": "SecurePass123",
  "full_name": "John Doe"
}

Retrieve user details:

GET /users/johndoe

This snippet provides a solid foundation for building secure web applications with modern Python practices. For production use, replace the in-memory storage with a proper database and add authentication middleware.