옵션
집 Skill 개발자 도구 azure-cosmos-db-py

azure-cosmos-db-py

microsoft/skills microsoft/skills

Python/FastAPI를 사용하여 듀얼 인증을 통한 클라이언트 설정, 서비스 계층의 CRUD 작업, 파티션 키 전략, 매개변수화된 쿼리, TDD 패턴 등을 포함하여 프로덕션급 Azure Cosmos DB NoSQL 서비스를 구축합니다.

...모든 것을 확장하십시오
9
업데이트 된 시간 2026년 9월 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(관리형 ID, 워크로드 ID) 모두에서 작동합니다. 연결 문자열, 계정/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 모델 계층 구조

명확한 분리를 위해 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 참조

핵심 원칙

보안 요구 사항

  1. RBAC 인증: DefaultAzureCredential Azure에서 사용 — 절대로 코드에 키를 저장하지 마십시오
  2. 에뮬레이터 전용 키: 로컬 개발 시에만 잘 알려진 에뮬레이터 키를 하드코딩하십시오
  3. 매개변수화된 쿼리: 항상 @parameter 구문을 사용하십시오 — 문자열 연결은 절대 사용하지 마십시오
  4. 파티션 키 유효성 검사: 파티션 키 액세스가 사용자 권한과 일치하는지 유효성 검사하십시오

깔끔한 코드 규칙

  1. 단일 책임: 클라이언트 모듈은 연결을 처리하고, 서비스는 비즈니스 로직을 처리합니다
  2. 점진적 성능 저하: Cosmos가 사용할 수 없을 때 서비스는 None/[] Cosmos를 사용할 수 없을 때
  3. 일관된 명명 규칙: _doc_to_model(), _model_to_doc(), _use_cosmos()
  4. 타입 힌트: 모든 공개 메서드에 완전한 타입 지정
  5. CamelCase 별칭: JSON 직렬화를 위해 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 from 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 피처

품질 속성(NFR)

신뢰성

  • Cosmos를 사용할 수 없을 때의 점진적 성능 저하
  • 일시적인 오류에 대한 지수적 백오프를 적용한 재시도 로직
  • 싱글톤 패턴을 통한 연결 풀링

보안

  • 코드에 비밀 정보 없음 (DefaultAzureCredential을 통한 RBAC)
  • 매개변수화된 쿼리를 통한 인젝션 방지
  • 파티션 키 격리로 데이터 경계 강제 적용

유지 관리성

  • 5계층 모델 패턴을 통해 스키마 진화 가능
  • 서비스 계층은 비즈니스 로직을 스토리지로부터 분리합니다
  • 모든 엔티티 서비스 전반에 걸쳐 일관된 패턴

테스트 용이성

  • 다음과 같은 방식을 통한 의존성 주입 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년 8월 27일
receiving-code-review
업데이트 된 시간 2026년 9월 3일
tech-debt-tracker
업데이트 된 시간 2026년 8월 29일
deprecation-and-migration
업데이트 된 시간 2026년 9월 3일
OR