This document provides detailed information about the SQLAlchemyStorage class and its usage with aiogram FSM.
SQLAlchemyStorage is a storage backend for aiogram's Finite State Machine (FSM) that persists state and data using SQLAlchemy. It provides asynchronous database operations and full compatibility with aiogram's storage interface.
SQLAlchemyStorage(
sessionmaker: sessionmaker[AsyncSession],
metadata: MetaData = MetaData(),
table_name: Optional[str] = 'aiogram_fsm_data',
key_builder: Optional[KeyBuilder] = None,
json_dumps: _JsonDumps = json.dumps,
json_loads: _JsonLoads = json.loads,
)sessionmaker(required): SQLAlchemy async sessionmaker instancemetadata: SQLAlchemy MetaData instance for table creationtable_name: Name of the database table (default:'aiogram_fsm_data')key_builder: Custom key builder for storage keys (default:DefaultKeyBuilder)json_dumps: JSON serialization function (default:json.dumps)json_loads: JSON deserialization function (default:json.loads)
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy.orm import declarative_base
from sqlalchemy_storage import SQLAlchemyStorage
# Create engine and sessionmaker
engine = create_async_engine("sqlite+aiosqlite:///database.db")
SessionLocal = async_sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)
Base = declarative_base()
# Initialize storage
storage = SQLAlchemyStorage(
sessionmaker=SessionLocal,
metadata=Base.metadata, # Recommended: avoid passing empty metadata
table_name="custom_fsm_table" # Optional: custom table name
)import orjson
storage = SQLAlchemyStorage(
sessionmaker=SessionLocal,
metadata=Base.metadata,
json_dumps=orjson.dumps,
json_loads=orjson.loads
)from aiogram.fsm.storage.base import KeyBuilder
class CustomKeyBuilder(KeyBuilder):
def build(self, key: StorageKey) -> str:
return f"custom_{key.bot_id}_{key.chat_id}_{key.user_id}"
storage = SQLAlchemyStorage(
sessionmaker=SessionLocal,
metadata=Base.metadata,
key_builder=CustomKeyBuilder()
)The storage automatically creates a table with the following structure:
CREATE TABLE aiogram_fsm_data (
id VARCHAR PRIMARY KEY,
state VARCHAR NULL,
data VARCHAR NULL
);id: Primary key built from bot_id, chat_id, and user_idstate: Current FSM state as stringdata: JSON-serialized user data
The SQLAlchemyStorage modifies the metadata passed to it by adding the FSM table definition. If no metadata is provided, it creates a new one. You can access the metadata through SQLAlchemyStorage.metadata to create all tables including the FSM storage table.
async def init_database():
"""Initialize database tables"""
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# Run once at application startup
import asyncio
asyncio.run(init_database())from sqlalchemy.pool import StaticPool
engine = create_async_engine(
"sqlite+aiosqlite:///database.db",
poolclass=StaticPool,
pool_pre_ping=True,
pool_recycle=300,
echo=True # Enable SQL logging for debugging
)Old way (deprecated):
Base = declarative_base()
storage = SQLAlchemyStorage(sessionmaker=SessionLocal, metadata=Base)New way:
Base = declarative_base()
storage = SQLAlchemyStorage(sessionmaker=SessionLocal, metadata=Base.metadata)The storage expects a sessionmaker[AsyncSession] instance. Make sure your sessionmaker is configured for async operations:
SessionLocal = async_sessionmaker(
bind=engine,
class_=AsyncSession,
expire_on_commit=False # Recommended for FSM usage
)- Connection Management: Use connection pooling for production deployments
- Table Names: Use descriptive table names when running multiple bots
- JSON Serialization: Consider using faster JSON libraries like
orjsonfor better performance - Database Cleanup: Implement periodic cleanup of old FSM records