{"id":50639789,"url":"https://github.com/yeongseon/azure-functions-db-python","last_synced_at":"2026-06-07T07:04:43.386Z","repository":{"id":349888078,"uuid":"1204367127","full_name":"yeongseon/azure-functions-db-python","owner":"yeongseon","description":"Database trigger and input/output bindings for Azure Functions Python v2 — Part of the Azure Functions Python DX Toolkit","archived":false,"fork":false,"pushed_at":"2026-05-21T00:34:15.000Z","size":1479,"stargazers_count":4,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T05:48:18.336Z","etag":null,"topics":["azure","azure-functions","binding","database","dx-toolkit","python","serverless","sqlalchemy","trigger"],"latest_commit_sha":null,"homepage":"https://yeongseon.github.io/azure-functions-db-python/","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/yeongseon.png","metadata":{"files":{"readme":"README.ja.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-08T00:17:26.000Z","updated_at":"2026-05-18T11:11:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/yeongseon/azure-functions-db-python","commit_stats":null,"previous_names":["yeongseon/azure-functions-db","yeongseon/azure-functions-db-python"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/yeongseon/azure-functions-db-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeongseon%2Fazure-functions-db-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeongseon%2Fazure-functions-db-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeongseon%2Fazure-functions-db-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeongseon%2Fazure-functions-db-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yeongseon","download_url":"https://codeload.github.com/yeongseon/azure-functions-db-python/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeongseon%2Fazure-functions-db-python/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34011827,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["azure","azure-functions","binding","database","dx-toolkit","python","serverless","sqlalchemy","trigger"],"created_at":"2026-06-07T07:04:05.462Z","updated_at":"2026-06-07T07:04:43.381Z","avatar_url":"https://github.com/yeongseon.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Azure Functions DB\n\n[![PyPI](https://img.shields.io/pypi/v/azure-functions-db-python.svg)](https://pypi.org/project/azure-functions-db-python/)\n[![Python Version](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://pypi.org/project/azure-functions-db-python/)\n[![CI](https://github.com/yeongseon/azure-functions-db-python/actions/workflows/ci-test.yml/badge.svg)](https://github.com/yeongseon/azure-functions-db-python/actions/workflows/ci-test.yml)\n[![Release](https://github.com/yeongseon/azure-functions-db-python/actions/workflows/publish-pypi.yml/badge.svg)](https://github.com/yeongseon/azure-functions-db-python/actions/workflows/publish-pypi.yml)\n[![codecov](https://codecov.io/gh/yeongseon/azure-functions-db-python/branch/main/graph/badge.svg)](https://codecov.io/gh/yeongseon/azure-functions-db-python)\n[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://pre-commit.com/)\n[![Docs](https://img.shields.io/badge/docs-gh--pages-blue)](https://yeongseon.github.io/azure-functions-db-python/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nRead this in: [English](README.md) | [한국어](README.ko.md) | [简体中文](README.zh-CN.md)\n\n**Azure Functions Python v2** 向けのデータベース統合ライブラリです。SQLAlchemy を使ったポーリング型変更検知トリガーと入出力バインディングを提供します。\n\n---\n\n**Azure Functions Python DX Toolkit** の一部\n→ Azure Functions に FastAPI ライクな開発体験を提供\n\n## これが必要な理由\n\nAzure Functions Python v2 には、データベース統合の標準的な仕組みがありません。\n\n- **DB トリガーがない** — Cosmos DB とは異なり、RDB 向けのネイティブトリガーがない\n- **入出力バインディングがない** — 関数で DB 行を宣言的に読み書きする方法がない\n- **ドライバー選定が複雑** — DB ごとにドライバー、接続文字列、セットアップが異なる\n- **変更検知がない** — polling / CDC / outbox を毎回ゼロから実装する必要がある\n\n## 提供機能\n\n- **疑似 DB トリガー** — チェックポイント、リース、at-least-once 配信を備えたポーリング型変更検知\n- **マルチ DB 対応** — SQLAlchemy dialect により PostgreSQL / MySQL / SQL Server をサポート\n- **単一 `pip install`** — DB ごとの optional extras を持つ 1 パッケージ\n- **データ注入** — `input` はクエリ結果を直接注入し、`output` は戻り値の書き込みを自動化\n- **クライアント注入** — 必要に応じて `inject_reader`/`inject_writer` で命令的制御が可能\n\n## Shared Core\n\n`azure-functions-db-python` は今後のバインディングで共有する基盤を公開しています。正規化された接続設定には `DbConfig` を、複数コンポーネントで遅延生成された SQLAlchemy エンジンを共有する場合は `EngineProvider` を利用してください。\n\n## Installation\n\n```bash\n# Core package (pick your database)\npip install azure-functions-db-python[postgres]\npip install azure-functions-db-python[mysql]\npip install azure-functions-db-python[mssql]\n\n# Multiple databases\npip install azure-functions-db-python[postgres,mysql]\n\n# All drivers\npip install azure-functions-db-python[all]\n```\n\nFunction App の依存関係には次を含めてください。\n\n```text\nazure-functions\nazure-functions-db-python[postgres]\n```\n\n## Quick Start\n\n### どのデコレーターを使うべきか？\n\n| Need | Decorator | Mode |\n|------|-----------|------|\n| Read data into handler | `input` | Declarative (data injection) |\n| Write data to DB | `output` | Declarative (data injection) |\n| Complex reads (multiple queries) | `inject_reader` | Imperative (client injection) |\n| Complex writes (transactions) | `inject_writer` | Imperative (client injection) |\n| React to DB changes | `trigger` | Event-driven (pseudo-trigger) |\n\n### Input Binding (data injection)\n\n`input` は実際のクエリ結果をそのままハンドラーに注入します。クライアントは不要です。\n\n**Row lookup mode** — 主キーで 1 行取得:\n\n```python\nfrom azure_functions_db import DbBindings\n\ndb = DbBindings()\n\n# Static primary key\n@db.input(\"user\", url=\"%DB_URL%\", table=\"users\", pk={\"id\": 42})\ndef load_user(user: dict | None) -\u003e None:\n    if user:\n        print(user[\"name\"])\n\n# Dynamic primary key — resolved from handler kwargs\n@db.input(\"user\", url=\"%DB_URL%\", table=\"users\",\n             pk=lambda req: {\"id\": req.params[\"id\"]})\ndef get_user(req, user: dict | None) -\u003e None:\n    print(user)\n```\n\n**Query mode** — SQL で複数行取得:\n\n```python\n# Multiple rows by SQL query\n@db.input(\"users\", url=\"%DB_URL%\",\n             query=\"SELECT * FROM users WHERE active = :active\",\n             params={\"active\": True})\ndef list_active_users(users: list[dict]) -\u003e None:\n    for user in users:\n        print(user[\"email\"])\n```\n\n### Output Binding (data injection)\n\n`output` は `DbOut` インスタンスをハンドラーに注入します。明示的に書き込むには `.set()` を呼びます。\n\n```python\nfrom azure_functions_db import DbBindings, DbOut\n\ndb = DbBindings()\n\n# Insert — call .set() with a dict for single row, list[dict] for batch\n@db.output(\"out\", url=\"%DB_URL%\", table=\"orders\")\ndef create_order(out: DbOut) -\u003e str:\n    out.set({\"id\": 1, \"status\": \"pending\", \"total\": 99.99})\n    return \"Created\"\n\n# Upsert — set action and conflict_columns\n@db.output(\"out\", url=\"%DB_URL%\", table=\"orders\",\n              action=\"upsert\", conflict_columns=[\"id\"])\ndef upsert_orders(out: DbOut) -\u003e str:\n    out.set([\n        {\"id\": 1, \"status\": \"shipped\", \"total\": 99.99},\n        {\"id\": 2, \"status\": \"pending\", \"total\": 49.99},\n    ])\n    return \"Upserted\"\n```\n\nハンドラーの戻り値は書き込み処理とは独立しています。HTTP レスポンスなどに自由に使えます。\n\n```python\nimport azure.functions as func\nfrom azure_functions_db import DbBindings, DbOut\n\ndb = DbBindings()\n\n@db.output(\"out\", url=\"%DB_URL%\", table=\"orders\")\ndef create_order(req: func.HttpRequest, out: DbOut) -\u003e func.HttpResponse:\n    out.set({\"id\": 1, \"status\": \"pending\"})\n    return func.HttpResponse(\"Created\", status_code=201)\n```\n\n対応 upsert dialect: PostgreSQL, SQLite, MySQL。\n\n### Client Injection (imperative escape hatches)\n\n複雑な処理（複数クエリ、トランザクション、update/delete）では、`inject_reader`/`inject_writer` でクライアントを注入して使います。\n\n```python\nfrom azure_functions_db import DbBindings, DbReader, DbWriter\n\ndb = DbBindings()\n\n@db.inject_reader(\"reader\", url=\"%DB_URL%\", table=\"users\")\ndef complex_read(reader: DbReader) -\u003e None:\n    user = reader.get(pk={\"id\": 42})\n    orders = reader.query(\"SELECT * FROM orders WHERE user_id = :uid\", params={\"uid\": 42})\n\n@db.inject_writer(\"writer\", url=\"%DB_URL%\", table=\"orders\")\ndef complex_write(writer: DbWriter) -\u003e None:\n    writer.insert(data={\"id\": 1, \"status\": \"pending\"})\n    writer.update(data={\"status\": \"shipped\"}, pk={\"id\": 1})\n    writer.delete(pk={\"id\": 1})\n```\n\n### Trigger (change detection)\n\n```python\nimport azure.functions as func\nfrom azure.storage.blob import ContainerClient\nfrom azure_functions_db import BlobCheckpointStore, DbBindings, RowChange, SqlAlchemySource\n\napp = func.FunctionApp()\ndb = DbBindings()\n\nsource = SqlAlchemySource(\n    url=\"%ORDERS_DB_URL%\",\n    table=\"orders\",\n    schema=\"public\",\n    cursor_column=\"updated_at\",\n    pk_columns=[\"id\"],\n)\n\ncheckpoint_store = BlobCheckpointStore(\n    container_client=ContainerClient.from_connection_string(\n        conn_str=\"%AzureWebJobsStorage%\",\n        container_name=\"db-state\",\n    ),\n    source_fingerprint=source.source_descriptor.fingerprint,\n)\n\n@app.function_name(name=\"orders_poll\")\n@app.schedule(schedule=\"0 */1 * * * *\", arg_name=\"timer\", use_monitor=True)\n@db.trigger(arg_name=\"events\", source=source, checkpoint_store=checkpoint_store)\ndef orders_poll(timer: func.TimerRequest, events: list[RowChange]) -\u003e None:\n    for event in events:\n        print(f\"Order {event.pk}: {event.op}\")\n```\n\n\u003e これは **疑似トリガー** です。実際に動かすには Azure Functions の実トリガー（例: timer）が必要です。\n\n\u003e API 全体は [Python API Spec](docs/04-python-api-spec.md) を参照してください。\n\n### Combined: Trigger + Binding\n\nDB 変更を処理し、結果を別テーブルへ書き込みます。接続プール共有のため `EngineProvider` を利用します。\n\n```python\nimport azure.functions as func\nfrom azure.storage.blob import ContainerClient\n\nfrom azure_functions_db import (\n    BlobCheckpointStore,\n    DbBindings,\n    DbOut,\n    EngineProvider,\n    RowChange,\n    SqlAlchemySource,\n)\n\napp = func.FunctionApp()\ndb = DbBindings()\n\nengine_provider = EngineProvider()\n\nsource = SqlAlchemySource(\n    url=\"%SOURCE_DB_URL%\",\n    table=\"orders\",\n    cursor_column=\"updated_at\",\n    pk_columns=[\"id\"],\n    engine_provider=engine_provider,\n)\n\ncheckpoint_store = BlobCheckpointStore(\n    container_client=ContainerClient.from_connection_string(\n        conn_str=\"%AzureWebJobsStorage%\",\n        container_name=\"db-state\",\n    ),\n    source_fingerprint=source.source_descriptor.fingerprint,\n)\n\n@app.function_name(name=\"orders_poll\")\n@app.schedule(schedule=\"0 */1 * * * *\", arg_name=\"timer\", use_monitor=True)\n@db.trigger(arg_name=\"events\", source=source, checkpoint_store=checkpoint_store)\n@db.output(\n    \"out\",\n    url=\"%DEST_DB_URL%\",\n    table=\"processed_orders\",\n    action=\"upsert\",\n    conflict_columns=[\"order_id\"],\n    engine_provider=engine_provider,\n)\ndef orders_poll(timer: func.TimerRequest, events: list[RowChange], out: DbOut) -\u003e None:\n    out.set([\n        {\n            \"order_id\": event.pk[\"id\"],\n            \"customer\": event.after[\"name\"],\n            \"processed_at\": str(event.cursor),\n        }\n        for event in events\n        if event.after is not None\n    ])\n```\n\n完全に実行可能なサンプルは [`examples/trigger_with_binding/`](examples/trigger_with_binding/) を参照してください。\n\n## Supported Databases\n\n| Database | Extra | Driver |\n|----------|-------|--------|\n| PostgreSQL | `azure-functions-db-python[postgres]` | [psycopg](https://www.psycopg.org/) |\n| MySQL | `azure-functions-db-python[mysql]` | [PyMySQL](https://pymysql.readthedocs.io/) |\n| SQL Server | `azure-functions-db-python[mssql]` | [pyodbc](https://github.com/mkleehammer/pyodbc) |\n\n## Scope\n\n- Azure Functions Python **v2 programming model**\n- Timer-triggered functions for poll-based change detection\n- SQLAlchemy 2.0+ for database abstraction\n- Checkpoint storage via Azure Blob Storage\n- Read/write bindings via HTTP/Queue/Event triggers\n\nこのパッケージはネイティブ Azure Functions トリガー拡張を実装しません。既存の timer trigger 上で動くポーリング方式を採用しています。\n\n## Observability\n\n`azure-functions-db-python` は構造化ログヘルパーと軽量な `MetricsCollector` プロトコルを提供し、重い依存追加なしで任意のメトリクス基盤と接続できます。\n\n```python\nfrom collections.abc import Mapping\n\nfrom azure_functions_db import MetricsCollector, PollTrigger\n\n\nclass PrintMetricsCollector:\n    def increment(\n        self, name: str, value: float = 1, *, labels: Mapping[str, str] | None = None\n    ) -\u003e None:\n        print(\"increment\", name, value, labels)\n\n    def observe(\n        self, name: str, value: float, *, labels: Mapping[str, str] | None = None\n    ) -\u003e None:\n        print(\"observe\", name, value, labels)\n\n    def set_gauge(\n        self, name: str, value: float, *, labels: Mapping[str, str] | None = None\n    ) -\u003e None:\n        print(\"gauge\", name, value, labels)\n\n\ntrigger = PollTrigger(\n    name=\"orders\",\n    source=source,\n    checkpoint_store=checkpoint_store,\n    metrics=PrintMetricsCollector(),\n)\n```\n\n## Key Design Decisions\n\n- **Pseudo trigger** — native C# extension ではなく timer ベース polling ([ADR-001](docs/16-ADR-001-pseudo-trigger-over-native.md))\n- **SQLAlchemy-centric** — 全 DB 向け単一 ORM レイヤー ([ADR-002](docs/17-ADR-002-sqlalchemy-centric-adapter.md))\n- **Blob checkpoint** — チェックポイント永続化に Azure Blob Storage ([ADR-003](docs/18-ADR-003-blob-checkpoint-mvp.md))\n- **At-least-once** — 冪等性を前提にした既定配信保証 ([ADR-004](docs/19-ADR-004-at-least-once-default.md))\n- **Unified package** — trigger + binding を 1 パッケージで提供 ([ADR-005](docs/23-ADR-005-unified-package-design.md))\n\n## Duplicate Handling\n\nこのパッケージは **at-least-once** 配信を提供します。プロセスクラッシュ、リース遷移、コミット失敗時に重複が発生する可能性があります。ハンドラーは冪等に実装してください。詳細は [Semantics — Duplicate Windows](docs/03-semantics.md#13-duplicate-and-reprocessing-windows) を参照してください。\n\n## Documentation\n\n- Full docs: [yeongseon.github.io/azure-functions-db-python](https://yeongseon.github.io/azure-functions-db-python/)\n- Examples: `examples/`\n- [Architecture](docs/02-architecture.md)\n- [Semantics](docs/03-semantics.md)\n- [Python API Spec](docs/04-python-api-spec.md)\n- [Adapter SDK](docs/05-adapter-sdk.md)\n\n## Ecosystem\n\n**Azure Functions Python DX Toolkit** の一部:\n\n| Package | Role |\n|---------|------|\n| [azure-functions-openapi-python](https://github.com/yeongseon/azure-functions-openapi-python) | OpenAPI spec generation and Swagger UI |\n| [azure-functions-validation-python](https://github.com/yeongseon/azure-functions-validation-python) | Request/response validation and serialization |\n| **azure-functions-db-python** | Database bindings for SQL, PostgreSQL, MySQL, SQLite, and Cosmos DB |\n| [azure-functions-langgraph-python](https://github.com/yeongseon/azure-functions-langgraph-python) | LangGraph deployment adapter for Azure Functions |\n| [azure-functions-scaffold-python](https://github.com/yeongseon/azure-functions-scaffold-python) | Project scaffolding CLI |\n| [azure-functions-logging-python](https://github.com/yeongseon/azure-functions-logging-python) | Structured logging and observability |\n| [azure-functions-doctor-python](https://github.com/yeongseon/azure-functions-doctor-python) | Pre-deploy diagnostic CLI |\n| [azure-functions-durable-graph-python](https://github.com/yeongseon/azure-functions-durable-graph-python) | Manifest-first graph runtime with Durable Functions *(experimental)* |\n| [azure-functions-cookbook-python](https://github.com/yeongseon/azure-functions-cookbook-python) | Recipes and examples |\n\n## Disclaimer\n\nこのプロジェクトは独立したコミュニティプロジェクトであり、Microsoft とは提携・承認・保守関係にありません。\n\nAzure および Azure Functions は Microsoft Corporation の商標です。\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyeongseon%2Fazure-functions-db-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyeongseon%2Fazure-functions-db-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyeongseon%2Fazure-functions-db-python/lists"}