選項
首頁首頁 Skill 開發者工具 azure-cosmos-db-py

azure-cosmos-db-py

microsoft/skills microsoft/skills

使用 Python/FastAPI 建置生產級的 Azure Cosmos DB NoSQL 服務,內容涵蓋具備雙重驗證的客戶端設定、服務層的 CRUD 操作、分區金鑰策略、參數化查詢,以及 TDD 模式。

...展開全部
9
更新時間 2026-09-12

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

身份驗證與生命週期

🔑 以下每個程式碼範例均適用兩項規則:

  1. 優先使用 DefaultAzureCredential它可在本地端(Azure CLI / VS Code / Developer CLI)及 Azure 環境中(託管身分識別、工作負載身分識別)運作,且無需修改程式碼。請避免使用連線字串、帳戶/API 金鑰——這些會繞過 Entra 的稽核與輪替機制。
    • 本地開發: DefaultAzureCredential 可直接使用。
    • 生產環境:設定 AZURE_TOKEN_CREDENTIALS=prod (或 AZURE_TOKEN_CREDENTIALS=) 將憑證鏈限制為生產環境安全的憑證。
  2. 將每個客戶端封裝在上下文管理器中,以確保 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 模型層級結構

採用五層模型模式以實現清晰的分離:

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

核心原則

安全性需求

  1. RBAC 驗證:在 DefaultAzureCredential Azure 進行驗證 — 切勿將金鑰儲存於程式碼中
  2. 僅限模擬器的金鑰:僅在本地開發時將已知的模擬器金鑰硬編碼
  3. 參數化查詢:請始終使用 @parameter 語法 — 切勿使用字串拼接
  4. 分區鍵驗證:驗證分區鍵存取權限是否符合使用者授權

乾淨程式碼規範

  1. 單一職責原則:客戶端模組負責連線處理;服務端負責業務邏輯
  2. 優雅降級:當 Cosmos 無法使用時,服務應返回 None/[] 當 Cosmos 無法使用時
  3. 命名一致性_doc_to_model(), _model_to_doc(), _use_cosmos()
  4. 類型提示:所有公開方法均應完整標示類型
  5. 駱駝式別名:使用 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

最佳實務

  1. 此技能全程採用非同步模式(azure.cosmos.aio);請勿與同步的 azure.cosmos 客戶端混合使用。請確保整個 FastAPI 請求路徑皆為非同步——切勿將同步的 Cosmos 呼叫與非同步處理程序搭配使用。
  2. 請始終為客戶端和非同步憑證使用上下文管理器。將客戶端封裝在 async with CosmosClient(...) as client: (或透過 FastAPI 生命週期管理其存活時間,並明確關閉它)。對於非同步 DefaultAzureCredential 來自 azure.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 固定裝置

品質屬性 (NFRs)

可靠性

  • 當 Cosmos 無法使用時能平穩降級
  • 針對暫時性故障採用指數退避的重試邏輯
  • 透過單例模式實現連線池化

安全性

  • 程式碼中不包含任何機密資訊(透過 DefaultAzureCredential 實現 RBAC)
  • 參數化查詢防止注入攻擊
  • 透過分區鍵隔離強制執行資料邊界

可維護性

  • 五層模型模式支援資料結構演進
  • 服務層將業務邏輯與儲存解耦
  • 所有實體服務皆採用一致的模式

可測試性

  • 透過 get_container()
  • 透過模組層級的全域變數輕鬆進行模擬
  • 明確的分離使我們能在不使用 Cosmos 的情況下進行單元測試

效能

  • 分區鍵查詢可避免跨分區掃描
  • 非同步封裝可防止阻塞 FastAPI 事件迴圈
  • 文檔轉換開銷極低
在 GitHub 上查看
---
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

複製 複製
快速設定: 將技能資料夾複製到 .claude/skills/ Claude 會自動偵測並使用該技能
儲存庫 microsoft/skills

相關技能

algorithmic-art
更新時間 2026-08-27
tech-debt-tracker
更新時間 2026-08-29
receiving-code-review
更新時間 2026-09-03
deprecation-and-migration
更新時間 2026-09-03
OR