Skip to main content

Foundation layer for SQLAlchemy-based services with UoW, session management, and observability

Project description

sqlalchemy-foundation-kit

PyPI - Version PyPI - Python Version License

CI codecov Documentation

Ruff Type checked: mypy uv


Production-ready foundation for SQLAlchemy-based async services with Unit of Work, session management, and observability

sqlalchemy-foundation-kit is a standalone, batteries-included foundation for building async SQLAlchemy microservices. It bundles everything you commonly re-implement for every service into a single library:

  • PostgreSQL configuration — Pydantic-based settings with grouped connection / pool / query options
  • Session managementAsyncSessionManager with pgbouncer transaction-mode compatibility
  • Unit of Work patternAsyncSQLAlchemyUnitOfWork with automatic commit/rollback
  • Base ORM models — Pre-configured Base, mixins, custom types (PydanticJSONB, UnConstrainedEnum)
  • Observability — Prometheus connection-pool metrics and OpenTelemetry tracing
  • DI integration — Ready-to-use providers for dishka and dependency-injector

Only sqlalchemy[asyncio] and pydantic are required by default — everything else is an opt-in extra.

Key Features

Single dependency — All foundation pieces in one place
Unit of Work pattern — Transactional consistency with automatic commit/rollback
Connection pool managementAsyncSessionManager with metrics and health checks
pgbouncer compatible — Custom connection class for transaction mode
Observability built-in — Prometheus metrics + OpenTelemetry tracing
Type-safe configuration — Pydantic settings with validation
Base ORM models — Pre-configured Base with naming conventions and mixins

Installation

# Basic installation (core functionality)
pip install sqlalchemy-foundation-kit

# With pydantic-settings support
pip install sqlalchemy-foundation-kit[settings]

# With Prometheus metrics
pip install sqlalchemy-foundation-kit[metrics]

# With OpenTelemetry tracing
pip install sqlalchemy-foundation-kit[telemetry]

# With dishka dependency injection
pip install sqlalchemy-foundation-kit[dishka]

# With dependency-injector containers
pip install sqlalchemy-foundation-kit[dependency-injector]

# With orjson serialization
pip install sqlalchemy-foundation-kit[orjson]

# All features
pip install sqlalchemy-foundation-kit[all]

Quick Start

1. Configuration (with pydantic-settings)

from pydantic import SecretStr
from pydantic_settings import BaseSettings
from sqlalchemy_foundation_kit.contrib.settings import (
    BasePostgresConfig,
    ConnectionSettings,
    PoolSettings,
    QuerySettings,
)

class Settings(BaseSettings):
    postgres: BasePostgresConfig = BasePostgresConfig(
        connection=ConnectionSettings(
            host="localhost",
            port=5432,
            user="postgres",
            password=SecretStr("secret"),
            database="mydb",
        ),
        pool=PoolSettings(),  # Uses defaults
        query=QuerySettings(),  # Uses defaults
        application_name="my-service",
    )

Or implement PostgresSettingsProtocol directly:

from dataclasses import dataclass
from sqlalchemy_foundation_kit import (
    PostgresSettingsProtocol,
    ConnectionSettingsProtocol,
    PoolSettingsProtocol,
    QuerySettingsProtocol,
)

@dataclass
class MyConnectionSettings:
    host: str = "localhost"
    port: int = 5432
    user: str = "postgres"
    database: str = "mydb"

@dataclass
class MyPoolSettings:
    kind: str = "async_adapted_queue"
    size: int | None = 10
    max_overflow: int | None = 20
    pre_ping: bool = True
    recycle: int | None = 3600
    timeout: float | None = 30.0

@dataclass
class MyQuerySettings:
    echo: bool = False
    statement_cache_size: int | None = 0
    prepared_statement_cache_size: int | None = 0
    isolation_level: str | None = None

class PostgresConfig:
    connection: ConnectionSettingsProtocol = MyConnectionSettings()
    pool: PoolSettingsProtocol = MyPoolSettings()
    query: QuerySettingsProtocol = MyQuerySettings()
    application_name: str = "my-service"
    db_schema: str | None = None
    use_orjson_serialization: bool = True
    jit: str | None = "off"
    
    def to_dsn(self) -> str:
        return f"postgresql+asyncpg://{self.connection.user}@{self.connection.host}:{self.connection.port}/{self.connection.database}"

