2.1.1. Connecting and Querying with the SDK
💡 First Principle: The SDK mirrors Cosmos DB's resource hierarchy — client → database → container → item — and the cost of any operation depends on how precisely you can point at what you want. Name the item and its partition key, and you get a ~1 RU point read. Make the engine search, and you pay for the search.
In Python, everything starts with a CosmosClient, created once and reused for the application's lifetime (it manages connection pooling internally — recreating it per request is a classic performance bug):
from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential
client = CosmosClient(url=endpoint, credential=DefaultAzureCredential())
container = client.get_database_client("chatdb").get_container_client("conversations")
Using DefaultAzureCredential (Microsoft Entra ID + managed identity) instead of account keys is the modern default — it removes secrets from configuration entirely, a theme that returns in Section 5.1.
Reading data comes in two flavors with very different price tags. When you know both the item's id and its partition key, use a point read:
item = container.read_item(item="conv_42", partition_key="user_1001")
When you must search, use a parameterized query — and scope it to a partition whenever possible:
items = container.query_items(
query="SELECT * FROM c WHERE c.status = @s",
parameters=[{"name": "@s", "value": "active"}],
partition_key="user_1001" # scoped: engine visits ONE partition
)
Omitting partition_key (or enabling cross-partition mode) forces a fan-out to every physical partition — the query still works, but you pay per partition visited. The SDK returns results as a paged iterable; iterating it handles continuation tokens for you. Always parameterize (@s) rather than string-concatenating values — same injection logic as SQL.
Each response carries an x-ms-request-charge header exposing exactly how many RUs the operation consumed — your primary tuning instrument during development.
⚠️ Exam Trap: If a scenario has both id and partition key in hand and the code runs SELECT * FROM c WHERE c.id = ..., the correct optimization is replacing the query with read_item(). A query for a known item can cost several times more RUs than the equivalent point read. Distractors will offer index tweaks or higher throughput — the point read is the answer.
Reflection Question: Why does scoping a query to a partition key reduce RU cost even when the result set is identical?