Skip to main content

A FastAPI-style dependency injection system for aiogram Telegram bots. This library brings clean, type-safe dependency injection to your aiogram handlers, making your code more modular, testable, and maintainable.

Project description

Aiogram Dependency Injection

A FastAPI-style dependency injection system for aiogram Telegram bots. This library brings clean, type-safe dependency injection to your aiogram handlers, making your code more modular, testable, and maintainable.

Features

  • 🚀 FastAPI-style syntax - Familiar Depends() decorator
  • 🔄 Multiple dependency scopes - Singleton, Request, and Transient
  • 🏗️ Nested dependencies - Dependencies can depend on other dependencies
  • Async support - Both sync and async dependency functions
  • 🛡️ Circular dependency detection - Prevents infinite loops
  • 🧪 Type-safe - Full type hints support
  • 💾 Smart caching - Efficient resource management

Installation

pip install aiogram-dependency

Or install from source:

git clone https://github.com/AstralMortem/aiogram-dependency
cd aiogram-dependency
pip install -e .

Quick Start

import asyncio
from aiogram import Bot, Dispatcher, F
from aiogram.types import Message

from aiogram_dependency import Depends, DependencyMiddleware, Scope

# Define your dependencies
class DatabaseConnection:
    def __init__(self, connection_string: str):
        self.connection_string = connection_string
    
    async def query(self, sql: str):
        # Your database logic here
        return f"Result for: {sql}"

class UserService:
    def __init__(self, db: DatabaseConnection):
        self.db = db
    
    async def get_user_profile(self, user_id: int):
        return await self.db.query(f"SELECT * FROM users WHERE id = {user_id}")

# Dependency factories
async def get_database() -> DatabaseConnection:
    return DatabaseConnection("postgresql://localhost/mydb")

async def get_user_service(
    db: DatabaseConnection = Depends(get_database, scope=Scope.SINGLETON)
) -> UserService:
    return UserService(db)

# Handler with dependency injection
async def profile_handler(
    message: Message,
    user_service: UserService = Depends(get_user_service)
):
    if not message.from_user:
        await message.answer("User not found")
        return
    
    profile = await user_service.get_user_profile(message.from_user.id)
    await message.answer(f"Your profile: {profile}")

# Setup bot
async def main():
    bot = Bot(token="YOUR_BOT_TOKEN")
    dp = Dispatcher()
    
    # Register dependency injection middleware
    dp.message.middleware(DependencyMiddleware())
    
    # Register handlers
    dp.message.register(profile_handler, F.text == "/profile")
    
    await dp.start_polling(bot)

if __name__ == "__main__":
    asyncio.run(main())

Dependency Scopes

Singleton

Created once and shared across all requests globally.

async def get_database() -> DatabaseConnection:
    return DatabaseConnection("connection_string")

async def handler(
    message: Message,
    db: DatabaseConnection = Depends(get_database, scope=DependencyScope.SINGLETON)
):
    # Same database instance for all users
    pass

Request (Default)

Created once per user/chat and cached for subsequent calls in the same context.

async def get_user_service() -> UserService:
    return UserService()

async def handler(
    message: Message,
    service: UserService = Depends(get_user_service, scope=DependencyScope.REQUEST)
):
    # Same service instance for this user, different for other users
    pass

Transient

Created fresh every time it's requested.

async def get_timestamp() -> float:
    return time.time()

async def handler(
    message: Message,
    timestamp: float = Depends(get_timestamp, scope=DependencyScope.TRANSIENT)
):
    # New timestamp every time
    pass

Advanced Usage

Annotated

You can use Annotated type to make it more readable.

from typing import Annotated
from aiogram_dependency import Depends
from aiogram import Dispatcher,filters,types

async def get_database() -> DatabaseConnection:
    return DatabaseConnection("postgresql://localhost/db")

DatabaseDep = Annotated[DatabaseConnection, Depends(get_database)]

