Middleware

Tri-phase middleware pipeline: execute logic before, around, or after handlers. Supports priority ordering, signature-based dependency injection, and timeout enforcement.

Execution Order

Update arrives
Before Middlewares
(highest priority first, signature-based DI)
Around Middlewares
(wrap the handler, control execution)
Handler
After Middlewares
(highest priority first, post-processing)

Phase Types

PhaseSignaturePurpose
beforeasync fn(update, client, patch_helper)Pre-processing: logging, auth checks, rate limiting
aroundfn(next_handler) → fn(update)Wraps handler: timing, retry, error boundaries
afterasync fn(update, client, patch_helper)Post-processing: analytics, cleanup, notifications

Quick Examples

Before middleware — logging
async def log_updates(update, client, patch_helper):
    print(f"Received: {type(update).__name__}")

manager = await patch(app)
await manager.add_middleware(log_updates, kind="before")
Around middleware — timing
import time

def timing_middleware(next_handler):
    async def wrapper(update):
        start = time.time()
        result = await next_handler(update)
        elapsed = time.time() - start
        print(f"Handler took {elapsed:.3f}s")
        return result
    return wrapper

await manager.add_middleware(timing_middleware, kind="around")

Priority

Higher priority values execute first. When two middlewares have the same priority, they execute in registration order.

# Priority 10 runs before priority 0
await manager.add_middleware(auth_check, kind="before", priority=10)
await manager.add_middleware(log_updates, kind="before", priority=0)

Signature-Based DI

Middleware functions receive arguments based on their parameter names. The manager inspects your function signature and injects only what you need:

# Only receives 'update' — efficient
async def simple_logger(update):
    print(type(update).__name__)

# Receives all three — full access
async def full_middleware(update, client, patch_helper):
    data = await patch_helper.get_data()
    # ...