azure-cosmos-ts

CategoryCoding
AuthorAgentic Awesome Skills 社区
LicenseMIT
Rating4.60/5
Uses12.9K

@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

bash
npm install @azure/cosmos @azure/identity

Current Version: 4.9.0
Node.js: >= 20.0.0

Environment Variables

bash
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)

typescript
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

typescript
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

code
CosmosClient
└── Database
    └── Container
        ├── Items (documents)
        ├── Scripts (stored procedures, triggers, UDFs)
        └── Conflicts

Core Operations

Database & Container Setup

typescript
const { database } = await client.databases.createIfNotExists({
  id: "my-database",
});

const { container } = await database.containers.createIfNotExists({
id: "my-container",
partitionKey: { paths: ["/partitionKey"] },
});

Create Document

typescript
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

typescript
const { resource } = await container
  .item("product-1", "electronics") // id, partitionKey
  .read<Product>();

if (resource) {
console.log(resource.name);
}

Update Document (Replace)

typescript
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

typescript
const item: Product = {
  id: "product-1",
  partitionKey: "electronics",
  name: "Laptop Pro",
  price: 1299.99,
};

const { resource } = await container.items.upsert<Product>(item);

Delete Document

typescript
await container.item("product-1", "electronics").delete();

Patch Document (Partial Update)

typescript
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

typescript
const { resources } = await container.items
  .query<Product>("SELECT * FROM c WHERE c.price < 1000")
  .fetchAll();

Parameterized Query (Recommended)

typescript
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

typescript
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

typescript
const { resources } = await container.items
  .query<Product>(
    "SELECT * FROM c WHERE c.price > 500",
    { enableCrossPartitionQuery: true }
  )
  .fetchAll();

Bulk Operations

Execute Bulk Operations

typescript
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

typescript
const { container } = await database.containers.createIfNotExists({
  id: "products",
  partitionKey: { paths: ["/category"] },
});

Hierarchical Partition Key (MultiHash)

typescript
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

typescript
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)

typescript
// 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

typescript
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

1. Use AAD authentication — Prefer DefaultAzureCredential over keys
2. Always use parameterized queries — Prevents injection, improves plan caching
3. Specify partition key — Avoid cross-partition queries when possible
4. Use bulk operations — For multiple writes, use executeBulkOperations
5. Handle 429 errors — Implement retry logic with exponential backoff
6. Use ETags for concurrency — Prevent lost updates in concurrent scenarios
7. Close client on shutdown — Call client.dispose() in cleanup

Common Patterns

Service Layer Pattern

typescript
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 |

When to Use

This skill is applicable to execute the workflow or actions described in the overview.

Limitations

  • Use this skill only when the task clearly matches the scope described above.
  • Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
  • Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
Join our Telegram