2. Define ORM Models

from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy_foundation_kit import BaseTable, DatetimeColumnsMixin

class UserDB(BaseTable, DatetimeColumnsMixin):
    __tablename__ = "users"
    __created_at_index__ = True  # Index on created_at

    id: Mapped[UUID] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(unique=True)
    username: Mapped[str]

3. Create Session Manager

from sqlalchemy_foundation_kit import create_async_session_manager

async def main():
    session_manager = create_async_session_manager(
        settings.postgres,
        metrics=postgres_metrics,  # optional
    )
    
    async with session_manager.get_transaction() as session:
        user = UserDB(id=uuid4(), email="user@example.com", username="user")
        session.add(user)
        # Auto-commit on exit

4. Unit of Work Pattern

from unit_of_work_kit import AsyncSQLAlchemyUnitOfWork, AsyncSQLAlchemyUowTransaction

# Define your transaction with repositories
class MyTransaction(AsyncSQLAlchemyUowTransaction):
    @property
    def users(self) -> UserRepository:
        if self._users is None:
            self._users = PostgresUserRepository(self.session)
        return self._users

# Create UoW
class MyUnitOfWork(AsyncSQLAlchemyUnitOfWork[MyTransaction]):
    def __init__(self, session_maker):
        super().__init__(session_maker, transaction_factory=MyTransaction)

# Usage in use case
class CreateUserUseCase:
    def __init__(self, uow: MyUnitOfWork):
        self._uow = uow
    
    async def execute(self, email: str) -> User:
        async with self._uow.transaction() as tx:
            user = await tx.users.create(email=email)
            await tx.outbox.create(UserCreatedEvent(...))
        return user

What's Included

Core (always available)

  • Session Management: AsyncSessionManager, AsyncCConnection
  • Base ORM: Base, BaseTable, DatetimeColumnsMixin, UnConstrainedEnum, PydanticJSONB
  • Unit of Work: AsyncUnitOfWork, AsyncSQLAlchemyUnitOfWork, IsolationLevel
  • Protocols: PostgresSettingsProtocol, PostgresMetricsProtocol
  • Utilities: build_engine_kwargs, resolve_pool_class, load_orm_metadata

Contrib (optional dependencies)

contrib.settings (requires [settings])

from sqlalchemy_foundation_kit.contrib.settings import (
    BasePostgresConfig,
    BasePostgresMigrationsConfig,
)
  • BasePostgresConfig — Pydantic-based PostgreSQL configuration
  • BasePostgresMigrationsConfig — Migrations configuration

contrib.metrics (requires [metrics])

from sqlalchemy_foundation_kit.contrib.metrics import PostgresMetrics
  • PostgresMetrics — Prometheus metrics for connection pool
  • Tracks: pool size, checked out connections, checkout duration, errors

contrib.di (requires [dishka])

from sqlalchemy_foundation_kit.contrib.di import (
    AsyncDatabaseProvider,
    AsyncUnitOfWorkProvider,
    PrometheusPostgresMetricsProvider,
)
  • AsyncDatabaseProvider — Provides AsyncSessionManager and async_sessionmaker
  • AsyncUnitOfWorkProvider — Provides AsyncUnitOfWork
  • PrometheusPostgresMetricsProvider — Provides Prometheus metrics integration

Example with Dishka:

from dishka import make_async_container
from sqlalchemy_foundation_kit.contrib.di import (
    AsyncDatabaseProvider,
    AsyncUnitOfWorkProvider,
    PrometheusPostgresMetricsProvider,
)

container = make_async_container(
    AsyncDatabaseProvider(),
    AsyncUnitOfWorkProvider(),
    PrometheusPostgresMetricsProvider(),
    # ... your providers
)

contrib.dependency_injector (requires [dependency-injector])