async def get_user(db: DatabaseDep):
    return db.execute('SQL')

UserDep = Annotated[dict, Depends(get_user)]

dp = Dispatcher()

@dp.message(filters.CommandStart())
async def start(message:types.Message, user: UserDep):
    return await message.answer(user['username'])

Nested Dependencies

Dependencies can depend on other dependencies:

async def get_database() -> DatabaseConnection:
    return DatabaseConnection("postgresql://localhost/db")

async def get_user_repository(
    db: DatabaseConnection = Depends(get_database)
) -> UserRepository:
    return UserRepository(db)

async def get_user_service(
    user_repo: UserRepository = Depends(get_user_repository),
    notification_service: NotificationService = Depends(get_notification_service)
) -> UserService:
    return UserService(user_repo, notification_service)

Using Event Data in Dependencies

Dependencies can access the current event and handler data:

async def get_current_user(event: Message) -> Optional[User]:
    return event.from_user

async def get_user_permissions(
    event: Message,
    data: dict,
    current_user: User = Depends(get_current_user)
) -> List[str]:
    # Access event, data, and other dependencies
    if current_user and current_user.id in data.get('admins', []):
        return ['admin', 'user']
    return ['user']

async def admin_handler(
    message: Message,
    permissions: List[str] = Depends(get_user_permissions)
):
    if 'admin' not in permissions:
        await message.answer("Access denied")
        return
    
    await message.answer("Welcome, admin!")

Custom Registry and Resolver

For advanced use cases, you can customize the dependency system:

from aiogram_dependency.registry import DependencyRegistry
from aiogram_dependency.resolver import DependencyResolver
from aiogram_dependency import DependencyMiddleware

# Create custom registry
registry = DependencyRegistry()

# Pre-populate with some dependencies
registry.set_dependency(
    get_database, 
    DatabaseConnection("custom://connection"),
    DependencyScope.SINGLETON,
    "global"
)

# Use custom middleware
middleware = DependencyMiddleware(registry)
dp.message.middleware(middleware)

Testing

The library is fully testable. Here's an example:

Run the full test suite:

# Install test dependencies
pip install pytest pytest-asyncio

# Run tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=aiogram_dependency --cov-report=html

License

This project is licensed under the MIT License - see the LICENSE file for details.

Related Projects

  • aiogram - Modern and fully asynchronous framework for Telegram Bot API
  • FastAPI - Modern, fast web framework for building APIs with Python

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

aiogram_dependency-1.0.1.tar.gz (5.6 kB view details)

Uploaded Source

Built Distribution

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

aiogram_dependency-1.0.1-py3-none-any.whl (7.2 kB view details)

Uploaded Python 3

File details

Details for the file aiogram_dependency-1.0.1.tar.gz.

File metadata

  • Download URL: aiogram_dependency-1.0.1.tar.gz
  • Upload date:
  • Size: 5.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for aiogram_dependency-1.0.1.tar.gz
Algorithm Hash digest
SHA256 0524313a96071947351101baad93768ec0b38bf472ab41e2fcc5d25a0254e812
MD5 3c4666d77d097fcbc0207efc1d7f4dde
BLAKE2b-256 70e1d0c5223d104ccff7306e1bf4f831a26d34e0924113b34ef7f39a274cf40e

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiogram_dependency-1.0.1.tar.gz:

Publisher: release.yaml on AstralMortem/aiogram-dependency

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aiogram_dependency-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for aiogram_dependency-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e4a7a9ff9eb726553a6aba7e1e0e69a1e002baccc05802ae8ec3324e4641da44
MD5 3b6a0bdb5596f16287d4bd10147c30e7
BLAKE2b-256 353732206eb1cd981e89e684f14152940be8082a98171795d9eb8afb1a50f980

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiogram_dependency-1.0.1-py3-none-any.whl:

Publisher: release.yaml on AstralMortem/aiogram-dependency

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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