Skip to content

Request State Management in FastAPI Mason

FastAPI Mason offers a request-scoped state management system that lets you safely share data across middleware, ViewSets, and other components during a single request lifecycle. Built with concurrency safety in mind, this system is ideal for passing user data, request context, and other runtime information throughout your FastAPI application.

Overview

The state management system uses Python's contextvars to maintain request-scoped data that persists throughout the request lifecycle but is isolated between concurrent requests.

Key features:

  • Request-scoped: Data is isolated per request
  • Thread-safe: Works correctly with FastAPI's async nature
  • Automatic cleanup: State is cleared after each request
  • Flexible storage: Store any type of data

Basic Usage

Setting User Information

The most common use case is storing the current user:

from typing import Optional
from fastapi import Depends, FastAPI, HTTPException, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from fastapi_mason.state import BaseStateManager


class OptionalHTTPBearer(HTTPBearer):
    async def __call__(self, request: Request) -> Optional[HTTPAuthorizationCredentials]:
        credentials: Optional[HTTPAuthorizationCredentials] = None
        try:
            credentials = await super().__call__(request)
        except HTTPException:
            # No credentials provided — allow anonymous
            return None
        return credentials


async def get_current_user(token: Optional[HTTPAuthorizationCredentials] = Depends(OptionalHTTPBearer())):
    if token and token.credentials == "token":  # Your logic
        user = {"id": 1, "username": "john"}
        BaseStateManager.set_user(user)
        return user
    return None


# Apply to app
app = FastAPI(dependencies=[Depends(get_current_user)])

Accessing State in ViewSets

ViewSets automatically have access to the current state:

@viewset(router)
class CompanyViewSet(ModelViewSet[Company]):
    model = Company
    read_schema = CompanyReadSchema
    create_schema = CompanyCreateSchema

    def get_queryset(self):
        # Access current user
        user = self.user

        # Access current request
        request = self.request

        # Access current action
        action = self.action

        if user:
            return Company.filter(owner=user.id)
        return Company.filter(is_public=True)

State Properties

The state manager provides several built-in properties:

user

The current authenticated user:

def get_queryset(self):
    if self.user:
        # User is authenticated
        return Company.filter(owner=self.user.id)
    else:
        # Anonymous user
        return Company.filter(is_public=True)

request

The current FastAPI Request object:

def get_queryset(self):
    # Access request details
    client_ip = self.request.client.host
    user_agent = self.request.headers.get('user-agent')

    # Log request details
    logger.info(f"Request from {client_ip}: {user_agent}")

    return Company.all()

action

The current ViewSet action being executed:

def get_permissions(self):
    # Different permissions based on action
    if self.action in ('list', 'retrieve'):
        return []  # Public read access
    elif self.action in ('create', 'update'):
        return [IsAuthenticated()]
    elif self.action == 'destroy':
        return [IsAuthenticated(), IsOwner()]

    return super().get_permissions()

request_id

Request id in uuid4 format

Custom State Data

Store custom data in the state:

# In middleware or dependency
state = BaseStateManager.get_state()
state.set('organization_id', user.organization_id)
state.set('request_start_time', time.time())
state.set('feature_flags', {'new_ui': True, 'beta_feature': False})

# In ViewSet
def get_queryset(self):
    state = self.state

    # Get custom data
    org_id = state.get('organization_id')
    feature_flags = state.get('feature_flags', {})

    queryset = Company.filter(organization_id=org_id)

    if feature_flags.get('new_filtering'):
        queryset = queryset.filter(is_featured=True)

    return queryset

State Management Methods

Setting Data

state = BaseStateManager.get_state()

# Set individual values
state.set('key', 'value')
state.set('user_preferences', {'theme': 'dark', 'language': 'en'})

# Set user (shortcut)
BaseStateManager.set_user(user)

Getting Data

state = BaseStateManager.get_state()

# Get with default
value = state.get('key', 'default_value')

# Check if key exists
if state.has('user_preferences'):
    prefs = state.get('user_preferences')

# Direct property access
user = state.user
request = state.request
action = state.action

Removing Data

state = BaseStateManager.get_state()

# Remove specific key
state.remove('temporary_data')

# Clear all custom data (keeps request, user, action)
state.clear()

Custom State Manager

Create your own state manager for additional functionality:

from fastapi_mason.state import BaseStateManager
from typing import Optional

class CustomStateManager(BaseStateManager):
    """Custom state manager with additional properties"""

    @property
    def organization_id(self) -> Optional[int]:
        """Get current user's organization ID"""
        if self.user:
            return getattr(self.user, 'organization_id', None)
        return None

    @property
    def is_admin(self) -> bool:
        """Check if current user is admin"""
        if self.user:
            return getattr(self.user, 'is_admin', False)
        return False

    @property
    def permissions(self) -> list:
        """Get cached user permissions"""
        return self.get('cached_permissions', [])

    def cache_permissions(self, permissions: list):
        """Cache user permissions for this request"""
        self.set('cached_permissions', permissions)

    def log_activity(self, action: str, details: dict = None):
        """Log user activity"""
        activity = {
            'user_id': self.user.id if self.user else None,
            'action': action,
            'details': details or {},
            'timestamp': time.time(),
            'ip_address': self.request.client.host if self.request else None,
        }

        activities = self.get('activities', [])
        activities.append(activity)
        self.set('activities', activities)

# Use custom state manager in ViewSets
@viewset(router)
class CompanyViewSet(ModelViewSet[Company]):
    state_class = CustomStateManager  # Use custom state manager

    def get_queryset(self):
        # Access custom properties
        org_id = self.state.organization_id
        is_admin = self.state.is_admin

        if is_admin:
            return Company.all()
        elif org_id:
            return Company.filter(organization_id=org_id)
        else:
            return Company.filter(is_public=True)

    async def perform_create(self, obj):
        # Log activity
        self.state.log_activity('company_created', {
            'company_name': obj.name
        })

        return await super().perform_create(obj)

State management in FastAPI Mason provides a clean way to share request-scoped data across your application while maintaining thread safety and proper isolation between requests.