azure-cosmos-db-py
microsoft/skills
Python/FastAPI を使用して、本番環境向けの Azure Cosmos DB NoSQL サービスを構築します。これには、デュアル認証によるクライアントの設定、サービス層での CRUD 操作、パーティションキーの戦略、パラメータ化されたクエリ、および TDD パターンが含まれます。
...すべて拡張しますCosmos DB サービスの実装
クリーンコード、セキュリティのベストプラクティス、およびTDDの原則に従って、本番環境向けのAzure Cosmos DB NoSQLサービスを構築します。
インストール
pip install azure-cosmos azure-identity
環境変数
COSMOS_ENDPOINT=https://.documents.azure.com:443/ # Required for all auth methods
COSMOS_DATABASE_NAME= # Required for all auth methods
COSMOS_CONTAINER_ID= # Required for all auth methods
# For emulator only (not production)
COSMOS_KEY= # Only required for key-based auth or emulator
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
認証とライフサイクル
🔑 以下のすべてのコードサンプルには、2つのルールが適用されます:
DefaultAzureCredentialを優先してください。コードを変更することなく、ローカル(Azure CLI / VS Code / Developer CLI)およびAzure(マネージド ID、ワークロード ID)の両方で動作します。接続文字列やアカウント/API キーの使用は避けてください。これらはEntraの監査およびローテーションの対象外となります。
- ローカル開発:
DefaultAzureCredentialそのまま動作します。- 本番環境:
AZURE_TOKEN_CREDENTIALS=prod(またはAZURE_TOKEN_CREDENTIALS=)を設定して、認証情報チェーンを本番環境に適した認証情報に制限してください。- すべてのクライアントをコンテキストマネージャーでラップし、HTTPトランスポート、ソケット、トークンキャッシュが確定的に解放されるようにします:
- 同期:
with(...) as client: - 非同期:
async withおよび(...) as client: async with DefaultAzureCredential() as credential:(出典:azure.identity.aio)スニペットではこの設定を簡略化している場合がありますが、本番環境のコードでは常に両方のルールに従う必要があります。
DefaultAzureCredential(推奨):
import os
from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
# Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=
credential = DefaultAzureCredential(require_envvar=True)
# Or use a specific credential directly in production:
# See https://learn.microsoft.com/python/api/overview/azure/identity-readme?view=azure-python#credential-classes
# credential = ManagedIdentityCredential()
with CosmosClient(
url=os.environ["COSMOS_ENDPOINT"],
credential=credential
) as client:
# Use client here (see following sections for operations)
...
エミュレーター(ローカル開発):
from azure.cosmos import CosmosClient
with CosmosClient(
url="https://localhost:8081",
credential=os.environ["COSMOS_KEY"],
connection_verify=False
) as client:
# Use client here (see following sections for operations)
...
アーキテクチャの概要
┌─────────────────────────────────────────────────────────────────┐
│ FastAPI Router │
│ - Auth dependencies (get_current_user, get_current_user_required)
│ - HTTP error responses (HTTPException) │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Service Layer │
│ - Business logic and validation │
│ - Document ↔ Model conversion │
│ - Graceful degradation when Cosmos unavailable │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Cosmos DB Client Module │
│ - Singleton container initialization │
│ - Dual auth: DefaultAzureCredential (Azure) / Key (emulator) │
│ - Async wrapper via run_in_threadpool │
└─────────────────────────────────────────────────────────────────┘
クイックスタート
1. クライアントモジュールの設定
二重認証を備えたシングルトン Cosmos クライアントを作成します:
# db/cosmos.py
from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential
from starlette.concurrency import run_in_threadpool
_cosmos_container = None
def _is_emulator_endpoint(endpoint: str) -> bool:
return "localhost" in endpoint or "127.0.0.1" in endpoint
async def get_container():
global _cosmos_container
if _cosmos_container is None:
# Singleton: client lives for the FastAPI app lifetime; close in a lifespan shutdown handler.
if _is_emulator_endpoint(settings.cosmos_endpoint):
client = CosmosClient(
url=settings.cosmos_endpoint,
credential=settings.cosmos_key,
connection_verify=False
)
else:
client = CosmosClient(
url=settings.cosmos_endpoint,
credential=DefaultAzureCredential()
)
db = client.get_database_client(settings.cosmos_database_name)
_cosmos_container = db.get_container_client(settings.cosmos_container_id)
return _cosmos_container
完全な実装:references/client-setup.md を参照
2. Pydantic モデル階層
明確な分離のために5層モデルパターンを使用します:
class ProjectBase(BaseModel): # Shared fields
name: str = Field(..., min_length=1, max_length=200)
class ProjectCreate(ProjectBase): # Creation request
workspace_id: str = Field(..., alias="workspaceId")
class ProjectUpdate(BaseModel): # Partial updates (all optional)
name: Optional[str] = Field(None, min_length=1)
class Project(ProjectBase): # API response
id: str
created_at: datetime = Field(..., alias="createdAt")
class ProjectInDB(Project): # Internal with docType
doc_type: str = "project"
3. サービス層のパターン
class ProjectService:
def _use_cosmos(self) -> bool:
return get_container() is not None
async def get_by_id(self, project_id: str, workspace_id: str) -> Project | None:
if not self._use_cosmos():
return None
doc = await get_document(project_id, partition_key=workspace_id)
if doc is None:
return None
return self._doc_to_model(doc)
パターンの詳細: references/service-layer.md を参照
基本原則
セキュリティ要件
- RBAC 認証:Azure を使用する
DefaultAzureCredentialAzure を使用 — コード内にキーを保存してはならない - エミュレータ専用キー:ローカル開発の場合に限り、既知のエミュレータキーをハードコードする
- パラメータ化されたクエリ:常に
@parameter構文を使用すること — 文字列の連結は絶対に避ける - パーティションキーの検証:パーティションキーへのアクセスがユーザーの権限と一致していることを検証する
クリーンコードの規約
- 単一責任の原則:クライアントモジュールは接続を処理し、サービスはビジネスロジックを処理する
- グレースフル・ディグレーション:Cosmosが利用できない場合は、サービスは
None/[]を返す - 一貫性のある命名:
_doc_to_model(),_model_to_doc(),_use_cosmos() - 型ヒント:すべてのパブリックメソッドに完全な型指定を行う
- キャメルケースのエイリアス:
Field(alias="camelCase")JSONシリアライゼーションに
TDDの要件
実装の前に、以下のパターンに従ってテストを作成してください:
@pytest.fixture
def mock_cosmos_container(mocker):
container = mocker.MagicMock()
mocker.patch("app.db.cosmos.get_container", return_value=container)
return container
@pytest.mark.asyncio
async def test_get_project_by_id_returns_project(mock_cosmos_container):
# Arrange
mock_cosmos_container.read_item.return_value = {"id": "123", "name": "Test"}
# Act
result = await project_service.get_by_id("123", "workspace-1")
# Assert
assert result.id == "123"
assert result.name == "Test"
テストの完全なガイド:references/testing.mdを参照
ベストプラクティス
- このスキルでは、全体を通して非同期処理(
azure.cosmos.aio)を使用しています。同期型のazure.cosmosクライアントと混在させないでください。FastAPIのリクエストパス全体を非同期に保ち、同期型のCosmos呼び出しと非同期ハンドラーを組み合わせないでください。 - クライアントおよび非同期認証情報には、常にコンテキストマネージャーを使用してください。クライアントを
async with CosmosClient(...) as client:でラップしてください(または、FastAPIのライフサイクル管理機能を使用してそのライフサイクルを管理し、明示的にクローズしてください)。非同期のDefaultAzureCredentialfromazure.identity.aioについては、async with credential:を使用し、トークンとトランスポートがクリーンアップされるようにしてください。
参照ファイル
| ファイル | 読むべきタイミング |
|---|---|
| references/client-setup.md | デュアル認証、SSL設定、シングルトンパターンを用いたCosmosクライアントの設定 |
| references/service-layer.md | CRUD、変換、グレースフル・デグラデーションを備えたフルサービスクラスの実装 |
| references/testing.md | pytestによるテストの作成、Cosmosのモック化、統合テストの設定 |
| references/partitioning.md | パーティションキーの選択、パーティションをまたぐクエリ、移動操作 |
| references/error-handling.md | CosmosResourceNotFoundErrorの処理、ロギング、HTTPエラーのマッピング |
テンプレートファイル
| ファイル | 目的 |
|---|---|
| assets/cosmos_client_template.py | すぐに使えるクライアントモジュール |
| assets/service_template.py | サービスクラスの骨格 |
| assets/conftest_template.py | Cosmosのモック化用pytestフィクスチャ |
品質属性(NFR)
信頼性
- Cosmos が利用できない場合のグレースフル・デグラデーション
- 一時的な障害に対する指数関数的バックオフを伴うリトライロジック
- シングルトンパターンを用いた接続プーリング
セキュリティ
- コード内のシークレットをゼロにする(DefaultAzureCredential による RBAC)
- パラメータ化されたクエリによるインジェクション防止
- パーティションキーによる分離でデータ境界を強制
保守性
- 5層モデルパターンによりスキーマの進化が可能
- サービス層により、ビジネスロジックとストレージが分離される
- すべてのエンティティサービスにわたる一貫したパターン
テスト可能性
- 以下の方法による依存性注入
get_container() - モジュールレベルのグローバル変数による簡単なモック化
- 明確な分離により、Cosmos を使用せずに単体テストが可能
パフォーマンス
- パーティションキーによるクエリにより、パーティションをまたぐスキャンを回避
- 非同期ラッピングにより、FastAPIのイベントループがブロックされるのを防止
- ドキュメント変換のオーバーヘッドを最小限に抑える
---
name: azure-cosmos-db-py
description: Build production-grade Azure Cosmos DB NoSQL services with Python/FastAPI, including client setup with dual authentication, service layer CRUD operations, partition key strategies, parameterized queries, and TDD patterns.
license: MIT
---
# Cosmos DB Service Implementation
Build production-grade Azure Cosmos DB NoSQL services following clean code, security best practices, and TDD principles.
## Installation
```bash
pip install azure-cosmos azure-identity
```
## Environment Variables
```bash
COSMOS_ENDPOINT=https://<account>.documents.azure.com:443/ # Required for all auth methods
COSMOS_DATABASE_NAME=<database-name> # Required for all auth methods
COSMOS_CONTAINER_ID=<container-id> # Required for all auth methods
# For emulator only (not production)
COSMOS_KEY=<emulator-key> # Only required for key-based auth or emulator
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
```
## Authentication & Lifecycle
> **🔑 Two rules apply to every code sample below:**
>
> 1. **Prefer `DefaultAzureCredential`.** It works locally (Azure CLI / VS Code / Developer CLI) and in Azure (managed identity, workload identity) with no code change. Avoid connection strings, account/API keys — they bypass Entra audit and rotation.
> - Local dev: `DefaultAzureCredential` works as-is.
> - Production: set `AZURE_TOKEN_CREDENTIALS=prod` (or `AZURE_TOKEN_CREDENTIALS=<specific_credential>`) to constrain the credential chain to production-safe credentials.
> 2. **Wrap every client in a context manager** so HTTP transports, sockets, and token caches are released deterministically:
> - Sync: `with <Client>(...) as client:`
> - Async: `async with <Client>(...) as client:` **and** `async with DefaultAzureCredential() as credential:` (from `azure.identity.aio`)
>
> Snippets may abbreviate this setup, but production code should always follow both rules.
**DefaultAzureCredential (preferred)**:
```python
import os
from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
# Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
credential = DefaultAzureCredential(require_envvar=True)
# Or use a specific credential directly in production:
# See https://learn.microsoft.com/python/api/overview/azure/identity-readme?view=azure-python#credential-classes
# credential = ManagedIdentityCredential()
with CosmosClient(
url=os.environ["COSMOS_ENDPOINT"],
credential=credential
) as client:
# Use client here (see following sections for operations)
...
```
**Emulator (local development)**:
```python
from azure.cosmos import CosmosClient
with CosmosClient(
url="https://localhost:8081",
credential=os.environ["COSMOS_KEY"],
connection_verify=False
) as client:
# Use client here (see following sections for operations)
...
```
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ FastAPI Router │
│ - Auth dependencies (get_current_user, get_current_user_required)
│ - HTTP error responses (HTTPException) │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Service Layer │
│ - Business logic and validation │
│ - Document ↔ Model conversion │
│ - Graceful degradation when Cosmos unavailable │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────────┐
│ Cosmos DB Client Module │
│ - Singleton container initialization │
│ - Dual auth: DefaultAzureCredential (Azure) / Key (emulator) │
│ - Async wrapper via run_in_threadpool │
└─────────────────────────────────────────────────────────────────┘
```
## Quick Start
### 1. Client Module Setup
Create a singleton Cosmos client with dual authentication:
```python
# db/cosmos.py
from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential
from starlette.concurrency import run_in_threadpool
_cosmos_container = None
def _is_emulator_endpoint(endpoint: str) -> bool:
return "localhost" in endpoint or "127.0.0.1" in endpoint
async def get_container():
global _cosmos_container
if _cosmos_container is None:
# Singleton: client lives for the FastAPI app lifetime; close in a lifespan shutdown handler.
if _is_emulator_endpoint(settings.cosmos_endpoint):
client = CosmosClient(
url=settings.cosmos_endpoint,
credential=settings.cosmos_key,
connection_verify=False
)
else:
client = CosmosClient(
url=settings.cosmos_endpoint,
credential=DefaultAzureCredential()
)
db = client.get_database_client(settings.cosmos_database_name)
_cosmos_container = db.get_container_client(settings.cosmos_container_id)
return _cosmos_container
```
**Full implementation**: See [references/client-setup.md](references/client-setup.md)
### 2. Pydantic Model Hierarchy
Use five-tier model pattern for clean separation:
```python
class ProjectBase(BaseModel): # Shared fields
name: str = Field(..., min_length=1, max_length=200)
class ProjectCreate(ProjectBase): # Creation request
workspace_id: str = Field(..., alias="workspaceId")
class ProjectUpdate(BaseModel): # Partial updates (all optional)
name: Optional[str] = Field(None, min_length=1)
class Project(ProjectBase): # API response
id: str
created_at: datetime = Field(..., alias="createdAt")
class ProjectInDB(Project): # Internal with docType
doc_type: str = "project"
```
### 3. Service Layer Pattern
```python
class ProjectService:
def _use_cosmos(self) -> bool:
return get_container() is not None
async def get_by_id(self, project_id: str, workspace_id: str) -> Project | None:
if not self._use_cosmos():
return None
doc = await get_document(project_id, partition_key=workspace_id)
if doc is None:
return None
return self._doc_to_model(doc)
```
**Full patterns**: See [references/service-layer.md](references/service-layer.md)
## Core Principles
### Security Requirements
1. **RBAC Authentication**: Use `DefaultAzureCredential` in Azure — never store keys in code
2. **Emulator-Only Keys**: Hardcode the well-known emulator key only for local development
3. **Parameterized Queries**: Always use `@parameter` syntax — never string concatenation
4. **Partition Key Validation**: Validate partition key access matches user authorization
### Clean Code Conventions
1. **Single Responsibility**: Client module handles connection; services handle business logic
2. **Graceful Degradation**: Services return `None`/`[]` when Cosmos unavailable
3. **Consistent Naming**: `_doc_to_model()`, `_model_to_doc()`, `_use_cosmos()`
4. **Type Hints**: Full typing on all public methods
5. **CamelCase Aliases**: Use `Field(alias="camelCase")` for JSON serialization
### TDD Requirements
Write tests BEFORE implementation using these patterns:
```python
@pytest.fixture
def mock_cosmos_container(mocker):
container = mocker.MagicMock()
mocker.patch("app.db.cosmos.get_container", return_value=container)
return container
@pytest.mark.asyncio
async def test_get_project_by_id_returns_project(mock_cosmos_container):
# Arrange
mock_cosmos_container.read_item.return_value = {"id": "123", "name": "Test"}
# Act
result = await project_service.get_by_id("123", "workspace-1")
# Assert
assert result.id == "123"
assert result.name == "Test"
```
**Full testing guide**: See [references/testing.md](references/testing.md)
## Best Practices
1. **This skill uses async throughout (`azure.cosmos.aio`); do not mix with the sync `azure.cosmos` client.** Keep the whole FastAPI request path async — don't pair sync Cosmos calls with async handlers.
2. **Always use context managers for clients and async credentials.** Wrap the client in `async with CosmosClient(...) as client:` (or manage its lifetime via FastAPI lifespan and close it explicitly). For async `DefaultAzureCredential` from `azure.identity.aio`, also use `async with credential:` so tokens and transports are cleaned up.
## Reference Files
| File | When to Read |
|------|--------------|
| [references/client-setup.md](references/client-setup.md) | Setting up Cosmos client with dual auth, SSL config, singleton pattern |
| [references/service-layer.md](references/service-layer.md) | Implementing full service class with CRUD, conversions, graceful degradation |
| [references/testing.md](references/testing.md) | Writing pytest tests, mocking Cosmos, integration test setup |
| [references/partitioning.md](references/partitioning.md) | Choosing partition keys, cross-partition queries, move operations |
| [references/error-handling.md](references/error-handling.md) | Handling CosmosResourceNotFoundError, logging, HTTP error mapping |
## Template Files
| File | Purpose |
|------|---------|
| [assets/cosmos_client_template.py](assets/cosmos_client_template.py) | Ready-to-use client module |
| [assets/service_template.py](assets/service_template.py) | Service class skeleton |
| [assets/conftest_template.py](assets/conftest_template.py) | pytest fixtures for Cosmos mocking |
## Quality Attributes (NFRs)
### Reliability
- Graceful degradation when Cosmos unavailable
- Retry logic with exponential backoff for transient failures
- Connection pooling via singleton pattern
### Security
- Zero secrets in code (RBAC via DefaultAzureCredential)
- Parameterized queries prevent injection
- Partition key isolation enforces data boundaries
### Maintainability
- Five-tier model pattern enables schema evolution
- Service layer decouples business logic from storage
- Consistent patterns across all entity services
### Testability
- Dependency injection via `get_container()`
- Easy mocking with module-level globals
- Clear separation enables unit testing without Cosmos
### Performance
- Partition key queries avoid cross-partition scans
- Async wrapping prevents blocking FastAPI event loop
- Minimal document conversion overhead
すべてのファイル
0件のファイルazure-cosmos-db-pyをインストール
スキルファイルをダウンロードし、.claude/skills/ ディレクトリに解凍してください。
ZIPをダウンロードリポジトリをクローンし、スキルファイルをプロジェクトにコピーしてください。
git clone https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-python/skills/azure-cosmos-db-py # Copy SKILL.md to your .claude/skills/ directory
コピー





家
