Skip to main content

Async Redis manager with FastAPI integration, connection pooling and retry mechanism

Project description

FastAPI Redis Utils

CI/CD codecov PyPI PyPI - Downloads License: MIT Python 3.11 Python 3.12 Python 3.13 Code style: ruff Type checked with mypy Security: bandit GitHub release (latest by date)

Async Redis manager with FastAPI integration, connection pooling and retry mechanism.

Features

  • 🔄 Async-first - Full async/await support
  • 🏊 Connection pooling - Efficient connection management
  • 🔁 Retry mechanism - Automatic retries on failures
  • FastAPI integration - Ready-to-use FastAPI dependencies
  • 🏥 Health check - Built-in connection status monitoring
  • 🛡️ Type safety - Full type hints support
  • 📦 BaseRepository - Base repository class with Pydantic model support

Documentation

Installation

From PyPI

uv add fastapi-redis-utils

From Git repository

uv add git+https://github.com/serafinovsky/fastapi-redis-utils.git

For development

git clone https://github.com/serafinovsky/fastapi-redis-utils.git
cd fastapi-redis-utils
uv sync --dev

Quick Start

Basic Usage

import asyncio
from fastapi_redis_utils import RedisManager

async def main():
    # Create manager
    redis_manager = RedisManager(
        dsn="redis://localhost:6379",
        max_connections=20,
        retry_attempts=3
    )

    # Connect
    await redis_manager.connect()

    # Use
    client = redis_manager.get_client()
    await client.set("key", "value")
    value = await client.get("key")

    # Close
    await redis_manager.close()

asyncio.run(main())

FastAPI Integration

from fastapi import FastAPI, Depends
from fastapi_redis_utils import RedisManager, create_redis_client_dependencies
import redis.asyncio as redis

app = FastAPI()

# Create Redis manager
redis_manager = RedisManager(
    dsn="redis://localhost:6379"
)

# Create FastAPI dependency
get_redis_client = create_redis_client_dependencies(redis_manager)

@app.on_event("startup")
async def startup_event():
    """Connect to Redis on application startup"""
    await redis_manager.connect()

@app.on_event("shutdown")
async def shutdown_event():
    """Close connection on application shutdown"""
    await redis_manager.close()

@app.get("/cache/{key}")
async def get_cached_data(key: str, redis_client: redis.Redis = Depends(get_redis_client)):
    """Get data from cache"""
    value = await redis_client.get(key)
    return {"key": key, "value": value}

@app.post("/cache/{key}")
async def set_cached_data(
    key: str,
    value: str,
    redis_client: redis.Redis = Depends(get_redis_client)
):
    """Save data to cache"""
    await redis_client.set(key, value)
    return {"key": key, "value": value, "status": "saved"}

@app.get("/health")
async def health_check():
    """Check Redis connection status"""
    is_healthy = await redis_manager.health_check()
    return {"redis_healthy": is_healthy}

Using BaseRepository with Separate Create and Update Schemas

import uuid
from uuid import UUID
from fastapi import HTTPException, status
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from fastapi_redis_utils import BaseRepository, RedisManager, BaseResultModel

class CreateDemoSchema(BaseModel):
    field1: str
    field2: str


class UpdateDemoSchema(BaseModel):
    field1: str | None = None
    field2: str | None = None


class DemoSchema(BaseResultModel):
    id: str | None = None
    field1: str
    field2: str

    def set_id(self, id: str) -> None:
        self.id = id


class DemoRepository(BaseRepository[CreateDemoSchema, UpdateDemoSchema, DemoSchema]):
    pass


demo_crud = DemoRepository(redis_manager, CreateDemoSchema, UpdateDemoSchema, DemoSchema)


@app.post("/repo/", response_model=DemoSchema, status_code=status.HTTP_201_CREATED)
async def create_demo(demo_model: CreateDemoSchema) -> DemoSchema:
    """Create a new demo record."""
    demo_id = str(uuid.uuid4())
    return await demo_crud.create(demo_id, demo_model)


@app.get("/repo/{demo_id}", response_model=DemoSchema)
async def get_demo(demo_id: UUID) -> DemoSchema:
    """Get a demo record by ID."""
    demo = await demo_crud.get(str(demo_id))
    if demo is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Demo record with ID '{demo_id}' not found",
        )

    return demo


@app.get("/repo/", response_model=list[DemoSchema])
async def list_demos(limit: int = 100) -> list[DemoSchema]:
    """List all demo records"""
    return await demo_crud.list(limit=limit)


@app.put("/repo/{demo_id}", response_model=DemoSchema)
async def update_demo(demo_id: UUID, demo_update: UpdateDemoSchema) -> DemoSchema:
    """Update a demo record."""
    updated_demo = await demo_crud.update(str(demo_id), demo_update)
    if updated_demo is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Demo record with ID '{demo_id}' not found",
        )
    return updated_demo


