Skip to content
GitHubDiscord

SQL (lexigram-sql)

SQL database abstractions for Lexigram Framework — Postgres, MySQL, SQLite with migrations, repositories, and query building.


lexigram-sql provides an async SQLAlchemy ORM layer with the repository pattern, unit-of-work, connection pooling, multi-database support, Alembic migrations, and optional HMAC audit checksums. All database operations are wired through DatabaseProviderProtocol in the DI container.


Terminal window
uv add lexigram lexigram-sql
# With async PostgreSQL driver
uv add "lexigram-sql[postgres]"
# With async MySQL driver
uv add "lexigram-sql[mysql]"
# With SQLite async driver
uv add "lexigram-sql[sqlite]"
from lexigram import Application, StandardModule
from lexigram.di.module import Module, module
from lexigram.sql import DatabaseModule
from lexigram.sql.config import DatabaseConfig
@module(
imports=[
DatabaseModule.configure(
DatabaseConfig(url="postgresql+asyncpg://user:pass@localhost/mydb")
)
]
)
class AppModule(Module):
pass
async def main() -> None:
async with Application.boot(modules=[AppModule]) as app:
from lexigram.contracts.data.sql.database import DatabaseProviderProtocol
db = await app.container.resolve(DatabaseProviderProtocol)
result = await db.execute_query("SELECT 1")
if __name__ == "__main__":
import asyncio
asyncio.run(main())

Zero-config usage: Call DatabaseModule.configure() with no arguments to use all defaults (SQLite).

application.yaml
sql:
backend:
url: "${LEX_SQL__BACKEND__URL}"
pool:
min_size: 2
max_size: 10
timeout: 30
operations:
echo: false
Section titled “Option 2 — Profiles + Environment Variables (recommended)”
Terminal window
export LEX_SQL__BACKEND__URL=postgresql+asyncpg://user:pass@host/db
export LEX_SQL__POOL__MAX_SIZE=20
export LEX_SQL__POOL__TIMEOUT=60
from lexigram.sql import DatabaseModule
from lexigram.sql.config import DatabaseConfig
DatabaseModule.configure(
DatabaseConfig(
url="postgresql+asyncpg://user:pass@localhost/mydb",
)
)
FieldDefaultEnv varDescription
backend.url"sqlite:///piccolina.db"LEX_SQL__BACKEND__URLDatabase connection URL
pool.min_size1LEX_SQL__POOL__MIN_SIZEMinimum pool connections
pool.max_size10LEX_SQL__POOL__MAX_SIZEMaximum pool connections
pool.timeout30LEX_SQL__POOL__TIMEOUTPool acquire timeout (seconds)
operations.echoFalseLEX_SQL__OPERATIONS__ECHOEcho SQL statements
audit_hmac_keyNoneLEX_SQL__AUDIT_HMAC_KEYHMAC key for audit checksums
MethodDescription
DatabaseModule.configure(config, enable_migrations, migration_dir)Configure with explicit DatabaseConfig
DatabaseModule.scope(*repositories)Scope repository classes into a feature module
DatabaseModule.stub(config=None)In-memory SQLite for testing
  • Repository patternSQLRepository base class with find, create, update, delete, count
  • Unit of workAbstractUnitOfWork tracks changes and publishes domain events on commit
  • Multi-databaseNamedDatabaseConfig for multiple backends resolved via Annotated[DatabaseProviderProtocol, Named("analytics")]
  • Connection pooling — SQLAlchemy async pool with configurable min/max size
  • Alembic migrations — auto-run on boot in development; disabled by default in production
  • HMAC audit checksums — optional signing of write operations for integrity verification
  • Production security — blocks default passwords (:password@, :postgres@, etc.) when LEX_ENV=production
from lexigram import Application
from lexigram.sql import DatabaseModule
from lexigram.sql.config import DatabaseConfig
async def test_repository():
async with Application.boot(
modules=[
DatabaseModule.stub(
DatabaseConfig(url="sqlite+aiosqlite:///:memory:")
)
]
) as app:
db = await app.container.resolve(DatabaseProviderProtocol)
# run your test queries
FileWhat it contains
src/lexigram/sql/module.pyDatabaseModule.configure(), .scope(), .stub()
src/lexigram/sql/config.pyDatabaseConfig, DatabasePoolConfig, NamedDatabaseConfig
src/lexigram/sql/di/provider.pyDatabaseProvider boot and registration
src/lexigram/sql/repositories/base.pySQLRepository base class
src/lexigram/sql/unit_of_work/base.pyAbstractUnitOfWork