{"id":42135181,"url":"https://github.com/depeche-py/depeche-db","last_synced_at":"2026-01-26T16:11:40.761Z","repository":{"id":197078665,"uuid":"697652179","full_name":"depeche-py/depeche-db","owner":"depeche-py","description":null,"archived":false,"fork":false,"pushed_at":"2026-01-13T10:42:15.000Z","size":2050,"stargazers_count":6,"open_issues_count":3,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-13T13:39:51.698Z","etag":null,"topics":["event-driven","event-sourcing","messages","postgresql","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/depeche-py.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-09-28T07:40:18.000Z","updated_at":"2026-01-13T10:41:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"c21cd1e8-f3e6-48aa-aa9d-cfaf24c99087","html_url":"https://github.com/depeche-py/depeche-db","commit_stats":null,"previous_names":["depeche-py/depeche-db"],"tags_count":41,"template":false,"template_full_name":null,"purl":"pkg:github/depeche-py/depeche-db","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/depeche-py%2Fdepeche-db","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/depeche-py%2Fdepeche-db/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/depeche-py%2Fdepeche-db/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/depeche-py%2Fdepeche-db/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/depeche-py","download_url":"https://codeload.github.com/depeche-py/depeche-db/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/depeche-py%2Fdepeche-db/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28782164,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T13:55:28.044Z","status":"ssl_error","status_checked_at":"2026-01-26T13:55:26.068Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["event-driven","event-sourcing","messages","postgresql","python"],"created_at":"2026-01-26T16:11:40.159Z","updated_at":"2026-01-26T16:11:40.754Z","avatar_url":"https://github.com/depeche-py.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://depeche-py.github.io/depeche-db/assets/logo-bg.png\" width=\"200\" /\u003e\n\u003c/p\u003e\n\n# Depeche DB\n\nA library for building event-based systems on top of PostgreSQL\n\n[![Tests](https://github.com/depeche-py/depeche-db/actions/workflows/tests.yml/badge.svg)](https://github.com/depeche-py/depeche-db/actions/workflows/tests.yml)\n[![pypi](https://img.shields.io/pypi/v/depeche-db.svg)](https://pypi.python.org/pypi/depeche-db)\n[![versions](https://img.shields.io/pypi/pyversions/depeche-db.svg)](https://github.com/depeche-py/depeche-db)\n[![Docs](https://img.shields.io/badge/docs-here-green.svg)](https://depeche-py.github.io/depeche-db/)\n[![license](https://img.shields.io/github/license/depeche-py/depeche-db.svg)](https://github.com/depeche-py/depeche-db/blob/main/LICENSE)\n\n---\n\n**Documentation**: [https://depeche-py.github.io/depeche-db/](https://depeche-py.github.io/depeche-db/)\n\n**Source code**: [https://github.com/depeche-py/depeche-db](https://github.com/depeche-py/depeche-db)\n\n---\n\nDepeche DB is modern Python library for building event-based systems\n\nKey features:\n\n* Message store with optimistic concurrency control \u0026 strong ordering guarantees\n* Subscriptions with \"at least once\" semantics\n* Parallel processing of (partitioned) subscriptions\n* No database polling\n\n## Requirements\n\nPython 3.9+\nSQLAlchemy 1.4 or 2+\nPostgreSQL 12+\nPsycopg (Version 2 \u003e= 2.9.3 or Version 3 \u003e= 3.1)\n\n\n## Installation\n\n```bash\npip install depeche-db\n# OR\npoetry add depeche-db\n# OR\nuv add depeche-db\n```\n\n## Example\n\n```python\nimport pydantic, sqlalchemy, uuid, datetime as dt\n\nfrom depeche_db import (\n    MessageStore,\n    StoredMessage,\n    MessageHandler,\n    SubscriptionMessage,\n)\nfrom depeche_db.tools import PydanticMessageSerializer\n\nDB_DSN = \"postgresql://depeche:depeche@localhost:4888/depeche_demo\"\ndb_engine = sqlalchemy.create_engine(DB_DSN)\n\n\nclass MyMessage(pydantic.BaseModel):\n    content: int\n    message_id: uuid.UUID = pydantic.Field(default_factory=uuid.uuid4)\n    sent_at: dt.datetime = pydantic.Field(default_factory=dt.datetime.utcnow)\n\n    def get_message_id(self) -\u003e uuid.UUID:\n        return self.message_id\n\n    def get_message_time(self) -\u003e dt.datetime:\n        return self.sent_at\n\n\nmessage_store = MessageStore[MyMessage](\n    name=\"example_store\",\n    engine=db_engine,\n    serializer=PydanticMessageSerializer(MyMessage),\n)\nmessage_store.write(stream=\"aggregate-me-1\", message=MyMessage(content=2))\nprint(list(message_store.read(stream=\"aggregate-me-1\")))\n# [StoredMessage(message_id=UUID('...'), stream='aggregate-me-1', version=1, message=MyMessage(content=2, message_id=UUID('...'), sent_at=datetime.datetime(...)), global_position=1)]\n\n\nclass ContentMessagePartitioner:\n    def get_partition(self, message: StoredMessage[MyMessage]) -\u003e int:\n        return message.message.content % 10\n\n\nclass MyHandlers(MessageHandler[MyMessage]):\n    @MessageHandler.register\n    def handle_message(self, message: SubscriptionMessage[MyMessage]):\n        print(message)\n\n\naggregated_stream = message_store.aggregated_stream(\n    name=\"aggregated\",\n    partitioner=ContentMessagePartitioner(),\n    stream_wildcards=[\"aggregate-me-%\"],\n)\nsubscription = aggregated_stream.subscription(\n    name=\"example_subscription\",\n    handlers=MyHandlers(),\n)\n\naggregated_stream.projector.run()\nsubscription.runner.run()\n# MyHandlers.handle_message prints:\n# SubscriptionMessage(partition=2, position=0, stored_message=StoredMessage(...))\n\n```\n\n\n## Contribute\n\nContributions in the form of issues, questions, feedback and pull requests are\nwelcome. Before investing a lot of time, let me know what you are up to so\nwe can see if your contribution fits the vision of the project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdepeche-py%2Fdepeche-db","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdepeche-py%2Fdepeche-db","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdepeche-py%2Fdepeche-db/lists"}