Skip to main content

db_handler

A small, opinionated Python package that wraps Supabase with a Pydantic-typed repository pattern. It consolidates the implementations that previously lived copy-pasted inside several projects (Cyclone, JDBOT, OpinionBlogger, TexasLawBrandEngine — see code_base/ for the originals).

Three pieces:

  • DatabaseManager — abstract base class describing the CRUD surface.
  • SupabaseManager — concrete DatabaseManager backed by Supabase / PostgREST, with retries, JSON-safe payload coercion, duplicate-key handling via KeyError, and a NOT_NULL sentinel.
  • BaseRepository[T] — generic per-table repo bound to a Pydantic model.

Install

This package is not on PyPI. Install directly from GitHub:

pip install "git+https://github.com/tjdaley/db_handler.git"

Pin a tag or commit for reproducibility:

pip install "git+https://github.com/tjdaley/db_handler.git@v0.1.0"

In requirements.txt:

db_handler @ git+https://github.com/tjdaley/db_handler.git@v0.1.0

In pyproject.toml (PEP 508):

dependencies = [
    "db_handler @ git+https://github.com/tjdaley/db_handler.git@v0.1.0",
]

Configuration

SupabaseManager reads credentials from constructor arguments first, then falls back to environment variables:

Setting Constructor arg Env var
Project URL url SUPABASE_URL
Service / anon key key SUPABASE_SERVICE_ROLE_KEY (then SUPABASE_KEY)
from db_handler import SupabaseManager

# from env vars
db = SupabaseManager()

# explicit
db = SupabaseManager(url="https://xxx.supabase.co", key="ey...")

# offline tests — skip the connection probe
db = SupabaseManager(url="...", key="...", verify_connection=False)

Usage

from pydantic import BaseModel
from db_handler import BaseRepository, SupabaseManager, NOT_NULL


class Attorney(BaseModel):
    id: int
    firm_id: int
    name: str
    bar_number: str | None = None


class AttorneyRepo(BaseRepository[Attorney]):
    def __init__(self, manager):
        super().__init__(manager, "attorneys", Attorney)

    # Table-specific helpers go here
    def by_firm_id(self, firm_id: int) -> list[Attorney]:
        rows, _ = self.select_many({"firm_id": firm_id})
        return rows

    def with_bar_number(self) -> list[Attorney]:
        rows, _ = self.select_many({"bar_number": NOT_NULL})
        return rows


db = SupabaseManager()
attorneys = AttorneyRepo(db)

attorney = attorneys.insert({"firm_id": 17, "name": "Atticus Finch"})
fetched = attorneys.select_one({"id": attorney.id})
attorneys.update(attorney.id, {"bar_number": "TX-123456"})
attorneys.delete(attorney.id)

Filtering

select_one / select_many accept a condition dict. Values map as follows:

Value Translates to
scalar (int, str, ...) field = value
None field IS NULL
NOT_NULL field IS NOT NULL
list / tuple / set field IN (...)

Upsert

attorneys.upsert(
    {"firm_id": 17, "name": "Atticus Finch", "bar_number": "TX-123456"},
    on_conflict="bar_number",
)

Duplicate keys

insert raises KeyError on a unique-constraint violation, with the offending column and value parsed from the PostgREST error detail.

JSON-safe payloads

insert / upsert / update automatically coerce datetime, date, Enum, UUID, and Decimal values (recursively, including inside nested dicts/lists) so that PostgREST can serialize them.

Logging

The library uses logging.getLogger("db_handler.*") and does not mutate log levels of httpx or postgrest. Configure those in your application:

import logging
logging.getLogger("db_handler").setLevel(logging.INFO)
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("postgrest").setLevel(logging.WARNING)

Adding a new backend

Implement DatabaseManager for any backend; repositories don't care:

from db_handler import DatabaseManager, BaseRepository

class SqliteManager(DatabaseManager):
    ...

repo = BaseRepository(SqliteManager(...), "attorneys", Attorney)

Repo layout

db_handler/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── db_handler/
│       ├── __init__.py
│       ├── manager.py            # DatabaseManager + NOT_NULL
│       ├── supabase_manager.py   # SupabaseManager
│       ├── repository.py         # BaseRepository
│       ├── _json.py              # json_safe coercion
│       └── py.typed

Metadata

Release files for tjd-db-handler 0.1.2

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

Source distribution (sdist)

Source distribution for tjd-db-handler 0.1.2
File Size Uploaded
tjd_db_handler-0.1.2.tar.gz 9.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tjd-db-handler 0.1.2
File Interpreter ABI Platform
tjd_db_handler-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 19.8 kB

Release files / tjd_db_handler-0.1.2.tar.gz

Download URL tjd_db_handler-0.1.2.tar.gz
Size 9.9 kB
Tags Source
SHA-256 checksum
How to use checksums
128c03aa8d66e13266474e7f2d0387344a963c1937dfa3e54eaff6aba0b0a7c3
BLAKE2b-256 checksum
How to use checksums
125c7eef8a3e22173674d32fa9340a1d7fe5eddbf00ecd4fd7d2f8ee9336d074
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 19, 2026.

Transparency log

Release files / tjd_db_handler-0.1.2-py3-none-any.whl

Download URL tjd_db_handler-0.1.2-py3-none-any.whl
Size 10.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
85f101c73bd2c026fdd5465002757823c94243bdfc681df94f516b08394171b6
BLAKE2b-256 checksum
How to use checksums
ff33fbdd9ef1f53b7ff7e3957f76d8ba3d7d7540a5688c5b26e4bfa7deb54ba5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

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