@app.delete("/repo/{demo_id}")
async def delete_demo(demo_id: UUID) -> dict[str, UUID]:
    """Delete a demo record."""
    deleted = await demo_crud.delete(str(demo_id))
    if not deleted:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Demo record with ID '{demo_id}' not found",
        )
    return {"id": demo_id}


@app.get("/repo/{demo_id}/exists")
async def check_demo_exists(demo_id: UUID) -> dict[str, UUID | bool]:
    """Check if a demo record exists."""
    exists = await demo_crud.exists(str(demo_id))
    return {"id": demo_id, "exists": exists}

Executing Operations with Retry

async def complex_operation():
    async def operation():
        client = redis_manager.get_client()
        # Complex Redis operation
        result = await client.execute_command("COMPLEX_COMMAND")
        return result

    # Automatic retries on failures
    result = await redis_manager.execute_with_retry(operation)
    return result

Configuration

RedisManager Parameters

Parameter Type Default Description
dsn str - DSN for Redis connection
max_connections int 20 Maximum number of connections in pool
retry_attempts int 3 Number of reconnection attempts
retry_delay float 1.0 Base delay between attempts (seconds)
socket_connect_timeout int 5 Socket connection timeout (seconds)
socket_timeout int 5 Socket operation timeout (seconds)

API Reference

RedisManager

Main class for managing Redis connections.

Methods

  • connect() - Connect to Redis with retry mechanism
  • ensure_connection() - Ensure connection availability
  • close() - Close connection and cleanup resources
  • health_check() - Check connection status
  • get_client() - Get Redis client
  • execute_with_retry() - Execute operations with retry

create_redis_client_dependencies

Creates FastAPI dependency for getting Redis client.

BaseRepository

Base repository class for working with Pydantic models in Redis. Supports separate schemas for create, update, and result operations with partial updates.

Generic Parameters

  • CreateSchemaType - Pydantic model for create operations
  • UpdateSchemaType - Pydantic model for update operations (all fields optional)
  • ResultSchemaType - Pydantic model for result operations (must inherit from BaseResultModel)

Core Methods

  • create(key, data: CreateSchemaType, ttl=None) - Create record
  • get(key) - Get record (returns ResultSchemaType)
  • update(key, data: UpdateSchemaType, ttl=None) - Update record with partial update (only set fields)
  • delete(key) - Delete record
  • exists(key) - Check record existence
  • list(pattern="*", limit=None) - Get list of records
  • count(pattern="*") - Count records
  • set_ttl(key, ttl) - Set TTL
  • get_ttl(key) - Get TTL
  • clear(pattern="*") - Clear records

Partial Update Feature

The update method performs partial updates - only fields that are set in the update schema will be modified. Fields with None values are ignored.

Development

Install development dependencies

uv sync --dev

Run tests

uv run pytest

Code checks

uv run ruff check .
uv run mypy .

Build package

uv run build

Makefile Commands

The project includes convenient Makefile commands for development:

# Main commands
make install          # Install development dependencies
make test            # Run tests
make lint            # Check code with linters
make format          # Format code
make build           # Build package
make clean           # Clean temporary files

# Version management
make version         # Show current version

# Publishing
make publish         # Create and push git tag with current version
make publish-dry-run # Show what would be done without creating tag
make release         # Full release: build, test, tag and push

# Additional commands
make tags            # List all git tags
make check           # Full pre-commit check
make example-fastapi # Run FastAPI example

Release Workflow

  1. Update version in fastapi_redis_utils/__init__.py
  2. Run full release: make release
  3. Or step by step:
make test          # Run tests
make build         # Build package
make publish       # Create and push tag

License

MIT License - see LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_redis_utils-1.0.3.tar.gz (79.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_redis_utils-1.0.3-py3-none-any.whl (10.0 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_redis_utils-1.0.3.tar.gz.

File metadata

  • Download URL: fastapi_redis_utils-1.0.3.tar.gz
  • Upload date:
  • Size: 79.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for fastapi_redis_utils-1.0.3.tar.gz
Algorithm Hash digest
SHA256 33ba1a188e36edbb7bdbe3cb6cdb29ac5efceac8baf53e226ff3ec45000567d0
MD5 525907ff1c674ad2766b4d366046b262
BLAKE2b-256 7ff0ee33d9461b9ad679e17a984cef827eead9f89e0a659f9700159c358e5d49

See more details on using hashes here.

File details

Details for the file fastapi_redis_utils-1.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_redis_utils-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 16f5950274bb921f297ac9835fcd9cb0f9b0e81285740ac7c7a7ce86e74d9e5d
MD5 0d5fac6b87867a07aaa6b1fb7f55a06d
BLAKE2b-256 8bcde00ddb98d2097187fbea58c17d68e9de6ccc392febf6172d4c5f3c723b92

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page