Add this skill
npx mdskills install sickn33/azure-cosmos-tsComprehensive Cosmos DB SDK reference with clear examples, auth patterns, and error handling
1---2name: azure-cosmos-ts3description: |4 Azure Cosmos DB JavaScript/TypeScript SDK (@azure/cosmos) for data plane operations. Use for CRUD operations on documents, queries, bulk operations, and container management. Triggers: "Cosmos DB", "@azure/cosmos", "CosmosClient", "document CRUD", "NoSQL queries", "bulk operations", "partition key", "container.items".5package: "@azure/cosmos"6---78# @azure/cosmos (TypeScript/JavaScript)910Data plane SDK for Azure Cosmos DB NoSQL API operations — CRUD on documents, queries, bulk operations.1112> **⚠️ Data vs Management Plane**13> - **This SDK (@azure/cosmos)**: CRUD operations on documents, queries, stored procedures14> - **Management SDK (@azure/arm-cosmosdb)**: Create accounts, databases, containers via ARM1516## Installation1718```bash19npm install @azure/cosmos @azure/identity20```2122**Current Version**: 4.9.023**Node.js**: >= 20.0.02425## Environment Variables2627```bash28COSMOS_ENDPOINT=https://<account>.documents.azure.com:443/29COSMOS_DATABASE=<database-name>30COSMOS_CONTAINER=<container-name>31# For key-based auth only (prefer AAD)32COSMOS_KEY=<account-key>33```3435## Authentication3637### AAD with DefaultAzureCredential (Recommended)3839```typescript40import { CosmosClient } from "@azure/cosmos";41import { DefaultAzureCredential } from "@azure/identity";4243const client = new CosmosClient({44 endpoint: process.env.COSMOS_ENDPOINT!,45 aadCredentials: new DefaultAzureCredential(),46});47```4849### Key-Based Authentication5051```typescript52import { CosmosClient } from "@azure/cosmos";5354// Option 1: Endpoint + Key55const client = new CosmosClient({56 endpoint: process.env.COSMOS_ENDPOINT!,57 key: process.env.COSMOS_KEY!,58});5960// Option 2: Connection String61const client = new CosmosClient(process.env.COSMOS_CONNECTION_STRING!);62```6364## Resource Hierarchy6566```67CosmosClient68└── Database69 └── Container70 ├── Items (documents)71 ├── Scripts (stored procedures, triggers, UDFs)72 └── Conflicts73```7475## Core Operations7677### Database & Container Setup7879```typescript80const { database } = await client.databases.createIfNotExists({81 id: "my-database",82});8384const { container } = await database.containers.createIfNotExists({85 id: "my-container",86 partitionKey: { paths: ["/partitionKey"] },87});88```8990### Create Document9192```typescript93interface Product {94 id: string;95 partitionKey: string;96 name: string;97 price: number;98}99100const item: Product = {101 id: "product-1",102 partitionKey: "electronics",103 name: "Laptop",104 price: 999.99,105};106107const { resource } = await container.items.create<Product>(item);108```109110### Read Document111112```typescript113const { resource } = await container114 .item("product-1", "electronics") // id, partitionKey115 .read<Product>();116117if (resource) {118 console.log(resource.name);119}120```121122### Update Document (Replace)123124```typescript125const { resource: existing } = await container126 .item("product-1", "electronics")127 .read<Product>();128129if (existing) {130 existing.price = 899.99;131 const { resource: updated } = await container132 .item("product-1", "electronics")133 .replace<Product>(existing);134}135```136137### Upsert Document138139```typescript140const item: Product = {141 id: "product-1",142 partitionKey: "electronics",143 name: "Laptop Pro",144 price: 1299.99,145};146147const { resource } = await container.items.upsert<Product>(item);148```149150### Delete Document151152```typescript153await container.item("product-1", "electronics").delete();154```155156### Patch Document (Partial Update)157158```typescript159import { PatchOperation } from "@azure/cosmos";160161const operations: PatchOperation[] = [162 { op: "replace", path: "/price", value: 799.99 },163 { op: "add", path: "/discount", value: true },164 { op: "remove", path: "/oldField" },165];166167const { resource } = await container168 .item("product-1", "electronics")169 .patch<Product>(operations);170```171172## Queries173174### Simple Query175176```typescript177const { resources } = await container.items178 .query<Product>("SELECT * FROM c WHERE c.price < 1000")179 .fetchAll();180```181182### Parameterized Query (Recommended)183184```typescript185import { SqlQuerySpec } from "@azure/cosmos";186187const querySpec: SqlQuerySpec = {188 query: "SELECT * FROM c WHERE c.partitionKey = @category AND c.price < @maxPrice",189 parameters: [190 { name: "@category", value: "electronics" },191 { name: "@maxPrice", value: 1000 },192 ],193};194195const { resources } = await container.items196 .query<Product>(querySpec)197 .fetchAll();198```199200### Query with Pagination201202```typescript203const queryIterator = container.items.query<Product>(querySpec, {204 maxItemCount: 10, // Items per page205});206207while (queryIterator.hasMoreResults()) {208 const { resources, continuationToken } = await queryIterator.fetchNext();209 console.log(`Page with ${resources?.length} items`);210 // Use continuationToken for next page if needed211}212```213214### Cross-Partition Query215216```typescript217const { resources } = await container.items218 .query<Product>(219 "SELECT * FROM c WHERE c.price > 500",220 { enableCrossPartitionQuery: true }221 )222 .fetchAll();223```224225## Bulk Operations226227### Execute Bulk Operations228229```typescript230import { BulkOperationType, OperationInput } from "@azure/cosmos";231232const operations: OperationInput[] = [233 {234 operationType: BulkOperationType.Create,235 resourceBody: { id: "1", partitionKey: "cat-a", name: "Item 1" },236 },237 {238 operationType: BulkOperationType.Upsert,239 resourceBody: { id: "2", partitionKey: "cat-a", name: "Item 2" },240 },241 {242 operationType: BulkOperationType.Read,243 id: "3",244 partitionKey: "cat-b",245 },246 {247 operationType: BulkOperationType.Replace,248 id: "4",249 partitionKey: "cat-b",250 resourceBody: { id: "4", partitionKey: "cat-b", name: "Updated" },251 },252 {253 operationType: BulkOperationType.Delete,254 id: "5",255 partitionKey: "cat-c",256 },257 {258 operationType: BulkOperationType.Patch,259 id: "6",260 partitionKey: "cat-c",261 resourceBody: {262 operations: [{ op: "replace", path: "/name", value: "Patched" }],263 },264 },265];266267const response = await container.items.executeBulkOperations(operations);268269response.forEach((result, index) => {270 if (result.statusCode >= 200 && result.statusCode < 300) {271 console.log(`Operation ${index} succeeded`);272 } else {273 console.error(`Operation ${index} failed: ${result.statusCode}`);274 }275});276```277278## Partition Keys279280### Simple Partition Key281282```typescript283const { container } = await database.containers.createIfNotExists({284 id: "products",285 partitionKey: { paths: ["/category"] },286});287```288289### Hierarchical Partition Key (MultiHash)290291```typescript292import { PartitionKeyDefinitionVersion, PartitionKeyKind } from "@azure/cosmos";293294const { container } = await database.containers.createIfNotExists({295 id: "orders",296 partitionKey: {297 paths: ["/tenantId", "/userId", "/sessionId"],298 version: PartitionKeyDefinitionVersion.V2,299 kind: PartitionKeyKind.MultiHash,300 },301});302303// Operations require array of partition key values304const { resource } = await container.items.create({305 id: "order-1",306 tenantId: "tenant-a",307 userId: "user-123",308 sessionId: "session-xyz",309 total: 99.99,310});311312// Read with hierarchical partition key313const { resource: order } = await container314 .item("order-1", ["tenant-a", "user-123", "session-xyz"])315 .read();316```317318## Error Handling319320```typescript321import { ErrorResponse } from "@azure/cosmos";322323try {324 const { resource } = await container.item("missing", "pk").read();325} catch (error) {326 if (error instanceof ErrorResponse) {327 switch (error.code) {328 case 404:329 console.log("Document not found");330 break;331 case 409:332 console.log("Conflict - document already exists");333 break;334 case 412:335 console.log("Precondition failed (ETag mismatch)");336 break;337 case 429:338 console.log("Rate limited - retry after:", error.retryAfterInMs);339 break;340 default:341 console.error(`Cosmos error ${error.code}: ${error.message}`);342 }343 }344 throw error;345}346```347348## Optimistic Concurrency (ETags)349350```typescript351// Read with ETag352const { resource, etag } = await container353 .item("product-1", "electronics")354 .read<Product>();355356if (resource && etag) {357 resource.price = 899.99;358359 try {360 // Replace only if ETag matches361 await container.item("product-1", "electronics").replace(resource, {362 accessCondition: { type: "IfMatch", condition: etag },363 });364 } catch (error) {365 if (error instanceof ErrorResponse && error.code === 412) {366 console.log("Document was modified by another process");367 }368 }369}370```371372## TypeScript Types Reference373374```typescript375import {376 // Client & Resources377 CosmosClient,378 Database,379 Container,380 Item,381 Items,382383 // Operations384 OperationInput,385 BulkOperationType,386 PatchOperation,387388 // Queries389 SqlQuerySpec,390 SqlParameter,391 FeedOptions,392393 // Partition Keys394 PartitionKeyDefinition,395 PartitionKeyDefinitionVersion,396 PartitionKeyKind,397398 // Responses399 ItemResponse,400 FeedResponse,401 ResourceResponse,402403 // Errors404 ErrorResponse,405} from "@azure/cosmos";406```407408## Best Practices4094101. **Use AAD authentication** — Prefer `DefaultAzureCredential` over keys4112. **Always use parameterized queries** — Prevents injection, improves plan caching4123. **Specify partition key** — Avoid cross-partition queries when possible4134. **Use bulk operations** — For multiple writes, use `executeBulkOperations`4145. **Handle 429 errors** — Implement retry logic with exponential backoff4156. **Use ETags for concurrency** — Prevent lost updates in concurrent scenarios4167. **Close client on shutdown** — Call `client.dispose()` in cleanup417418## Common Patterns419420### Service Layer Pattern421422```typescript423export class ProductService {424 private container: Container;425426 constructor(client: CosmosClient) {427 this.container = client428 .database(process.env.COSMOS_DATABASE!)429 .container(process.env.COSMOS_CONTAINER!);430 }431432 async getById(id: string, category: string): Promise<Product | null> {433 try {434 const { resource } = await this.container435 .item(id, category)436 .read<Product>();437 return resource ?? null;438 } catch (error) {439 if (error instanceof ErrorResponse && error.code === 404) {440 return null;441 }442 throw error;443 }444 }445446 async create(product: Omit<Product, "id">): Promise<Product> {447 const item = { ...product, id: crypto.randomUUID() };448 const { resource } = await this.container.items.create<Product>(item);449 return resource!;450 }451452 async findByCategory(category: string): Promise<Product[]> {453 const querySpec: SqlQuerySpec = {454 query: "SELECT * FROM c WHERE c.partitionKey = @category",455 parameters: [{ name: "@category", value: category }],456 };457 const { resources } = await this.container.items458 .query<Product>(querySpec)459 .fetchAll();460 return resources;461 }462}463```464465## Related SDKs466467| SDK | Purpose | Install |468|-----|---------|---------|469| `@azure/cosmos` | Data plane (this SDK) | `npm install @azure/cosmos` |470| `@azure/arm-cosmosdb` | Management plane (ARM) | `npm install @azure/arm-cosmosdb` |471| `@azure/identity` | Authentication | `npm install @azure/identity` |472
Full transparency — inspect the skill content before installing.