from sqlalchemy_foundation_kit.contrib.dependency_injector import (
    DatabaseContainer,
    AsyncDatabaseResourceProvider,
    PrometheusMetricsContainer,
)
  • DatabaseContainer — Container providing session_manager, session_maker, and uow
  • AsyncDatabaseResourceProvider — Manual lifecycle management helper
  • PrometheusMetricsContainer — Container for Prometheus metrics integration

Example with dependency-injector:

from dependency_injector import containers, providers
from sqlalchemy_foundation_kit.contrib.dependency_injector import (
    DatabaseContainer,
    PrometheusMetricsContainer,
)

class AppContainer(containers.DeclarativeContainer):
    config = providers.Singleton(Settings)
    
    metrics = providers.Container(
        PrometheusMetricsContainer,
        metrics_settings=config.provided.metrics,
        default_prefix=providers.Object("myapp"),
        postgres_settings=config.provided.postgres,
    )
    
    database = providers.Container(
        DatabaseContainer,
        postgres_config=config.provided.postgres,
        metrics=metrics.postgres_metrics,
    )

container = AppContainer()
await container.init_resources()

# Use dependencies
uow = container.database.uow()
async with uow.transaction() as tx:
    user = await tx.users.create(...)

contrib.telemetry (requires [telemetry])

from sqlalchemy_foundation_kit.contrib.telemetry import (
    instrument_sqlalchemy,
    instrument_asyncpg,
    TracedAsyncUnitOfWork,
)
  • instrument_sqlalchemy() — OpenTelemetry instrumentation for SQLAlchemy
  • instrument_asyncpg() — OpenTelemetry instrumentation for asyncpg
  • TracedAsyncUnitOfWork — UoW with automatic span creation

Example with OpenTelemetry:

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, BatchSpanProcessor

# Setup tracer
provider = TracerProvider()
processor = BatchSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# Instrument SQLAlchemy
from sqlalchemy_foundation_kit.contrib.telemetry import (
    instrument_sqlalchemy,
    TracedAsyncUnitOfWork,
)

instrument_sqlalchemy(engine=engine)

# Use traced UoW
uow = TracedAsyncUnitOfWork(
    session_maker=session_maker,
    transaction_factory=MyTransaction,
    service_name="my-service",
)

async with uow.transaction() as tx:
    # Automatically creates span "uow.transaction"
    user = await tx.users.create(...)

Architecture

graph LR
    A[Your Service] --> B[sqlalchemy-foundation-kit]
    B --> C[SQLAlchemy asyncio]
    B --> D[Pydantic]
    B -.optional.-> E[Prometheus]
    B -.optional.-> F[OpenTelemetry]
    B -.optional.-> G[dishka / dependency-injector]

Documentation

Full documentation is available at https://bedrock-python.github.io/sqlalchemy-foundation-kit/

License

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

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

sqlalchemy_foundation_kit-0.0.0.tar.gz (45.1 kB view details)

Uploaded Source

Built Distribution

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

sqlalchemy_foundation_kit-0.0.0-py3-none-any.whl (63.9 kB view details)

Uploaded Python 3

File details

Details for the file sqlalchemy_foundation_kit-0.0.0.tar.gz.

File metadata

  • Download URL: sqlalchemy_foundation_kit-0.0.0.tar.gz
  • Upload date:
  • Size: 45.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sqlalchemy_foundation_kit-0.0.0.tar.gz
Algorithm Hash digest
SHA256 990003bcbda7ed0cf86a946151bf528eff930b84d7cb254414e59bc1098d105e
MD5 640495def46a8494f485be6e9d46a5e3
BLAKE2b-256 af184081734a56a4aae340d7af87421cc179de83aca1ab7e204e72ad789a0dc7

See more details on using hashes here.

File details

Details for the file sqlalchemy_foundation_kit-0.0.0-py3-none-any.whl.

File metadata

  • Download URL: sqlalchemy_foundation_kit-0.0.0-py3-none-any.whl
  • Upload date:
  • Size: 63.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sqlalchemy_foundation_kit-0.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6b05ea140182241bd0c778857a658d8021c832cc4a03674636efcc8a0ba735ce
MD5 7a9adfcdc8f305223566708855b21445
BLAKE2b-256 38d0b153262fa1b2ab17bd173182a815dbbcff5f3bd3b9306dab55c69a23fe36

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