Session Management
Session management is a critical component of web applications, allowing you to store and retrieve user data across multiple requests. Nexios provides a robust, flexible session management system that's easy to configure yet powerful enough for complex applications.
👤 Basic Session Setup
Setting up sessions in your Nexios application is straightforward:
from nexios import NexiosApp
from nexios.session.middleware import SessionMiddleware
app = NexiosApp()
# Required: Configure a secret key for signing sessions
app.config.secret_key = "your-secure-secret-key"
# Add the session middleware
app.add_middleware(SessionMiddleware)
With this minimal setup, Nexios will use the default cookie-based session backend. Your routes can now access the session through the request object:
@app.get("/")
async def index(req, res):
# Access the session
counter = req.session.get("counter", 0)
counter += 1
req.session["counter"] = counter
return res.text(f"You've visited this page {counter} times")
⚙️ Session Configuration Options
Nexios offers various configuration options for customizing session behavior:
from nexios import NexiosApp
from nexios.session.middleware import SessionMiddleware
from nexios.session.file import FileSessionInterface
app = NexiosApp()
# Required secret key for secure cookies
app.config.secret_key = "your-secure-secret-key"
# Session configuration
app.config.session = {
# Session cookie name
"session_cookie_name": "nexios_session",
# Cookie settings
"cookie_path": "/",
"cookie_domain": None,
"cookie_secure": True,
"cookie_httponly": True,
"cookie_samesite": "lax",
# Session expiration (in seconds)
"expiry": 86400, # 24 hours
# Session backend
"manager": FileSessionInterface,
# Backend-specific settings
"directory": "sessions",
"prefix": "session_"
}
# Add the session middleware
app.add_middleware(SessionMiddleware())
🔧 Configuration Options Reference
Option | Description | Default |
---|---|---|
session_cookie_name | Name of the cookie storing the session ID | "session_id" |
cookie_path | Path for which the cookie is valid | "/" |
cookie_domain | Domain for which the cookie is valid | None |
cookie_secure | Whether cookie should only be sent over HTTPS | False |
cookie_httponly | Whether cookie should be accessible via JavaScript | True |
cookie_samesite | SameSite attribute ("lax" , "strict" , or "none" ) | "lax" |
expiry | Session lifetime in seconds | 86400 (24 hours) |
manager | Session backend class | SignedSessionManager |
🧢 Basic Session Operations
@app.get("/session-demo")
async def session_demo(req, res):
# Get a value with default if not present
user_id = req.session.get("user_id", None)
# Set a value
req.session["last_visit"] = time.time()
# Check if a key exists
if "preferences" in req.session:
preferences = req.session["preferences"]
# Remove a key
if "temporary_data" in req.session:
del req.session["temporary_data"]
# Clear the entire session
# req.session.clear()
return res.json({
"user_id": user_id,
"session_keys": list(req.session.keys())
})
Session Properties and Methods
Sessions in Nexios behave similar to dictionaries but with additional methods:
Method/Property | Description |
---|---|
session.get(key, default=None) | Get a value, returning default if not present |
session[key] = value | Set a session value |
key in session | Check if key exists in the session |
del session[key] | Delete a key from the session |
session.clear() | Remove all keys from the session |
session.keys() | Get all keys in the session |
session.items() | Get all key-value pairs in the session |
session.pop(key, default=None) | Get and remove a key, returning default if not present |
session.is_empty() | Check if session has no data |
session.modified | Whether session has been modified |
Session Expiration
By default, sessions expire after 24 hours (86400 seconds). You can customize this:
# Set global session expiration time
app.config.session = {
"expiry": 3600 # 1 hour
}
# Or set per-session expiration time
@app.post("/login")
async def login(req, res):
# Authenticate user...
req.session["user_id"] = user.id
# Set this specific session to expire in 30 minutes
req.session.set_expiry(1800)
return res.json({"success": True})
💻 Session Backends
Nexios supports multiple session backends to store session data. Each backend has different characteristics suitable for various use cases.
🔑 Signed Cookie Sessions (Default)
The simplest session backend, storing the session data directly in a signed cookie:
from nexios.session.signed_cookies import SignedSessionManager
app.config.session = {
"manager": SignedSessionManager
}
Pros:
- No server-side storage required
- Works well in distributed environments
- Simple setup
Cons:
- Limited storage size (4KB cookie limit)
- Session data sent with every request
- Cannot be invalidated server-side
🗃️ File-based Sessions
Stores session data in files on the server filesystem:
from nexios.session.file import FileSessionInterface
app.config.session = {
"manager": FileSessionInterface,
"directory": "sessions", # Directory to store session files
"prefix": "session_" # Prefix for session files
}
Pros:
- Unlimited session data size
- Sessions can be invalidated server-side
- Simple setup for single-server environments
Cons:
- Not suitable for distributed environments
- Requires filesystem access
- Needs cleanup of expired session files
🎨 Building Custom Session Backends
You can create custom session backends by implementing the BaseSessionInterface
:
from nexios.session.base import BaseSessionInterface
class RedisSessionInterface(BaseSessionInterface):
"""Redis-backed session interface"""
def __init__(self, session_key=None):
super().__init__(session_key)
self.redis_client = redis.Redis()
async def load(self):
"""Load the session data from Redis"""
if not self.session_key:
return
data = self.redis_client.get(f"session:{self.session_key}")
if data:
self._data = json.loads(data)
async def save(self):
"""Save the session data to Redis"""
if not self.session_key:
self.session_key = self.generate_sid()
expiry = self.get_expiry_age()
self.redis_client.setex(
f"session:{self.session_key}",
expiry,
json.dumps(self._data)
)
self.modified = False
def get_session_key(self):
"""Return the session key"""
return self.session_key
😎 Session Security Best Practices
Session management requires careful attention to security:
Generate a Strong Secret Key
import secrets
# Generate a secure random key
app.config.secret_key = secrets.token_hex(32)
# For production, store this in environment variables
app.config.secret_key = os.environ.get("SECRET_KEY")
Enable Secure Cookies
app.config.session = {
"cookie_secure": True, # Only send cookies over HTTPS
"cookie_httponly": True, # Prevent JavaScript access
"cookie_samesite": "lax" # Mitigate CSRF attacks
}
Use Appropriate Session Expiration
# Short expiration for sensitive operations
@app.post("/banking/transfer")
async def transfer(req, res):
# Verify authentication is recent
auth_time = req.session.get("auth_time", 0)
if time.time() - auth_time > 300: # 5 minutes
return res.redirect("/re-authenticate")
# Process transfer...
Implement Session Invalidation
@app.post("/logout")
async def logout(req, res):
# Clear session and remove cookie
req.session.clear()
return res.redirect("/login")
👤 Practical Examples
Example 1: User Authentication Flow
@app.post("/login")
async def login(req, res):
data = await req.form
username = data.get("username")
password = data.get("password")
# Authenticate user (pseudo-code)
user = authenticate_user(username, password)
if not user:
return res.redirect("/login?error=invalid_credentials")
# Store user info in session
req.session["user_id"] = user.id
req.session["username"] = user.username
req.session["auth_time"] = time.time()
req.session["is_admin"] = user.is_admin
return res.redirect("/dashboard")
@app.get("/dashboard")
async def dashboard(req, res):
# Check if user is logged in
if "user_id" not in req.session:
return res.redirect("/login")
username = req.session["username"]
return res.html(f"<h1>Welcome, {username}!</h1>")
@app.post("/logout")
async def logout(req, res):
req.session.clear()
return res.redirect("/login?message=logged_out")
Example 2: Shopping Cart
@app.get("/cart")
async def view_cart(req, res):
# Initialize cart if it doesn't exist
cart = req.session.get("cart", {})
# Calculate total
total = sum(item["price"] * item["quantity"] for item in cart.values())
return res.json({
"items": cart,
"total": total
})
@app.post("/cart/add/{product_id}")
async def add_to_cart(req, res):
product_id = req.path_params.product_id
quantity = int(req.query_params.get("quantity", 1))
# Get product details (pseudo-code)
product = get_product(product_id)
if not product:
return res.json({"error": "Product not found"}, status_code=404)
# Get or initialize cart
cart = req.session.get("cart", {})
# Add or update product in cart
if product_id in cart:
cart[product_id]["quantity"] += quantity
else:
cart[product_id] = {
"name": product.name,
"price": product.price,
"quantity": quantity
}
# Save cart to session
req.session["cart"] = cart
return res.json({"success": True, "cart": cart})
@app.post("/cart/clear")
async def clear_cart(req, res):
if "cart" in req.session:
del req.session["cart"]
return res.json({"success": True})
Example 3: Multi-step Form with Session Data
@app.get("/wizard/step1")
async def wizard_step1(req, res):
# Initialize or get form data
form_data = req.session.get("wizard_data", {})
return res.html_template("wizard/step1.html", form_data=form_data)
@app.post("/wizard/step1")
async def wizard_step1_post(req, res):
form_data = await req.form
# Validate form (pseudo-code)
if not validate_step1(form_data):
return res.redirect("/wizard/step1?error=invalid_data")
# Initialize wizard data if not exists
wizard_data = req.session.get("wizard_data", {})
# Update with step 1 data
wizard_data.update({
"name": form_data.get("name"),
"email": form_data.get("email")
})
# Save back to session
req.session["wizard_data"] = wizard_data
# Proceed to next step
return res.redirect("/wizard/step2")
@app.post("/wizard/complete")
async def wizard_complete(req, res):
# Get all wizard data
wizard_data = req.session.get("wizard_data", {})
# Process the complete submission
result = process_wizard_submission(wizard_data)
# Clear wizard data from session
del req.session["wizard_data"]
return res.redirect(f"/wizard/success?id={result.id}")