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
This FastAPI application provides a secure user registration system with the following features:
/register) - Accepts user details with robust validation:
/docspip install fastapi uvicorn
Save the code in main.py
uvicorn main:app --reload
http://127.0.0.1:8000/docs to interact with the APIRegister 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.