Skip to main content

openframe-adapters-db-postgres

PostgreSQL database adapter for the OpenFrame Microservice Suite.

Part of the openframe-adapters monorepo. Implements BaseRepository[T] and HealthCheck from openframe-core using asyncpg.


Installation

pip install openframe-adapters-db-postgres

Required env var:

DATABASE_URL=postgresql://user:password@host:5432/dbname

Quick start

Raw dict mode

from openframe.adapters.db.postgres import PostgresSettings, PostgresRepository

settings = PostgresSettings()  # reads DATABASE_URL from env
repo = PostgresRepository(settings, table="items", id_column="id")

item = await repo.get("abc-123")          # dict | None
items, total = await repo.list(10, 0)     # ([dict, ...], int)
created = await repo.create({"name": "x"})
updated = await repo.update({"id": "abc-123", "name": "y"})
deleted = await repo.delete("abc-123")    # bool

Typed domain mode

from dataclasses import dataclass
from openframe.adapters.db.postgres import PostgresSettings, PostgresRepository

@dataclass
class Item:
    id: str
    name: str

class ItemRepository(PostgresRepository[Item]):
    _table = "items"
    _id_column = "id"

    def _row_to_entity(self, row) -> Item:
        return Item(**dict(row))

    def _entity_to_row(self, entity: Item) -> dict:
        return {"id": entity.id, "name": entity.name}

settings = PostgresSettings()
repo = ItemRepository(settings)
item: Item | None = await repo.get("abc-123")

Wiring into an application

For a real service, wire PostgresPlugin (the BasePort-satisfying plugin class) through ApplicationBootstrap.compose() from openframe-core. This gives you proper lifecycle management — initialize() / health() / shutdown() — for free, instead of constructing PostgresRepository directly and managing the pool yourself:

from openframe.core.runtime import ApplicationBootstrap
from openframe.core.ports import Capability
from openframe.adapters.db.postgres import PostgresPlugin, PostgresSettings

settings = PostgresSettings()  # reads DATABASE_URL from env
plugin = PostgresPlugin(settings, table="items", id_column="id")

async with ApplicationBootstrap.compose(plugin) as app:
    repo = app.get(Capability.PERSISTENCE)   # -> PostgresRepository
    item = await repo.get("abc-123")
# pool is closed automatically on exit (plugin.shutdown() ran)

compose() calls plugin.initialize() on entry and plugin.shutdown() on exit, so the pool is created, health-checked, and torn down without any manual lifecycle code. Requires openframe-core>=3.3.

Reach for a subclassed ApplicationBootstrap (with a configure() method) only when you need per-port config=/init_timeout= or conditional registration order; use app.registry as an escape hatch for anything neither tier covers. The PostgresRepository(settings) construction shown above under "Quick start" remains valid for tests, scripts, or any context that doesn't need plugin lifecycle management.


Configuration

All settings are read from environment variables.

Env var Type Default Description
DATABASE_URL str required Full asyncpg DSN
POOL_SIZE int 10 Pool min/max size
POOL_MAX_INACTIVE_CONN_LIFETIME float 300.0 Idle connection TTL (s)
POOL_COMMAND_TIMEOUT float 60.0 Per-statement timeout (s)
POOL_MAX_QUERIES int 50000 Queries per connection before recycle
CONNECTION_TIMEOUT float 30.0 Pool creation timeout (s)
OPERATION_TIMEOUT float 10.0 Per-operation timeout (s)
MAX_RETRIES int 3 Max retry attempts

Health checks

PostgresRepository implements the HealthCheck protocol from openframe-core.

alive = await repo.ping()       # SELECT 1 — fast liveness check
ready = await repo.is_ready()   # pg_tables query — full readiness check

Both methods return False on any failure and never raise.


Exception hierarchy

All exceptions are AdapterError subclasses from openframe.core.exceptions. Raw asyncpg exceptions never escape the adapter.

Situation Exception
Cannot connect to Postgres AdapterConnectionError
Invalid DATABASE_URL catalog AdapterConfigurationError
Query failed (constraint, syntax, etc.) AdapterQueryError
Entity not found AdapterNotFoundError
Operation exceeded timeout AdapterTimeoutError

Development

# from the package directory
pip install -e ".[dev]"
pytest tests/ -v

Protocol conformance

from openframe.core.ports import BaseRepository
from openframe.core.health import HealthCheck

repo = PostgresRepository(settings, table="items", id_column="id")
assert isinstance(repo, BaseRepository)   # True — structural check
assert isinstance(repo, HealthCheck)      # True — structural check

No inheritance from either Protocol is required or used.


License

MIT

Metadata

Release files for openframe-adapters-db-postgres 2.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openframe-adapters-db-postgres 2.0.3
File Size Uploaded
openframe_adapters_db_postgres-2.0.3.tar.gz 18.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openframe-adapters-db-postgres 2.0.3
File Interpreter ABI Platform
openframe_adapters_db_postgres-2.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 32.7 kB

Release files / openframe_adapters_db_postgres-2.0.3.tar.gz

Download URL openframe_adapters_db_postgres-2.0.3.tar.gz
Size 18.6 kB
Tags Source
SHA-256 checksum
How to use checksums
d7f7b500e86bfb8fba78d49d1f32e7782ffd985252409b978b47ce29a44ec96f
BLAKE2b-256 checksum
How to use checksums
2c73451fd0e1c056a4537c0bc7be3e04d40a0da5ca7e056b46c51e101124a89b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / openframe_adapters_db_postgres-2.0.3-py3-none-any.whl

Download URL openframe_adapters_db_postgres-2.0.3-py3-none-any.whl
Size 14.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a87353c2d090240675e06c00d332fd69e7d74a05dd1a6bc8d4c7e44237842fc6
BLAKE2b-256 checksum
How to use checksums
74c64b5570e894ed95a44a9dc38470999990943dc5dd10542861cdb4c152ab31
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

2.0.4

2 release files

This release

2.0.3 This release

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page