DEV Community

Dunamix
Dunamix

Posted on

Getting started with Nexios Asgi Python Framework

Image description

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:

  1. True Async Performance: Unlike frameworks that add async as an afterthought, Nexios is built async-first with no sync-to-async bridges
  2. Clean Architecture: Promotes clean code practices through dependency injection and clear separation of concerns
  3. Modern Authentication: Flexible authentication system with multiple backends (JWT, Session, OAuth)
  4. WebSocket Support: First-class WebSocket support for real-time applications
  5. 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 
Enter fullscreen mode Exit fullscreen mode

Using pip

# Create a virtual environment python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate # Install Nexios pip install nexios 
Enter fullscreen mode Exit fullscreen mode

Using poetry

# Create a new project poetry new my-nexios-app cd my-nexios-app # Add Nexios poetry add nexios # Activate environment poetry shell 
Enter fullscreen mode Exit fullscreen mode

Using pipenv

# Create a new project directory mkdir my-nexios-app cd my-nexios-app # Initialize project pipenv install nexios # Activate environment pipenv shell 
Enter fullscreen mode Exit fullscreen mode

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" }) 
Enter fullscreen mode Exit fullscreen mode

Running Your Application

uvicorn main:app --reload 
Enter fullscreen mode Exit fullscreen mode

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 }) 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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"}) 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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 }) 
Enter fullscreen mode Exit fullscreen mode

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()) 
Enter fullscreen mode Exit fullscreen mode

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()) 
Enter fullscreen mode Exit fullscreen mode

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 }) 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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") 
Enter fullscreen mode Exit fullscreen mode

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" 
Enter fullscreen mode Exit fullscreen mode

Deployment

Development

# Run with auto-reload python main.py # Or with uvicorn uvicorn main:app --reload --host 0.0.0.0 --port 8000 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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"] 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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]) 
Enter fullscreen mode Exit fullscreen mode

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()) 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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)) 
Enter fullscreen mode Exit fullscreen mode

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) 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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

  1. Async-First: Nexios is built from the ground up with async/await support
  2. High Performance: Leverages ASGI for efficient concurrent request handling
  3. Developer-Friendly: Clean APIs with excellent type hints and documentation
  4. Production-Ready: Built-in security, testing, and deployment features
  5. 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)