A Next.js-inspired file-based routing framework built on FastAPI for Python backend development. runapi makes building robust APIs as intuitive as creating files and folders.
- 🚀 Developer Experience: Just like Next.js for React, runapi makes backend development intuitive
- ⚡ Performance: Built on FastAPI, one of the fastest Python frameworks
- 🛡️ Production Ready: Security, middleware, and error handling built-in
- 🎯 Type Safe: Full typing support with automatic validation
- 📁 Intuitive: File-based routing means your folder structure IS your API
- 📁 File-based routing - Create API routes by simply adding Python files
- ⚡ FastAPI integration - Built on top of FastAPI for high performance
- 🔐 Authentication system - JWT-based auth with middleware support
- 🛡️ Middleware stack - Built-in middleware for CORS, rate limiting, security headers
- ⚙️ Configuration management - Environment-based configuration with
.envsupport - 🚨 Error handling - Comprehensive error handling with custom exceptions
- 🔧 CLI tools - Command-line interface for project management
- 📝 Auto-documentation - Automatic API documentation via FastAPI
- 🎯 Type hints - Full typing support with Pydantic integration
pip install runapi- Python 3.8+
- FastAPI
- Uvicorn (for development server)
- Quick Start
- Configuration
- Authentication
- Middleware
- Error Handling
- CLI Commands
- Advanced Usage
- Testing
- Deployment
- API Reference
- Examples
- Contributing
runapi init my-api
cd my-apimy-api/
├── routes/ # API routes (file-based routing)
│ ├── index.py # GET /
│ └── api/
│ ├── users.py # GET, POST /api/users
│ └── users/
│ └── [id].py # GET, PUT, DELETE /api/users/{id}
├── static/ # Static files
├── uploads/ # File uploads directory
├── main.py # Application entry point
├── .env # Configuration file
└── README.md
Routes are created by adding Python files in the routes/ directory:
routes/index.py (GET /)
from runapi import JSONResponse
async def get():
return JSONResponse({"message": "Hello runapi!"})routes/api/users.py (GET,POST /api/users)
from runapi import JSONResponse, Request
async def get():
return JSONResponse({"users": []})
async def post(request: Request):
body = await request.json()
return JSONResponse({"created": body})routes/api/users/[id].py (GET,PUT,DELETE /api/users/{id})
from runapi import JSONResponse, Request
async def get(request: Request):
user_id = request.path_params["id"]
return JSONResponse({"user_id": user_id})
async def put(request: Request):
user_id = request.path_params["id"]
body = await request.json()
return JSONResponse({"user_id": user_id, "updated": body})
async def delete(request: Request):
user_id = request.path_params["id"]
return JSONResponse({"user_id": user_id, "deleted": True})runapi devVisit http://localhost:8000 to see your API!
Once your server is running, you can access:
- Interactive API Documentation:
http://localhost:8000/docs(Swagger UI) - Alternative Documentation:
http://localhost:8000/redoc(ReDoc) - OpenAPI JSON Schema:
http://localhost:8000/openapi.json
runapi uses environment variables for configuration. Create a .env file:
# Server Settings
DEBUG=true
HOST=127.0.0.1
PORT=8000
# Security
SECRET_KEY=your-secret-key-here
# CORS
CORS_ORIGINS=http://localhost:3000,http://localhost:8080
CORS_CREDENTIALS=true
# Rate Limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_CALLS=100
RATE_LIMIT_PERIOD=60
# Database (example)
DATABASE_URL=sqlite:///./app.db
# Logging
LOG_LEVEL=INFO
# Static Files
STATIC_FILES_ENABLED=true
STATIC_FILES_PATH=static
STATIC_FILES_URL=/static
# JWT Settings
JWT_ALGORITHM=HS256
JWT_EXPIRY=3600
JWT_REFRESH_EXPIRY=86400| Variable | Type | Default | Description |
|---|---|---|---|
DEBUG |
boolean | true |
Enable debug mode |
HOST |
string | 127.0.0.1 |
Server host |
PORT |
integer | 8000 |
Server port |
SECRET_KEY |
string | dev-secret-key... |
Secret key for JWT |
CORS_ORIGINS |
string | * |
Comma-separated allowed origins |
CORS_CREDENTIALS |
boolean | true |
Allow credentials in CORS |
RATE_LIMIT_ENABLED |
boolean | false |
Enable rate limiting |
RATE_LIMIT_CALLS |
integer | 100 |
Requests per period |
RATE_LIMIT_PERIOD |
integer | 60 |
Rate limit period in seconds |
LOG_LEVEL |
string | INFO |
Logging level |
DATABASE_URL |
string | None |
Database connection URL |
runapi includes built-in JWT authentication:
main.py
from runapi import create_runapi_app
app = create_runapi_app()
# Protect specific routes
app.add_auth_middleware(
protected_paths=["/api/protected"],
excluded_paths=["/api/auth/login", "/docs"]
)routes/api/auth/login.py
from runapi import JSONResponse, Request, create_token_response, verify_password
async def post(request: Request):
body = await request.json()
username = body.get("username")
password = body.get("password")
# Verify credentials (implement your logic)
if verify_credentials(username, password):
user_data = {"sub": "user_id", "username": username}
tokens = create_token_response(user_data)
return JSONResponse(tokens.dict())
return JSONResponse({"error": "Invalid credentials"}, status_code=401)routes/api/protected.py
from runapi import JSONResponse, get_current_user, Depends
async def get(current_user: dict = Depends(get_current_user())):
return JSONResponse({
"message": "This is protected!",
"user": current_user
})runapi includes several built-in middleware:
from runapi import create_runapi_app
app = create_runapi_app()
# Built-in middleware (automatically configured via .env)
# - CORS
# - Rate limiting
# - Security headers
# - Request logging
# - Compression
# Add custom middleware
from runapi import RunApiMiddleware
class CustomMiddleware(RunApiMiddleware):
async def dispatch(self, request, call_next):
# Pre-processing
response = await call_next(request)
# Post-processing
return response
app.add_middleware(CustomMiddleware)runapi provides comprehensive error handling:
from runapi import ValidationError, NotFoundError, raise_not_found
async def get_user(user_id: str):
if not user_id:
raise ValidationError("User ID is required")
user = find_user(user_id)
if not user:
raise_not_found("User not found")
return userrunapi includes a powerful CLI for development:
# Create new project
runapi init my-project
# Run development server
runapi dev
# Generate boilerplate code
runapi generate route users
runapi generate middleware auth
runapi generate main
# List all routes
runapi routes
# Show project info
runapi infomain.py
from runapi import create_runapi_app, get_config
# Load custom configuration
config = get_config()
# Create app with custom settings
app = create_runapi_app(
title="My API",
description="Built with runapi",
version="1.0.0"
)
# Add custom middleware
app.add_auth_middleware()
# Add custom startup/shutdown events
@app.get_app().on_event("startup")
async def startup():
print("Starting up!")
# Get underlying FastAPI app
fastapi_app = app.get_app()# Using SQLAlchemy (example)
from sqlalchemy import create_engine
from runapi import get_config
config = get_config()
engine = create_engine(config.database_url)
# Use in routes
async def get_users():
# Your database logic here
passfrom fastapi import BackgroundTasks
from runapi import JSONResponse
async def send_email(email: str):
# Send email logic
pass
async def post(request: Request, background_tasks: BackgroundTasks):
body = await request.json()
background_tasks.add_task(send_email, body["email"])
return JSONResponse({"message": "Email queued"})runapi supports dynamic route parameters:
routes/users/[id].py→/users/{id}routes/posts/[slug].py→/posts/{slug}routes/api/[...path].py→/api/{path:path}(catch-all)
from fastapi import UploadFile, File
from runapi import JSONResponse
async def post(file: UploadFile = File(...)):
contents = await file.read()
# Process file
return JSONResponse({"filename": file.filename})from fastapi.testclient import TestClient
from main import app
client = TestClient(app.get_app())
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json()["message"] == "Hello runapi!"Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]pip install gunicorn
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorkerrunapi supports WebSocket connections through FastAPI:
# routes/ws/chat.py
from fastapi import WebSocket
from runapi import get_app
app = get_app()
@app.websocket("/ws/chat")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message: {data}")Creates a runapi application instance.
Parameters:
title(str): API titledescription(str): API descriptionversion(str): API versionconfig(runapiConfig): Custom configuration
Returns: runapiApp instance
Returns the global configuration instance.
Loads configuration from environment file.
Creates a JWT access token.
Verifies and decodes a JWT token.
Hashes a password using bcrypt.
Verifies a password against its hash.
Raises a validation error (400).
Raises a not found error (404).
Raises an authentication error (401).
index.py→ Root path/users.py→/users[id].py→/{id}(dynamic parameter)[...slug].py→/{slug:path}(catch-all)
Export async functions named after HTTP methods:
async def get(): # GET request
async def post(): # POST request
async def put(): # PUT request
async def delete(): # DELETE request
async def patch(): # PATCH requestfrom runapi import Request, JSONResponse
async def post(request: Request):
# Get JSON body
body = await request.json()
# Get path parameters
user_id = request.path_params.get("id")
# Get query parameters
limit = request.query_params.get("limit", 10)
# Get headers
auth_header = request.headers.get("authorization")
return JSONResponse({"status": "success"})Import Error: No module named 'runapi'
pip install runapiRoutes not loading
- Ensure
routes/directory exists in your project root - Check that route files have proper async function exports
- Verify file naming conventions
Authentication not working
- Set a proper
SECRET_KEYin production - Check that protected paths are correctly configured
- Verify JWT token format and expiration
CORS Issues
- Configure
CORS_ORIGINSin your.envfile - Set
CORS_CREDENTIALS=trueif needed - Check that your frontend origin is included
Enable debug mode for detailed error messages:
DEBUG=true
LOG_LEVEL=DEBUG- Use async/await: All route functions should be async
- Enable compression: Built-in gzip compression for responses
- Configure rate limiting: Protect against abuse
- Use proper HTTP status codes: For better client handling
- Implement caching: For frequently accessed data
- Change default secret key in production
- Use HTTPS in production
- Configure CORS properly
- Implement rate limiting
- Validate all inputs
- Use environment variables for sensitive data
Check out the /example directory for a complete example application demonstrating:
- File-based routing
- Authentication with JWT
- Protected routes
- Error handling
- Middleware usage
- Configuration management
Run the example:
cd example
runapi dev- Database integration helpers (SQLAlchemy, MongoDB)
- Built-in caching mechanisms (Redis, in-memory)
- WebSocket routing support
- Background task queue integration
- Plugin system
- More authentication providers (OAuth, LDAP)
- Performance monitoring and metrics
- GraphQL support
We welcome contributions! Here's how to get started:
- Clone the repository:
git clone https://github.com/Amanbig/runapi.git
cd runapi- Create a virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install development dependencies:
pip install -e .
pip install pytest httpx- Run tests:
python -m pytest tests/- Follow PEP 8 style guidelines
- Add tests for new features
- Update documentation
- Create detailed commit messages
- Open an issue before major changes
Please include:
- Python version
- runapi version
- Minimal code example
- Full error traceback
- Expected vs actual behavior
- New Feature: Added
runapi startcommand for production deployments (no-reload, multi-worker support) - Performance: Optimized startup time by ignoring irrelevant directories during route discovery
- Performance: Replaced O(N) rate limiting with O(1) Fixed Window Counter algorithm
- Performance: Implemented streaming compression using
GZipMiddlewarefor lower TTFB and memory usage - Security: Refactored authentication to use standard
python-joselibrary instead of manual implementation - CLI: Optimized
runapi devstartup speed andrunapi routesrobustness
- Bug Fix: Fixed
runapi devcommand failing to import main module - Enhancement: Improved CLI error handling and validation
- Enhancement: Better Python path management for uvicorn integration
- Enhancement: Added pre-validation of main.py before server startup
- Initial release
- File-based routing system
- JWT authentication
- Middleware stack
- CLI tools
- Configuration management
- Error handling system
This project is licensed under the MIT License - see the LICENSE file for details.
- 📚 Documentation
- 🐛 Issue Tracker
- 💬 Discussions
- Built on top of FastAPI by Sebastián Ramirez
- Inspired by Next.js file-based routing by Vercel
- Uses Typer for CLI by Sebastián Ramirez
- Password hashing with Passlib
- Testing with pytest and httpx
- FastAPI - Modern, fast web framework for Python
- Starlette - Lightweight ASGI framework
- Pydantic - Data validation using Python type hints
- Next.js - React framework (inspiration)
If runapi has been helpful to your project:
- ⭐ Star the repo on GitHub
- 🐛 Report bugs and request features
- 📝 Contribute to documentation
- 💰 Sponsor the project
runapi - Making Python backend development as intuitive as frontend development! 🚀