Flask Complete Example¶
A comprehensive example of building a REST API with REROUTE and Flask.
Project Structure¶
my-flask-api/
├── app/
│ ├── routes/
│ │ ├── users/
│ │ │ └── page.py
│ │ └── products/
│ │ └── page.py
│ └── models/
│ └── user.py
├── config.py
├── main.py
└── requirements.txt
Installation¶
# Create a new REROUTE project
reroute init
# Interactive prompts:
? Project name: my-flask-api
? Which framework would you like to use? flask
? Would you like to generate test cases? Yes
# Review and confirm:
# Project Name: my-flask-api
# Framework: FLASK
# Host: 0.0.0.0
# Port: 7376
# Include Tests: Yes
? Does this look correct? Yes
# Navigate to project directory
cd my-flask-api
# Generate user model (Pydantic schemas)
reroute create model --name User
# Generate user routes (handles both list and individual user operations)
# Add --http-test flag to generate .http test file automatically
reroute create route --path /users --name UserRoutes --methods GET,POST,PUT,DELETE --http-test
# Generate product routes with HTTP test file
reroute create route --path /products --name ProductRoutes --methods GET,POST --http-test
# Your project structure is now ready!
Note: The --http-test flag is optional. If included, REROUTE automatically generates a .http test file with pre-configured API test requests for the route.
Generated Structure:
my-flask-api/
├── app/
│ ├── routes/
│ │ ├── users/
│ │ │ └── page.py # UserRoutes class (handles /users and /users/<id>)
│ │ └── products/
│ │ └── page.py # ProductRoutes class
│ └── models/
│ └── user.py # UserBase, UserCreate, UserUpdate, etc.
├── tests/
│ ├── users.http # HTTP test file for user routes (if --http-test used)
│ └── products.http # HTTP test file for product routes (if --http-test used)
├── config.py
├── main.py
└── requirements.txt
Note: Dynamic path parameters (like user_id) are handled in your route methods, not in the folder structure. See the code examples below.
Configuration¶
config.py
from reroute import Config
class AppConfig(Config):
# Server Configuration
HOST = "0.0.0.0"
PORT = 7376
DEBUG = True
# OpenAPI Documentation
class OpenAPI:
ENABLE = True
DOCS_PATH = "/docs" # Swagger UI
REDOC_PATH = None # ReDoc disabled
JSON_PATH = "/openapi.json"
TITLE = "My Flask API"
VERSION = "1.0.0"
DESCRIPTION = "A REST API built with REROUTE and Flask"
# CORS Configuration
ENABLE_CORS = True
CORS_ALLOW_ORIGINS = ["http://localhost:3000"]
CORS_ALLOW_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"]
CORS_ALLOW_HEADERS = ["*"]
CORS_ALLOW_CREDENTIALS = False
Main Application¶
main.py
from pathlib import Path
from flask import Flask
from reroute.adapters import FlaskAdapter
from config import AppConfig
# Create Flask app
app = Flask(__name__)
# Initialize REROUTE adapter
adapter = FlaskAdapter(
flask_app=app,
app_dir=Path(__file__).parent / "app",
config=AppConfig
)
# Register all file-based routes
adapter.register_routes()
# Root endpoint
@adapter.get('/')
def root():
"""Welcome endpoint showing API information"""
return {
"message": "Welcome to My Flask API",
"version": "1.0.0",
"docs": "/swagger/",
"openapi": "/openapi.json"
}
# Health check endpoint
@adapter.get('/health')
def health():
"""Health check endpoint"""
return {
"status": "healthy",
"framework": "Flask + REROUTE"
}
if __name__ == "__main__":
adapter.run_server()
Pydantic Models¶
app/models/user.py
from pydantic import BaseModel, EmailStr, Field
from typing import Optional
from datetime import datetime
class UserBase(BaseModel):
"""Base user schema with common fields"""
name: str = Field(..., min_length=2, max_length=100)
email: EmailStr
age: Optional[int] = Field(None, ge=0, le=150)
class UserCreate(BaseModel):
"""Schema for creating a new user"""
name: str
email: EmailStr
password: str = Field(..., min_length=8)
age: Optional[int] = None
class UserUpdate(BaseModel):
"""Schema for updating user (all fields optional)"""
name: Optional[str] = None
email: Optional[EmailStr] = None
age: Optional[int] = None
class UserResponse(BaseModel):
"""Schema for user responses (without password)"""
id: int
name: str
email: EmailStr
age: Optional[int] = None
created_at: datetime
File-Based Routes¶
Important: Using REROUTE's Parameter System¶
Always use REROUTE's parameter injection:
Why? - REROUTE provides a unified parameter system that works across both Flask and FastAPI - Ensures your code is portable and framework-agnostic - Automatic validation and OpenAPI documentation generation
User Routes¶
app/routes/users/page.py
from reroute import RouteBase
from reroute.params import Query, Body
from app.models.user import UserCreate, UserUpdate, UserResponse
from typing import Optional
# In-memory storage (use database in production)
users_db = []
next_id = 1
class UserRoutes(RouteBase):
"""User management endpoints - handles both list and individual user operations"""
tag = "Users"
def get(
self,
user_id: Optional[int] = Query(default=None, description="User ID for specific user"),
page: int = Query(default=1, ge=1, description="Page number"),
limit: int = Query(default=10, ge=1, le=100, description="Items per page")
):
"""
List all users OR get a specific user
- If user_id is provided: Returns a specific user
- If user_id is None: Returns paginated list of users
"""
# Get specific user
if user_id is not None:
user = next((u for u in users_db if u["id"] == user_id), None)
if not user:
return {"error": "User not found"}, 404
return user
# List all users with pagination
start = (page - 1) * limit
end = start + limit
paginated_users = users_db[start:end]
return {
"page": page,
"limit": limit,
"total": len(users_db),
"users": paginated_users
}
def post(self, user: UserCreate = Body()):
"""
Create a new user
Creates a new user with the provided information.
"""
global next_id
# Check if email already exists
if any(u["email"] == user.email for u in users_db):
return {"error": "Email already exists"}, 400
new_user = {
"id": next_id,
"name": user.name,
"email": user.email,
"age": user.age,
"created_at": "2025-11-21T00:00:00"
}
users_db.append(new_user)
next_id += 1
return new_user, 201
def put(
self,
user_id: int = Query(description="User ID to update"),
user: UserUpdate = Body()
):
"""
Update user
Updates an existing user's information.
"""
user_idx = next(
(i for i, u in enumerate(users_db) if u["id"] == user_id),
None
)
if user_idx is None:
return {"error": "User not found"}, 404
# Update only provided fields
if user.name is not None:
users_db[user_idx]["name"] = user.name
if user.email is not None:
users_db[user_idx]["email"] = user.email
if user.age is not None:
users_db[user_idx]["age"] = user.age
return users_db[user_idx]
def delete(self, user_id: int = Query(description="User ID to delete")):
"""
Delete user
Deletes a user by their ID.
"""
user_idx = next(
(i for i, u in enumerate(users_db) if u["id"] == user_id),
None
)
if user_idx is None:
return {"error": "User not found"}, 404
deleted_user = users_db.pop(user_idx)
return {"message": "User deleted", "user": deleted_user}
Product Routes¶
app/routes/products/page.py
from reroute import RouteBase
from reroute.decorators import cache, rate_limit
from reroute.params import Query
class ProductRoutes(RouteBase):
"""Product management endpoints"""
tag = "Products"
@cache(duration=300) # Cache for 5 minutes
def get(
self,
category: str = Query(default=None, description="Filter by category"),
min_price: float = Query(default=None, ge=0, description="Minimum price")
):
"""
List products with optional filters
Returns a list of products, optionally filtered by category and price.
"""
products = [
{"id": 1, "name": "Laptop", "price": 999.99, "category": "Electronics"},
{"id": 2, "name": "Mouse", "price": 29.99, "category": "Electronics"},
{"id": 3, "name": "Desk", "price": 299.99, "category": "Furniture"},
]
# Apply filters
if category:
products = [p for p in products if p["category"] == category]
if min_price is not None:
products = [p for p in products if p["price"] >= min_price]
return {"products": products}
@rate_limit("10/min") # Max 10 requests per minute
def post(self):
"""
Create a new product
Creates a new product (rate limited to prevent abuse).
"""
return {"message": "Product created"}, 201
Running the Application¶
# Development (with auto-reload)
python main.py
# Production
adapter.run_server(debug=False, threaded=True)
API Documentation¶
Once running, access the documentation at:
- Swagger UI: http://localhost:7376/swagger/
- Scalar UI: http://localhost:7376/scalar/
- OpenAPI JSON: http://localhost:7376/openapi.json
Testing¶
REROUTE can automatically generate .http test files when you use the --http-test flag. These files contain pre-configured HTTP requests for testing your API directly in VS Code (with REST Client extension) or other HTTP clients.
Auto-Generated HTTP Test Files¶
If you used --http-test when creating routes, you'll have test files in the tests/ directory with ready-to-use requests:
tests/users.http (auto-generated)
### List all users
GET http://localhost:7376/users
### Create a new user
POST http://localhost:7376/users
Content-Type: application/json
{
"name": "Test User",
"email": "test@example.com",
"password": "password123",
"age": 25
}
### Get specific user
GET http://localhost:7376/users?user_id=1
### Update user
PUT http://localhost:7376/users?user_id=1
Content-Type: application/json
{
"name": "Updated Name",
"age": 30
}
### Delete user
DELETE http://localhost:7376/users?user_id=1
Manual .http Files¶
You can also create custom test files manually:
test_users.http
### Get all users
GET http://localhost:7376/users
### Create a new user
POST http://localhost:7376/users
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com",
"password": "securepass123",
"age": 30
}
### Get specific user (using query parameter)
GET http://localhost:7376/users?user_id=1
### Update user (using query parameter)
PUT http://localhost:7376/users?user_id=1
Content-Type: application/json
{
"name": "John Updated",
"age": 31
}
### Delete user (using query parameter)
DELETE http://localhost:7376/users?user_id=1
How to Use .http Files¶
VS Code (Recommended):
1. Install the REST Client extension
2. Open any .http file
3. Click "Send Request" above each request or press Ctrl+Alt+R
Other Clients: - IntelliJ IDEA / PyCharm (built-in support) - Postman (import as collection) - Insomnia (import as workspace)
Using curl¶
# List users
curl http://localhost:7376/users
# Create user
curl -X POST http://localhost:7376/users \
-H "Content-Type: application/json" \
-d '{"name":"Jane","email":"jane@example.com","password":"pass123","age":25}'
# Get user by ID (using query parameter)
curl "http://localhost:7376/users?user_id=1"
# Update user (using query parameter)
curl -X PUT "http://localhost:7376/users?user_id=1" \
-H "Content-Type: application/json" \
-d '{"name":"Jane Updated"}'
# Delete user (using query parameter)
curl -X DELETE "http://localhost:7376/users?user_id=1"
Key Features Demonstrated¶
- OpenAPI Documentation - Automatic Swagger UI with Spectree
- Request Validation - Pydantic models for type safety
- Query Parameters - Pagination and filtering
- Body Validation - Structured request bodies
- Rate Limiting - Protect endpoints from abuse
- Caching - Improve performance for expensive operations
- CORS - Cross-origin requests for frontend integration
- Error Handling - Proper HTTP status codes
- File-Based Routing - Organized route structure
- Auto-Generated HTTP Tests -
.httpfiles with--http-testflag
Troubleshooting¶
Problem 1: IndentationError in config.py¶
Error:
Solution: This is caused by a bug in earlier versions of the REROUTE template. Update REROUTE to the latest version:
Or manually fix config.py by ensuring each line under class OpenAPI: is properly indented with 8 spaces.
Problem 2: ModuleNotFoundError: No module named 'spectree'¶
Error:
Solution: Flask adapter requires Spectree for OpenAPI support. Install it:
Or install with Flask extras:
Problem 3: Routes not being registered¶
Symptom: Routes defined in app/routes/ are not accessible (404 errors)
Common Causes:
1. Missing page.py file: Route files must be named page.py
2. Wrong directory structure: Ensure routes are in app/routes/
3. Class not inheriting RouteBase: Route class must inherit from reroute.RouteBase
Solution:
# Correct structure:
# app/routes/users/page.py
from reroute import RouteBase
class UserRoutes(RouteBase): # Must inherit RouteBase
def get(self):
return {"message": "Users"}
Problem 4: Swagger UI shows 404¶
Symptom: Accessing /swagger/ returns 404
Solutions:
1. Check config.py has OpenAPI enabled:
-
Ensure Spectree is installed:
pip install spectree -
Verify Flask app is using the adapter:
Problem 5: CORS errors in browser¶
Error: Access-Control-Allow-Origin header missing
Solution:
Enable CORS in config.py:
class AppConfig(Config):
ENABLE_CORS = True
CORS_ALLOW_ORIGINS = ["http://localhost:3000"] # Add your frontend URL
CORS_ALLOW_METHODS = ["GET", "POST", "PUT", "DELETE"]
Or install Flask-CORS manually if not working:
Next Steps¶
- Add database integration (SQLAlchemy, Peewee)
- Implement authentication (JWT, OAuth2)
- Add input sanitization
- Set up logging and monitoring
- Deploy to production