NXT Humans Technical Challenge Submission
- Overview
- Architecture Overview
- Prerequisites
- Quick Start
- Environment Variables
- API Documentation
- Known Issues
- Future Improvements
This project is an AI-powered Product Discovery Assistant that demonstrates real-time communication patterns, AI function calling, and context validation. Built for the NXT Humans technical challenge, it showcases production-ready patterns used in AI applications.
Backend:
- FastAPI (Python 3.10+) - Async web framework
- SQLModel + SQLAlchemy - Async ORM with PostgreSQL
- Server-Sent Events - Real-time streaming protocol
- PostgreSQL 15 - Primary database
Frontend:
- React 18 - Modern UI library
- TypeScript 5 - Type-safe JavaScript
- Tailwind CSS - Utility-first CSS framework
- EventSource API - Native SSE support
Infrastructure:
- Docker & Docker Compose - Containerization
- PostgreSQL - Persistent data storage
- Nginx - Frontend static file serving (for production builds)
┌───────────────────────────────────────────────────────────────────┐
│ FRONTEND (React + TypeScript) │
│ │
│ ┌──────────────────┐ ┌────────────────────────────────────────┐ │
│ │ Chat Interface │ │ Dynamic Components │ │
│ │ ─────────────── │ │ ───────────────────────────────────── │ │
│ │ • SSE Hook │ │ • SearchResults (Product Grid) │ │
│ │ • Message List │ │ • ProductCard (Detail View) │ │
│ │ • Message Input │ │ • CartView (Shopping Cart) │ │
│ │ • Status Bar │ │ • RecommendationGrid (Suggestions) │ │
│ └──────────────────┘ └────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Custom Hooks & Context │ │
│ │ • useSSEConnection - EventSource management │ │
│ │ • AppStateContext - Global state (cart, session) │ │
│ │ • ErrorBoundary - Graceful error handling │ │
│ └──────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘
↕ HTTP/SSE
┌───────────────────────────────────────────────────────────────────┐
│ BACKEND (FastAPI + Python) │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ SSE Stream Handler (main.py) │ │
│ │ ───────────────────────────────────────────────────────── │ │
│ │ • Event streaming with yield │ │
│ │ • Connection management & cleanup │ │
│ │ • Text chunking with realistic delays │ │
│ │ • Function call orchestration │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ AI Agent Layer │ │ Context Manager │ │ Product Service │ │
│ │ ───────────── │ │ ────────────── │ │ ─────────────── │ │
│ │ • Function │ │ • Validation │ │ • Search Engine │ │
│ │ calling │ │ • Suggestions │ │ • Fuzzy Match │ │
│ │ • Mock/Real AI │ │ • Context Track │ │ • Cart Ops │ │
│ │ • Streaming │ │ • Cleanup │ │ • TTL Cache │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Database Layer (SQLModel + Async SQLAlchemy) │ │
│ │ • Product catalog with specifications │ │
│ │ • SearchContext for validation │ │
│ │ • SessionContext for user state │ │
│ │ • CartItem for shopping cart │ │
│ └──────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘
↕ Async Driver
┌───────────────────────────────────────────────────────────────────┐
│ DATABASE (PostgreSQL 15) │
│ │
│ Tables: product, search_context, session_context, cart_item │
│ Indexes: product.name, product.category, product.in_stock │
│ Features: JSONB columns, full-text search ready │
└───────────────────────────────────────────────────────────────────┘
1. User Sends Message:
User Input → ChatInterface → useSSEConnection.sendMessage()
→ POST /api/chat/{session_id}/message → Backend queue
2. SSE Stream Response:
Backend → SSE Event Stream → EventSource → useSSEConnection.handleXXX()
→ State Update → React Re-render → UI Update
3. Function Call Flow:
AI Decision → Function Call Event → FunctionCallRenderer
→ Component Mapping → Dynamic Component → User Interaction
→ New Message → Cycle Repeats
| Software | Version | Purpose |
|---|---|---|
| Docker | 20.10+ | Container runtime |
| Docker Compose | 2.0+ | Multi-container orchestration |
| Git | 2.30+ | Version control |
| Software | Version | Purpose |
|---|---|---|
| Python | 3.10+ | Backend development |
| Node.js | 18+ | Frontend development |
| PostgreSQL | 15+ | Local database |
- RAM: 4GB minimum, 8GB recommended
- Disk: 2GB free space for Docker images
- OS: Linux, macOS, or Windows 10/11 with WSL2
# Clone the repository
git clone <your-repository-url>
cd ai-product-assistant
# Copy environment configuration
cp .env.example .env
# Edit .env with your preferences (optional - defaults work)# Start all services in detached mode
docker-compose up -d
# View logs
docker-compose logs -f
# Check service health
docker-compose psExpected output:
NAME STATUS PORTS
assistant-backend Up (healthy) 0.0.0.0:8000->8000/tcp
assistant-frontend Up (healthy) 0.0.0.0:3000->80/tcp
assistant-db Up (healthy) 0.0.0.0:5432->5432/tcp
| Service | URL | Description |
|---|---|---|
| Frontend | http://localhost:3000 | Main chat interface |
| Backend API | http://localhost:8000/docs | Interactive API documentation |
| Health Check | http://localhost:8000/health | Service health status |
| Database | postgresql://localhost:5432/assistant | Direct DB access |
Test API Endpoint:
curl http://localhost:8000/healthExpected Response:
{
"status": "healthy",
"timestamp": "2024-01-20T10:30:00Z",
"services": {
"database": "healthy"
},
"version": "1.0.0"
}Create a .env file in the project root with these variables:
# PostgreSQL connection string for async operations
# Format: postgresql+asyncpg://user:password@host:port/database
DATABASE_URL=postgresql+asyncpg://user:password@db:5432/assistant
# Individual database components (used by Docker Compose)
POSTGRES_DB=assistant # Database name
POSTGRES_USER=user # Database user
POSTGRES_PASSWORD=password # Database password (change in production!)Purpose: Configures the PostgreSQL database connection. The +asyncpg driver enables async database operations with SQLAlchemy/SQLModel.
# AI Provider Selection
AI_PROVIDER=simulate # Options: simulate, openai, anthropic
# OpenAI Configuration (if using OpenAI)
OPENAI_API_KEY=sk-... # Your OpenAI API key
AI_MODEL=gpt-3.5-turbo # Model: gpt-3.5-turbo, gpt-4, etc.
# Anthropic Configuration (if using Anthropic)
ANTHROPIC_API_KEY=sk-ant-... # Your Anthropic API key
AI_MODEL=claude-3-haiku # Model: claude-3-haiku, claude-3-sonnetPurpose:
simulate- Uses mock AI responses with realistic delays (no API key needed)openai- Provider scaffold present but not wired to the API in this submission; backend still plans and runs tools. To add real model streaming, wireOpenAIAgent.stream_responseto Chat Completions and keep server‑managed tools.anthropic- Placeholder for future work
# Security
SECRET_KEY=your-secret-key-here
# Purpose: Used for session signing and CSRF protection
# Environment
ENVIRONMENT=development # Options: development, staging, production
# Purpose: Controls debug features and logging verbosity
# Debugging
DEBUG=true # Set to false in production
# Purpose: Enables detailed error messages and SQL echo
# Logging
LOG_LEVEL=INFO # Options: DEBUG, INFO, WARNING, ERROR, CRITICAL
# Purpose: Controls log verbosity# Allowed Origins (comma-separated)
ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
# Purpose: Prevents unauthorized cross-origin requests
# Rate Limiting
RATE_LIMIT_PER_MINUTE=100 # Max requests per session per minute
MAX_SSE_CONNECTIONS=50 # Max concurrent SSE connections per IP
# Purpose: Prevents abuse and ensures fair resource usage# Session Management
SESSION_TIMEOUT_HOURS=4 # Session expiration time
MAX_CONTEXT_SIZE=100 # Maximum context items to track
# Purpose: Controls session lifecycle and memory usageNote: Context retention and cleanup windows are constants in this submission (configured in code), not environment-driven:
- Recent search validation window: 30 minutes (see
ContextManager.context_window_minutes) - Old search cleanup horizon: 2 hours (see
ContextManager._cleanup_old_search_context)
# API URLs (used by React app)
REACT_APP_API_URL=http://localhost:8000
# Purpose: Backend API base URL for fetch requests
REACT_APP_WS_URL=ws://localhost:8000
# Purpose: WebSocket URL (reserved for future use)# Database Initialization
SEED_ON_STARTUP=false # Auto-seed database on startup
SEED_FROM_SQL=false # Use init.sql for seeding
# Purpose: Controls how sample data is loaded
# SQL Debugging
SQL_ECHO=false # Log all SQL queries
# Purpose: Enables SQLAlchemy query logging for debuggingFastAPI automatically generates interactive API documentation:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Connect to Real-Time Stream
GET /api/stream/{session_id}
Accept: text/event-stream
Cache-Control: no-cache
Connection: keep-aliveResponse Events:
| Event Type | Description | Data Format |
|---|---|---|
text_chunk |
Streaming text response | {"content": "...", "partial": true} |
function_call |
AI function execution | {"function": "search_products", "parameters": {...}, "result": {...}} |
completion |
Turn completed | {"turn_id": "...", "status": "complete"} |
error |
Error occurred | {"error": "message", "code": "ERROR_CODE"} |
connection |
Connection established | {"session_id": "...", "timestamp": "..."} |
POST /api/chat/{session_id}/message
Request Body:
{
"message": "Show me wireless headphones",
"context": {
"previous_search": "electronics"
}
}Response:
{
"success": true,
"message_id": "msg_abc123",
"session_id": "session_abc123"
}To test:
-
Open a new tab and go to http://localhost:8000/api/stream/{session_id} and replace the session id with what you go from GET /api/stream/{session_id}
-
Send a message Swagger: POST /api/chat/{session_id}/message Path param: paste your session_id Body (required): { "message": "headphones under $100", "context": {} } Execute. The POST response is just { success, message_id }.
-
Watch the SSE tab You’ll see event: text_chunk lines (assistant streaming), then event: function_call with JSON results, then event: completion.
POST /api/functions/search_products
Purpose: Search product catalog with fuzzy matching and category filtering
Request Body:
{
"query": "wireless headphones",
"category": "ELECTRONICS",
"limit": 10,
"price_min": 50.0,
"price_max": 300.0,
"session_id": "session_abc123"
}Response:
{
"success": true,
"data": {
"products": [
{
"id": "prod_001",
"name": "Sony WH-1000XM4",
"description": "Premium noise-cancelling headphones",
"price": 349.99,
"category": "ELECTRONICS",
"image_url": "https://...",
"in_stock": true,
"rating": 4.8,
"reviews_count": 1247
}
],
"total_results": 15,
"search_context": {
"query": "wireless headphones",
"category": "ELECTRONICS",
"cached": true
}
},
"context_updated": true
}Features:
- Fuzzy matching with difflib for typo tolerance
- Category filtering (ELECTRONICS, CLOTHING, HOME, BOOKS, SPORTS, BEAUTY)
- Price range filtering
- Results cached for 60 seconds
- Context tracking for validation
POST /api/functions/show_product_details
Purpose: Get detailed product information with validation and recommendations
Request Body:
{
"product_id": "prod_001",
"include_recommendations": true,
"session_id": "session_abc123"
}Response:
{
"success": true,
"data": {
"product": {
"id": "prod_001",
"name": "Sony WH-1000XM4",
"description": "Premium noise-cancelling headphones",
"long_description": "The Sony WH-1000XM4 features...",
"price": 349.99,
"specifications": {
"battery_life": "30 hours",
"weight": "254g",
"connectivity": "Bluetooth 5.0"
},
"features": [
"Active Noise Cancellation",
"Quick Charge",
"Voice Assistant Compatible"
]
},
"recommendations": [
{
"id": "prod_002",
"name": "Bose QuietComfort 45",
"price": 329.99,
"similarity_score": 0.89
}
]
},
"validation": {
"product_exists": true,
"in_recent_search": true,
"context_valid": true
}
}Validation:
- Checks product exists in database
- Validates product_id against recent search context (30-minute window)
- Returns suggestions if validation fails
POST /api/functions/add_to_cart
Purpose: Add product to shopping cart with inventory validation
Request Body:
{
"product_id": "prod_001",
"quantity": 2,
"session_id": "session_abc123"
}Response:
{
"success": true,
"data": {
"cart_item": {
"id": 1,
"product_id": "prod_001",
"product_name": "Sony WH-1000XM4",
"quantity": 2,
"unit_price": 349.99,
"total_price": 699.98,
"added_at": "2024-01-20T10:30:00Z"
},
"cart_summary": {
"total_items": 3,
"total_products": 2,
"subtotal": 899.97,
"estimated_tax": 89.99,
"estimated_total": 989.96
}
},
"validation": {
"product_exists": true,
"sufficient_stock": true,
"valid_quantity": true
}
}Features:
- Inventory validation
- Automatic cart summary calculation
- Tax estimation (10%)
- Duplicate product handling (updates quantity)
POST /api/functions/get_recommendations
Purpose: Get AI-powered product recommendations
Request Body:
{
"based_on": "prod_001",
"recommendation_type": "similar",
"max_results": 5,
"session_id": "session_abc123"
}Response:
{
"success": true,
"data": {
"recommendations": [
{
"id": "prod_002",
"name": "Bose QuietComfort 45",
"price": 329.99,
"category": "ELECTRONICS",
"similarity_score": 0.89,
"reason": "Similar noise cancellation features"
}
],
"recommendation_context": {
"based_on_product": "Sony WH-1000XM4",
"algorithm": "category_based",
"factors": ["category", "price_range", "features"]
}
}
}Algorithm:
- Category-based similarity
- Price range matching
- Feature overlap analysis
- Excludes base product from results
POST /api/sessions
Purpose: Create new user session
Request:
{
"user_id": "user_789",
"context": {}
}Response:
{
"session_id": "session_abc123",
"created_at": "2024-01-20T10:30:00Z",
"expires_at": "2024-01-20T14:30:00Z"
}GET /api/sessions/{session_id}/context
Purpose: Retrieve session context
Response:
{
"session_id": "session_abc123",
"context": {
"search_history": ["wireless headphones", "bluetooth speakers"],
"viewed_products": ["prod_001", "prod_002"],
"cart_items": ["cart_456"]
},
"last_updated": "2024-01-20T10:35:00Z"
}GET /health
Purpose: Service health monitoring
Response:
{
"status": "healthy",
"timestamp": "2024-01-20T10:30:00Z",
"services": {
"database": "healthy"
},
"version": "1.0.0"
}All endpoints return consistent error format:
{
"success": false,
"error": {
"code": "PRODUCT_NOT_FOUND",
"message": "Product with ID 'prod_999' was not found",
"details": {
"product_id": "prod_999",
"suggestions": [
{
"id": "prod_001",
"name": "Sony WH-1000XM4",
"similarity": 0.75
}
]
}
},
"timestamp": "2024-01-20T10:30:00Z"
}Common Error Codes:
| Code | HTTP Status | Description |
|---|---|---|
PRODUCT_NOT_FOUND |
404 | Product doesn't exist |
INVALID_PRODUCT_ID |
400 | Product ID not in search context |
INSUFFICIENT_STOCK |
400 | Not enough inventory |
SESSION_EXPIRED |
401 | Session no longer valid |
VALIDATION_ERROR |
422 | Invalid request parameters |
RATE_LIMIT_EXCEEDED |
429 | Too many requests |
1. Context Window Limited to 30 Minutes
- Issue: Search context expires after 30 minutes
- Impact: Long-running sessions may lose product validation
- Workaround: User must re-search products
- Future Fix: Implement Redis with sliding expiration
2. In-Memory Cache Not Distributed
- Issue: Cache doesn't persist across container restarts
- Impact: Cold start has no cached data
- Workaround: First queries after restart will be slower
- Future Fix: Migrate to Redis for persistent caching
3. No Real-Time Inventory Updates
- Issue: Stock quantities not updated in real-time
- Impact: Race condition possible (two users buy last item)
- Workaround: Manual inventory checks
- Future Fix: WebSocket for inventory updates
4. Limited Fuzzy Search Algorithm
- Issue: Only handles single-word typos
- Impact: Multi-word typos may not be corrected
- Workaround: User must rephrase query
- Future Fix: Implement Levenshtein distance or use PostgreSQL FTS
5. No User Authentication
- Issue: Sessions are cookie-based only
- Impact: Not suitable for production multi-user
- Workaround: Implement JWT or OAuth
- Future Fix: Add authentication middleware
6. Cart Not Persistent
- Issue: Cart lost when session expires
- Impact: User loses cart after 4 hours
- Workaround: Store cart in localStorage
- Future Fix: Persistent cart with user accounts
Tested and Working:
- Chrome 120+
- Firefox 121+
- Edge 120+
- Safari 17+
Known Issues:
- Safari < 15: EventSource reconnection issues
- IE11: Not supported (no EventSource support)
- Chrome < 90: May have SSE bugs
Current Performance:
- Average SSE connection time: <100ms
- Search query response: 50-200ms (cached) / 200-500ms (uncached)
- Function call execution: 100-300ms
- Frontend initial load: <2s
Bottlenecks:
- Fuzzy search token corpus rebuild (60s cache helps)
- Database queries without proper indexes
- Large product catalog (20 products OK, 10k+ would need pagination)
1. Redis Caching Layer
# Replace in-memory cache with Redis
import aioredis
redis = await aioredis.create_redis_pool('redis://redis:6379')
await redis.setex(f"product:{product_id}", 300, json.dumps(product))Benefits:
- Distributed caching across multiple backend instances
- Persistent cache across restarts
- Advanced features (pub/sub for inventory updates)
2. PostgreSQL Full-Text Search
-- Add FTS index
CREATE INDEX product_fts_idx ON product
USING GIN(to_tsvector('english', name || ' ' || description));
-- Query with FTS
SELECT * FROM product
WHERE to_tsvector('english', name || ' ' || description)
@@ to_tsquery('english', 'wireless & headphones');Benefits:
- Better search quality (stemming, stopwords)
- Faster than fuzzy matching
- Relevance ranking
3. User Authentication (JWT)
from fastapi_jwt_auth import AuthJWT
@app.post("/api/login")
async def login(credentials: Credentials, Authorize: AuthJWT = Depends()):
# Verify credentials
access_token = Authorize.create_access_token(subject=user.id)
return {"access_token": access_token}
@app.get("/api/protected")
async def protected(Authorize: AuthJWT = Depends()):
Authorize.jwt_required()
current_user = Authorize.get_jwt_subject()
return {"user": current_user}Benefits:
- Secure multi-user support
- Persistent user sessions
- Authorization for sensitive operations
4. Rate Limiting Middleware
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
@app.post("/api/chat/{session_id}/message")
@limiter.limit("10/minute")
async def send_message(request: Request, session_id: str):
# Handle messageBenefits:
- Prevent abuse
- Fair resource allocation
- API cost control
5. Real-Time Inventory Updates (WebSocket)
from fastapi import WebSocket
@app.websocket("/ws/inventory")
async def inventory_updates(websocket: WebSocket):
await websocket.accept()
async for message in inventory_stream():
await websocket.send_json(message)Benefits:
- Live stock updates
- Real-time price changes
- Collaborative shopping (see other users' activity)
6. Advanced Recommendation Engine
# Collaborative filtering
from sklearn.metrics.pairwise import cosine_similarity
def get_recommendations(user_id: str, n: int = 5):
# User-item matrix
# Compute similarities
# Return top N recommendationsBenefits:
- Better product suggestions
- Personalized recommendations
- Upsell/cross-sell opportunities
7. Image Upload and Analysis
from PIL import Image
import torch
from transformers import CLIPProcessor, CLIPModel
@app.post("/api/search/by-image")
async def search_by_image(file: UploadFile):
image = Image.open(file.file)
# Extract features with CLIP
# Search similar products
return {"products": similar_products}Benefits:
- Visual search
- Multi-modal AI integration
- Enhanced user experience
8. Voice Interface
const recognition = new webkitSpeechRecognition();
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
sendMessage(transcript);
};Benefits:
- Accessibility
- Hands-free shopping
- Multi-modal interaction
9. Analytics Dashboard
from prometheus_client import Counter, Histogram
search_counter = Counter('product_searches_total', 'Total searches')
response_time = Histogram('response_time_seconds', 'Response time')
@app.post("/api/functions/search_products")
async def search_products(...):
search_counter.inc()
with response_time.time():
# Process searchBenefits:
- Usage insights
- Performance monitoring
- Business intelligence
10. A/B Testing Framework
from fastapi_ab_testing import ABTestMiddleware
@app.get("/api/recommend")
async def recommend(variant: str = Depends(get_variant)):
if variant == "A":
return basic_recommendations()
else:
return ml_recommendations()Benefits:
- Data-driven decisions
- Gradual feature rollout
- Conversion optimization
11. Internationalization (i18n)
import i18n from 'i18next';
i18n.init({
resources: {
en: { translation: { "search": "Search" } },
es: { translation: { "search": "Buscar" } }
}
});
// Usage
<button>{t('search')}</button>Benefits:
- Global audience
- Localized experience
- Market expansion
Frontend (React):
- Initial page load: 1.5-2.0s
- SSE connection establish: <100ms
- Message render time: <16ms (60fps)
- Time to interactive: <2.5s
Backend (FastAPI):
- Health check response: <10ms
- Product search (cached): 50-100ms
- Product search (uncached): 200-500ms
- Function call execution: 100-300ms
- SSE event generation: 50ms per chunk
Database (PostgreSQL):
- Simple SELECT: <10ms
- Indexed WHERE clause: <20ms
- JOIN operations: 50-100ms
- Full table scan: 200-500ms (20 products)
1. Database Indexing
-- Already implemented
CREATE INDEX idx_product_name ON product(name);
CREATE INDEX idx_product_category ON product(category);
CREATE INDEX idx_product_stock ON product(in_stock);
-- Future additions
CREATE INDEX idx_product_price ON product(price);
CREATE INDEX idx_product_rating ON product(rating);2. Query Optimization
# Use select_in_loading for N+1 prevention
from sqlalchemy.orm import selectinload
stmt = (
select(Product)
.options(selectinload(Product.reviews)) # Eager load
.where(Product.category == "ELECTRONICS")
)3. Frontend Optimization
// Code splitting
const ProductCard = lazy(() => import('./ProductCard'));
// Memoization
const expensiveCalculation = useMemo(() => {
return calculateRecommendations(products);
}, [products]);
// Virtualization for long lists
import { FixedSizeList } from 'react-window';4. Caching Strategy
┌─────────────────────┐
│ Browser Cache │ 1. Check browser cache (1-5 min)
│ (Service Worker) │
└──────────┬──────────┘
│ miss
▼
┌─────────────────────┐
│ In-Memory Cache │ 2. Check app cache (60s TTL)
│ (Python dict) │
└──────────┬──────────┘
│ miss
▼
┌─────────────────────┐
│ Database │ 3. Query PostgreSQL
│ (PostgreSQL) │
└─────────────────────┘
This submission does not include formal load tests or measured throughput. If you want to test, a typical approach is to use locust or k6 against the search and chat endpoints. Example (locust):
locust -f locustfile.py --host=http://localhost:8000SSE capacity depends on host, worker count, and network. Measure in your environment.
Horizontal Scaling (Multiple Instances):
# docker-compose.prod.yml
services:
backend:
deploy:
replicas: 3 # 3 backend instances
nginx:
# Load balancer configuration
# Sticky sessions for SSEDatabase Scaling:
-- Read replicas for search queries
-- Primary: Writes (cart, context updates)
-- Replicas: Reads (product search, details)
-- Connection pooling
asyncpg.create_pool(
dsn=DATABASE_URL,
min_size=10,
max_size=100
)CDN for Static Assets:
// Use CDN for product images
const imageUrl = `https://cdn.example.com/products/${productId}.jpg`;
// CloudFlare, AWS CloudFront, etc.1. Input Validation (example pattern)
from pydantic import BaseModel, Field, validator
class SearchRequest(BaseModel):
query: str = Field(..., min_length=1, max_length=200)
category: Optional[ProductCategory] = None
limit: int = Field(default=10, ge=1, le=50)
@validator('query')
def sanitize_query(cls, v):
# Remove potentially dangerous characters
return v.strip().replace(';', '').replace('--', '')Actual validations in this submission:
- Pydantic request models with typed fields and bounds (e.g.,
limitrange) - Category normalization to the
ProductCategoryenum - SQLModel/SQLAlchemy parameter binding (no string‑built SQL)
- CORS configuration with origin allowlist
- Global rate limiting (
slowapi)
2. SQL Injection Prevention
# SQLModel/SQLAlchemy uses parameterized queries automatically
stmt = select(Product).where(Product.name == user_input)
# Generated SQL: SELECT * FROM product WHERE name = $1
# NOT: SELECT * FROM product WHERE name = 'user_input'3. CORS Configuration
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # Specific origins
allow_credentials=True,
allow_methods=["GET", "POST"], # Limited methods
allow_headers=["Content-Type", "Authorization"]
)4. Error Information Limiting
# Don't expose internal errors to users
try:
result = await process_request()
except DatabaseError as e:
logger.error(f"Database error: {e}") # Log internally
raise HTTPException(
status_code=500,
detail="Service temporarily unavailable" # Generic message
)