Getting Started with Nexios: A Modern Python Web Framework
Introduction
In the ever-evolving landscape of Python web frameworks, developers are constantly seeking solutions that offer the perfect balance of performance, simplicity, and developer experience. Enter Nexios - a modern, async-first Python web framework that's designed to deliver exceptional performance while maintaining clean, intuitive APIs.
Nexios is built on ASGI (Asynchronous Server Gateway Interface) and combines the speed of Rust-powered engines with the elegance of Python. It's designed for developers who want to build scalable, high-performance web applications without the complexity often associated with enterprise frameworks.
What is Nexios?
Nexios is a high-performance Python web framework that stands out for several key characteristics:
- Async-First Design: Built from the ground up with async/await support
- High Performance: Leverages ASGI for efficient concurrent request handling
- Developer-Friendly: Clean, intuitive APIs with excellent type hints
- Production-Ready: Built-in security, testing, and deployment features
- Flexible: Extensive customization options for any use case
Why Choose Nexios?
Before diving into the technical details, let's understand why Nexios might be the right choice for your next project:
- True Async Performance: Unlike frameworks that add async as an afterthought, Nexios is built async-first with no sync-to-async bridges
- Clean Architecture: Promotes clean code practices through dependency injection and clear separation of concerns
- Modern Authentication: Flexible authentication system with multiple backends (JWT, Session, OAuth)
- WebSocket Support: First-class WebSocket support for real-time applications
- Production Ready: Built-in features for security, monitoring, and deployment
Prerequisites
Before we begin, ensure you have:
- Python 3.9 or higher (Nexios leverages modern Python features)
- Basic understanding of async/await in Python
- A code editor (VS Code, PyCharm, or your preferred editor)
- Package manager (pip, uv, poetry, or pipenv)
Python Version Requirements
Nexios requires Python 3.9+ because it leverages modern Python features like:
- Type annotations with generics
- Async context managers
- Pattern matching (Python 3.10+)
- Union types and other type system improvements
Installation
There are several ways to install Nexios depending on your preferred package manager:
Using uv (Recommended)
# Install uv (if you don't have it) pip install uv # Create a virtual environment and install Nexios uv venv venv source venv/bin/activate # On Windows: venv\Scripts\activate uv pip install nexios
Using pip
# Create a virtual environment python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate # Install Nexios pip install nexios
Using poetry
# Create a new project poetry new my-nexios-app cd my-nexios-app # Add Nexios poetry add nexios # Activate environment poetry shell
Using pipenv
# Create a new project directory mkdir my-nexios-app cd my-nexios-app # Initialize project pipenv install nexios # Activate environment pipenv shell
Your First Nexios Application
Let's create a simple "Hello, World!" application to get familiar with Nexios basics.
Basic Application
Create a file named main.py
:
from nexios import NexiosApp from nexios.http import Request, Response app = NexiosApp() @app.get("/") async def hello(request: Request, response: Response): return response.json({ "message": "Hello from Nexios!", "status": "success" })
Running Your Application
uvicorn main:app --reload
Your application will start on http://localhost:4000
. Visit the URL in your browser to see the JSON response.
With Configuration
For more control over your application, you can use configuration:
from nexios import NexiosApp, MakeConfig from nexios.http import Request, Response config = MakeConfig( debug=True, cors_enabled=True, allowed_hosts=["localhost", "127.0.0.1"] ) app = NexiosApp( config=config, title="My First Nexios API", version="1.0.0" ) @app.get("/") async def hello(request: Request, response: Response): return response.json({ "message": "Hello from Nexios!", "app_title": app.title, "version": app.openapi_config.version })
Core Concepts
1. Application Instance
The NexiosApp
class is the main entry point for your application. It handles:
- Route registration
- Middleware management
- Configuration
- OpenAPI documentation
- Event handling
2. Request and Response Objects
Nexios provides Request
and Response
objects that encapsulate HTTP request and response data:
@app.get("/users/{user_id:int}") async def get_user(request: Request, response: Response): user_id = request.path_params.user_id query_params = request.query_params # Access query parameters include_profile = query_params.get("include_profile", "false").lower() == "true" # Simulate database query user = { "id": user_id, "name": f"User {user_id}", "email": f"user{user_id}@example.com" } if include_profile: user["profile"] = {"bio": "This is a sample profile"} return response.json(user)
3. Path Parameters
Nexios supports typed path parameters:
@app.get("/items/{item_id:int}") async def get_item(request: Request, response: Response): item_id = request.path_params.item_id # Automatically converted to int return response.json({"id": item_id, "name": f"Item {item_id}"}) @app.get("/users/{username:str}") async def get_user_by_name(request: Request, response: Response): username = request.path_params.username # String parameter return response.json({"username": username, "status": "active"})
Available path parameter types:
-
str
(default) int
float
uuid
path
date
datetime
4. HTTP Methods
Nexios provides decorators for all HTTP methods:
@app.get("/users") async def list_users(request: Request, response: Response): return response.json([{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]) @app.post("/users") async def create_user(request: Request, response: Response): user_data = request.json() # Simulate user creation new_user = {"id": 3, **user_data} return response.json(new_user, status=201) @app.put("/users/{user_id:int}") async def update_user(request: Request, response: Response): user_id = request.path_params.user_id user_data = request.json() # Simulate user update updated_user = {"id": user_id, **user_data} return response.json(updated_user) @app.delete("/users/{user_id:int}") async def delete_user(request: Request, response: Response): user_id = request.path_params.user_id # Simulate user deletion return response.status(204)
Dependency Injection
Nexios provides a powerful dependency injection system:
from nexios import Depend async def get_database(): # Simulate database connection return {"connection": "active", "pool_size": 10} async def get_current_user(request: Request): # Simulate user authentication user_id = request.headers.get("X-User-ID") if user_id: return {"id": int(user_id), "name": f"User {user_id}"} return None @app.get("/users") async def list_users( request: Request, response: Response, db: dict = Depend(get_database), current_user: dict = Depend(get_current_user) ): if not current_user: return response.json({"error": "Unauthorized"}, status=401) return response.json({ "users": [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}], "db_status": db["connection"], "current_user": current_user })
Middleware
Middleware in Nexios allows you to process requests and responses globally:
from nexios.middleware import BaseMiddleware from nexios.http import Request, Response class LoggingMiddleware(BaseMiddleware): async def __call__(self, request: Request, response: Response, call_next): # Pre-processing print(f"Request: {request.method} {request.url}") # Call the next middleware/handler response = await call_next() # Post-processing print(f"Response: {response.status_code}") return response # Add middleware to your app app.add_middleware(LoggingMiddleware())
Built-in Middleware
Nexios comes with several built-in middleware options:
from nexios.middleware import CORSMiddleware, SecurityMiddleware # CORS middleware app.add_middleware(CORSMiddleware( allow_origins=["http://localhost:3000"], allow_methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["*"] )) # Security middleware app.add_middleware(SecurityMiddleware())
Authentication
Nexios provides a flexible authentication system:
from nexios.auth.middleware import AuthenticationMiddleware from nexios.auth.backends.jwt import JWTAuthBackend from nexios.auth.decorator import auth async def get_user_from_payload(**payload): # Simulate user lookup from JWT payload user_id = payload.get("sub") if user_id: return {"id": user_id, "name": f"User {user_id}"} return None # Add authentication middleware app.add_middleware( AuthenticationMiddleware( backend=JWTAuthBackend(get_user_from_payload) ) ) @app.get("/protected") @auth(["jwt"]) async def protected_route(request: Request, response: Response): return response.json({ "message": "This is a protected route", "user": request.user })
WebSocket Support
Nexios provides excellent WebSocket support for real-time applications:
from nexios.websockets import Channel # Create a chat channel chat = Channel("chat") @app.ws_route("/ws/chat/{room}") async def chat_room(websocket, room: str): await chat.connect(websocket) try: while True: # Receive message from client message = await websocket.receive_json() # Broadcast to all connected clients await chat.broadcast({ "room": room, "message": message["text"], "user": message["user"] }) except Exception: # Handle disconnection await chat.disconnect(websocket)
Error Handling
Nexios provides comprehensive error handling:
from nexios.exceptions import HTTPException @app.get("/users/{user_id:int}") async def get_user(request: Request, response: Response): user_id = request.path_params.user_id # Simulate user lookup if user_id > 100: raise HTTPException(status_code=404, detail="User not found") return response.json({"id": user_id, "name": f"User {user_id}"}) # Custom exception handler @app.add_exception_handler(ValueError) async def handle_value_error(request: Request, response: Response, exc: ValueError): return response.json({ "error": "Invalid value provided", "detail": str(exc) }, status=400)
OpenAPI Documentation
Nexios automatically generates OpenAPI documentation:
from pydantic import BaseModel class UserCreate(BaseModel): name: str email: str age: int class UserResponse(BaseModel): id: int name: str email: str age: int @app.post("/users", summary="Create a new user", description="Creates a new user with the provided information", response_model=UserResponse, status_code=201 ) async def create_user( request: Request, response: Response, user_data: UserCreate ): # Simulate user creation new_user = { "id": 1, **user_data.dict() } return response.json(new_user, status=201)
Visit http://localhost:4000/docs
to see the interactive API documentation.
Project Structure
As your application grows, you'll want to organize it properly:
my-nexios-app/ βββ app/ β βββ __init__.py β βββ main.py β βββ config.py β βββ models/ β β βββ __init__.py β β βββ user.py β βββ services/ β β βββ __init__.py β β βββ user_service.py β βββ routes/ β β βββ __init__.py β β βββ users.py β β βββ auth.py β βββ middleware/ β βββ __init__.py β βββ custom_middleware.py βββ tests/ β βββ __init__.py β βββ test_users.py βββ requirements.txt βββ README.md
Example: Modular Route Structure
# app/routes/users.py from nexios import Router from nexios.http import Request, Response router = Router() @router.get("/") async def list_users(request: Request, response: Response): return response.json([{"id": 1, "name": "John"}]) @router.post("/") async def create_user(request: Request, response: Response): user_data = request.json() return response.json(user_data, status=201) # app/main.py from nexios import NexiosApp from app.routes.users import router app = NexiosApp() # Mount the users router app.mount_router(router, path="/users")
Testing
Nexios provides testing utilities:
from nexios.testing import TestClient from app.main import app client = TestClient(app) def test_list_users(): response = client.get("/users") assert response.status_code == 200 assert isinstance(response.json(), list) def test_create_user(): user_data = {"name": "John", "email": "john@example.com"} response = client.post("/users", json=user_data) assert response.status_code == 201 assert response.json()["name"] == "John"
Deployment
Development
# Run with auto-reload python main.py # Or with uvicorn uvicorn main:app --reload --host 0.0.0.0 --port 8000
Production
# Using uvicorn uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 # Using gunicorn with uvicorn workers gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker
Docker
FROM python:3.11-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"]
Performance Considerations
1. Async Best Practices
# Good: Use async for I/O operations @app.get("/users") async def list_users(request: Request, response: Response): # Simulate async database query users = await database.fetch_all("SELECT * FROM users") return response.json(users) # Avoid: Blocking operations in async handlers @app.get("/users") async def list_users(request: Request, response: Response): # Bad: This blocks the event loop users = time.sleep(1) # Don't do this! return response.json(users)
2. Connection Pooling
import asyncpg # Create connection pool pool = None @app.on_startup async def startup(): global pool pool = await asyncpg.create_pool( "postgresql://user:password@localhost/dbname", min_size=5, max_size=20 ) @app.on_shutdown async def shutdown(): if pool: await pool.close() @app.get("/users") async def list_users(request: Request, response: Response): async with pool.acquire() as conn: users = await conn.fetch("SELECT * FROM users") return response.json([dict(user) for user in users])
Common Patterns
1. Pagination
from nexios.pagination import Pagination @app.get("/users") async def list_users( request: Request, response: Response, page: int = 1, size: int = 10 ): # Simulate paginated data total_users = 100 users = [ {"id": i, "name": f"User {i}"} for i in range((page - 1) * size + 1, min(page * size + 1, total_users + 1)) ] pagination = Pagination( page=page, size=size, total=total_users, items=users ) return response.json(pagination.to_dict())
2. File Upload
@app.post("/upload") async def upload_file(request: Request, response: Response): form = await request.form() file = form.get("file") if file: # Save file with open(f"uploads/{file.filename}", "wb") as f: f.write(file.file.read()) return response.json({ "message": "File uploaded successfully", "filename": file.filename }) return response.json({"error": "No file provided"}, status=400)
3. Background Tasks
@app.post("/users") async def create_user(request: Request, response: Response): user_data = request.json() # Add background task @app.background_task async def send_welcome_email(user_email: str): # Simulate sending email print(f"Sending welcome email to {user_email}") # Schedule background task app.add_background_task(send_welcome_email, user_data["email"]) return response.json(user_data, status=201)
Best Practices
1. Error Handling
from nexios.exceptions import HTTPException @app.get("/users/{user_id:int}") async def get_user(request: Request, response: Response): user_id = request.path_params.user_id try: user = await user_service.get_user(user_id) if not user: raise HTTPException(status_code=404, detail="User not found") return response.json(user) except ValueError as e: raise HTTPException(status_code=400, detail=str(e))
2. Validation
from pydantic import BaseModel, EmailStr class UserCreate(BaseModel): name: str email: EmailStr age: int @app.post("/users") async def create_user( request: Request, response: Response, user_data: UserCreate ): # Pydantic automatically validates the data return response.json(user_data.dict(), status=201)
3. Security
from nexios.middleware import SecurityMiddleware # Add security headers app.add_middleware(SecurityMiddleware()) # Use HTTPS in production if not app.config.debug: app.config.force_https = True
Conclusion
Nexios is a powerful, modern Python web framework that offers excellent performance, developer experience, and flexibility. Its async-first design, clean architecture, and comprehensive feature set make it an excellent choice for building scalable web applications.
Key Takeaways
- Async-First: Nexios is built from the ground up with async/await support
- High Performance: Leverages ASGI for efficient concurrent request handling
- Developer-Friendly: Clean APIs with excellent type hints and documentation
- Production-Ready: Built-in security, testing, and deployment features
- Flexible: Extensive customization options for any use case
Next Steps
Now that you have a solid foundation with Nexios, consider exploring:
- Database Integration: Connect to PostgreSQL, MySQL, or MongoDB
- Authentication: Implement JWT, OAuth, or session-based auth
- WebSockets: Build real-time features with WebSocket support
- Testing: Write comprehensive tests for your application
- Deployment: Deploy to production with proper configuration
Resources
Happy coding with Nexios! π
This blog post was written to help developers get started with the Nexios framework. For the latest information and updates, always refer to the official documentation.
Top comments (0)