@azure/cosmos (TypeScript/JavaScript)
Data plane SDK for Azure Cosmos DB NoSQL API operations — CRUD on documents, queries, bulk operations.
⚠️ Data vs Management Plane
-
This SDK (@azure/cosmos): CRUD operations on documents, queries, stored procedures
-
Management SDK (@azure/arm-cosmosdb): Create accounts, databases, containers via ARM
Installation
npm install @azure/cosmos @azure/identity
Current Version: 4.9.0
Node.js: >= 20.0.0
Environment Variables
COSMOS_ENDPOINT=https://<account>.documents.azure.com:443/ COSMOS_DATABASE=<database-name> COSMOS_CONTAINER=<container-name>
For key-based auth only (prefer AAD)
COSMOS_KEY=<account-key>
Authentication
AAD with DefaultAzureCredential (Recommended)
import { CosmosClient } from "@azure/cosmos"; import { DefaultAzureCredential } from "@azure/identity";
const client = new CosmosClient({ endpoint: process.env.COSMOS_ENDPOINT!, aadCredentials: new DefaultAzureCredential(), });
Key-Based Authentication
import { CosmosClient } from "@azure/cosmos";
// Option 1: Endpoint + Key const client = new CosmosClient({ endpoint: process.env.COSMOS_ENDPOINT!, key: process.env.COSMOS_KEY!, });
// Option 2: Connection String const client = new CosmosClient(process.env.COSMOS_CONNECTION_STRING!);
Resource Hierarchy
CosmosClient └── Database └── Container ├── Items (documents) ├── Scripts (stored procedures, triggers, UDFs) └── Conflicts
Core Operations
Database & Container Setup
const { database } = await client.databases.createIfNotExists({ id: "my-database", });
const { container } = await database.containers.createIfNotExists({ id: "my-container", partitionKey: { paths: ["/partitionKey"] }, });
Create Document
interface Product { id: string; partitionKey: string; name: string; price: number; }
const item: Product = { id: "product-1", partitionKey: "electronics", name: "Laptop", price: 999.99, };
const { resource } = await container.items.create<Product>(item);
Read Document
const { resource } = await container .item("product-1", "electronics") // id, partitionKey .read<Product>();
if (resource) { console.log(resource.name); }
Update Document (Replace)
const { resource: existing } = await container .item("product-1", "electronics") .read<Product>();
if (existing) { existing.price = 899.99; const { resource: updated } = await container .item("product-1", "electronics") .replace<Product>(existing); }
Upsert Document
const item: Product = { id: "product-1", partitionKey: "electronics", name: "Laptop Pro", price: 1299.99, };
const { resource } = await container.items.upsert<Product>(item);
Delete Document
await container.item("product-1", "electronics").delete();
Patch Document (Partial Update)
import { PatchOperation } from "@azure/cosmos";
const operations: PatchOperation[] = [ { op: "replace", path: "/price", value: 799.99 }, { op: "add", path: "/discount", value: true }, { op: "remove", path: "/oldField" }, ];
const { resource } = await container .item("product-1", "electronics") .patch<Product>(operations);
Queries
Simple Query
const { resources } = await container.items .query<Product>("SELECT * FROM c WHERE c.price < 1000") .fetchAll();
Parameterized Query (Recommended)
import { SqlQuerySpec } from "@azure/cosmos";
const querySpec: SqlQuerySpec = { query: "SELECT * FROM c WHERE c.partitionKey = @category AND c.price < @maxPrice", parameters: [ { name: "@category", value: "electronics" }, { name: "@maxPrice", value: 1000 }, ], };
const { resources } = await container.items .query<Product>(querySpec) .fetchAll();
Query with Pagination
const queryIterator = container.items.query<Product>(querySpec, { maxItemCount: 10, // Items per page });
while (queryIterator.hasMoreResults()) {
const { resources, continuationToken } = await queryIterator.fetchNext();
console.log(Page with ${resources?.length} items);
// Use continuationToken for next page if needed
}
Cross-Partition Query
const { resources } = await container.items .query<Product>( "SELECT * FROM c WHERE c.price > 500", { enableCrossPartitionQuery: true } ) .fetchAll();
Bulk Operations
Execute Bulk Operations
import { BulkOperationType, OperationInput } from "@azure/cosmos";
const operations: OperationInput[] = [ { operationType: BulkOperationType.Create, resourceBody: { id: "1", partitionKey: "cat-a", name: "Item 1" }, }, { operationType: BulkOperationType.Upsert, resourceBody: { id: "2", partitionKey: "cat-a", name: "Item 2" }, }, { operationType: BulkOperationType.Read, id: "3", partitionKey: "cat-b", }, { operationType: BulkOperationType.Replace, id: "4", partitionKey: "cat-b", resourceBody: { id: "4", partitionKey: "cat-b", name: "Updated" }, }, { operationType: BulkOperationType.Delete, id: "5", partitionKey: "cat-c", }, { operationType: BulkOperationType.Patch, id: "6", partitionKey: "cat-c", resourceBody: { operations: [{ op: "replace", path: "/name", value: "Patched" }], }, }, ];
const response = await container.items.executeBulkOperations(operations);
response.forEach((result, index) => {
if (result.statusCode >= 200 && result.statusCode < 300) {
console.log(Operation ${index} succeeded);
} else {
console.error(Operation ${index} failed: ${result.statusCode});
}
});
Partition Keys
Simple Partition Key
const { container } = await database.containers.createIfNotExists({ id: "products", partitionKey: { paths: ["/category"] }, });
Hierarchical Partition Key (MultiHash)
import { PartitionKeyDefinitionVersion, PartitionKeyKind } from "@azure/cosmos";
const { container } = await database.containers.createIfNotExists({ id: "orders", partitionKey: { paths: ["/tenantId", "/userId", "/sessionId"], version: PartitionKeyDefinitionVersion.V2, kind: PartitionKeyKind.MultiHash, }, });
// Operations require array of partition key values const { resource } = await container.items.create({ id: "order-1", tenantId: "tenant-a", userId: "user-123", sessionId: "session-xyz", total: 99.99, });
// Read with hierarchical partition key const { resource: order } = await container .item("order-1", ["tenant-a", "user-123", "session-xyz"]) .read();
Error Handling
import { ErrorResponse } from "@azure/cosmos";
try {
const { resource } = await container.item("missing", "pk").read();
} catch (error) {
if (error instanceof ErrorResponse) {
switch (error.code) {
case 404:
console.log("Document not found");
break;
case 409:
console.log("Conflict - document already exists");
break;
case 412:
console.log("Precondition failed (ETag mismatch)");
break;
case 429:
console.log("Rate limited - retry after:", error.retryAfterInMs);
break;
default:
console.error(Cosmos error ${error.code}: ${error.message});
}
}
throw error;
}
Optimistic Concurrency (ETags)
// Read with ETag const { resource, etag } = await container .item("product-1", "electronics") .read<Product>();
if (resource && etag) { resource.price = 899.99;
try { // Replace only if ETag matches await container.item("product-1", "electronics").replace(resource, { accessCondition: { type: "IfMatch", condition: etag }, }); } catch (error) { if (error instanceof ErrorResponse && error.code === 412) { console.log("Document was modified by another process"); } } }
TypeScript Types Reference
import { // Client & Resources CosmosClient, Database, Container, Item, Items,
// Operations OperationInput, BulkOperationType, PatchOperation,
// Queries SqlQuerySpec, SqlParameter, FeedOptions,
// Partition Keys PartitionKeyDefinition, PartitionKeyDefinitionVersion, PartitionKeyKind,
// Responses ItemResponse, FeedResponse, ResourceResponse,
// Errors ErrorResponse, } from "@azure/cosmos";
Best Practices
-
Use AAD authentication — Prefer DefaultAzureCredential over keys
-
Always use parameterized queries — Prevents injection, improves plan caching
-
Specify partition key — Avoid cross-partition queries when possible
-
Use bulk operations — For multiple writes, use executeBulkOperations
-
Handle 429 errors — Implement retry logic with exponential backoff
-
Use ETags for concurrency — Prevent lost updates in concurrent scenarios
-
Close client on shutdown — Call client.dispose() in cleanup
Common Patterns
Service Layer Pattern
export class ProductService { private container: Container;
constructor(client: CosmosClient) { this.container = client .database(process.env.COSMOS_DATABASE!) .container(process.env.COSMOS_CONTAINER!); }
async getById(id: string, category: string): Promise<Product | null> { try { const { resource } = await this.container .item(id, category) .read<Product>(); return resource ?? null; } catch (error) { if (error instanceof ErrorResponse && error.code === 404) { return null; } throw error; } }
async create(product: Omit<Product, "id">): Promise<Product> { const item = { ...product, id: crypto.randomUUID() }; const { resource } = await this.container.items.create<Product>(item); return resource!; }
async findByCategory(category: string): Promise<Product[]> { const querySpec: SqlQuerySpec = { query: "SELECT * FROM c WHERE c.partitionKey = @category", parameters: [{ name: "@category", value: category }], }; const { resources } = await this.container.items .query<Product>(querySpec) .fetchAll(); return resources; } }
Related SDKs
SDK Purpose Install
@azure/cosmos
Data plane (this SDK) npm install @azure/cosmos
@azure/arm-cosmosdb
Management plane (ARM) npm install @azure/arm-cosmosdb
@azure/identity
Authentication npm install @azure/identity