Generated banner for Azure Cosmos TS
Generated logo for Azure Cosmos TS

Azure Cosmos TS

MicrosoftOfficialCreated by Microsoft

  • Skill
  • Tool integration
  • Software development
  • MIT
  • No issues found

From the author

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".
— the description by Microsoft

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The azure-cosmos-ts skill provides the interface for the @azure/cosmos SDK, enabling developers to interact with Azure Cosmos DB NoSQL data plane resources. It covers document CRUD operations, NoSQL query execution, bulk data processing, and container management within a TypeScript or JavaScript environment.

What it does

  • Performs CRUD operations on documents within Cosmos DB containers.
  • Executes NoSQL queries using parameterized query specifications.
  • Manages bulk operations for batch processing of data.
  • Handles container and database resource interactions.
  • Supports authentication via Microsoft Entra ID or account keys.
  • Implements optimistic concurrency control using ETags.

When to use it

  • When building applications that require data storage in Azure Cosmos DB.
  • When performing document-level operations in a Node.js environment.
  • When implementing batch data processing or bulk updates.
  • When needing to execute complex NoSQL queries with pagination.

Advantages

  • Provides native TypeScript support for type-safe database interactions.
  • Includes built-in support for bulk operation execution.
  • Supports secure authentication patterns using Azure Identity.
  • Offers structured error handling for common database response codes.

Usage examples

“How do I create a new document in my Cosmos DB container using the TypeScript SDK?”
The agent provides a code snippet using container.items.create, demonstrating how to define an interface for the document and pass it to the SDK method.
“Show me how to perform a parameterized query to find products by category.”
The agent generates a SqlQuerySpec object with the query string and parameters array, then demonstrates calling container.items.query with that specification.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/microsoft/azure-cosmos-ts/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip azure-cosmos-ts.zip -d .agents/skills

Documentation by Microsoft

@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>
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production

Authentication

import { CosmosClient } from "@azure/cosmos";
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";

// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();

const client = new CosmosClient({
  endpoint: process.env.COSMOS_ENDPOINT!,
  aadCredentials: credential,
});
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();
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

  1. Use Microsoft Entra Token Credential — Use DefaultAzureCredential for local development; use ManagedIdentityCredential or WorkloadIdentityCredential for production

  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
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;
  }
}
SDKPurposeInstall
@azure/cosmosData plane (this SDK)npm install @azure/cosmos
@azure/arm-cosmosdbManagement plane (ARM)npm install @azure/arm-cosmosdb
@azure/identityAuthenticationnpm install @azure/identity

Files

references/query-patterns.md

Query Patterns Reference

Advanced query patterns for Azure Cosmos DB using the @azure/cosmos TypeScript SDK.

Overview

Cosmos DB supports SQL-like queries with support for JSON documents. This reference covers parameterized queries, pagination, cross-partition queries, and advanced query patterns.

SqlQuerySpec Interface

import { SqlQuerySpec, SqlParameter } from "@azure/cosmos";

interface SqlQuerySpec {
  /** SQL query text */
  query: string;
  /** Array of parameters */
  parameters?: SqlParameter[];
}

interface SqlParameter {
  /** Parameter name (including @) */
  name: string;
  /** Parameter value */
  value: unknown;
}

Always use parameterized queries to prevent injection and improve plan caching.

import { SqlQuerySpec, Container } from "@azure/cosmos";

interface Product {
  id: string;
  category: string;
  name: string;
  price: number;
  inStock: boolean;
}

// Single parameter
const querySpec: SqlQuerySpec = {
  query: "SELECT * FROM c WHERE c.category = @category",
  parameters: [
    { name: "@category", value: "electronics" }
  ]
};

const { resources } = await container.items
  .query<Product>(querySpec)
  .fetchAll();

// Multiple parameters
const rangeQuery: SqlQuerySpec = {
  query: `
    SELECT * FROM c 
    WHERE c.category = @category 
      AND c.price >= @minPrice 
      AND c.price <= @maxPrice
      AND c.inStock = @inStock
  `,
  parameters: [
    { name: "@category", value: "electronics" },
    { name: "@minPrice", value: 100 },
    { name: "@maxPrice", value: 1000 },
    { name: "@inStock", value: true }
  ]
};

const { resources: filtered } = await container.items
  .query<Product>(rangeQuery)
  .fetchAll();

Pagination with Continuation Tokens

import { FeedOptions } from "@azure/cosmos";

interface PagedResult<T> {
  items: T[];
  continuationToken?: string;
  hasMore: boolean;
}

async function queryWithPagination<T>(
  container: Container,
  querySpec: SqlQuerySpec,
  pageSize: number,
  continuationToken?: string
): Promise<PagedResult<T>> {
  const options: FeedOptions = {
    maxItemCount: pageSize,
    continuationToken
  };

  const queryIterator = container.items.query<T>(querySpec, options);
  const { resources, continuationToken: nextToken } = await queryIterator.fetchNext();

  return {
    items: resources || [],
    continuationToken: nextToken,
    hasMore: !!nextToken
  };
}

// Usage
let page = await queryWithPagination<Product>(
  container,
  { query: "SELECT * FROM c ORDER BY c.createdAt DESC" },
  10
);

console.log(`Page 1: ${page.items.length} items`);

while (page.hasMore) {
  page = await queryWithPagination<Product>(
    container,
    { query: "SELECT * FROM c ORDER BY c.createdAt DESC" },
    10,
    page.continuationToken
  );
  console.log(`Next page: ${page.items.length} items`);
}

Async Iterator Pattern

async function* queryAll<T>(
  container: Container,
  querySpec: SqlQuerySpec
): AsyncGenerator<T> {
  const queryIterator = container.items.query<T>(querySpec);
  
  while (queryIterator.hasMoreResults()) {
    const { resources } = await queryIterator.fetchNext();
    if (resources) {
      for (const item of resources) {
        yield item;
      }
    }
  }
}

// Usage
for await (const product of queryAll<Product>(container, querySpec)) {
  console.log(product.name);
}

Cross-Partition Queries

// Enable cross-partition query when partition key is not specified
const crossPartitionQuery: SqlQuerySpec = {
  query: "SELECT * FROM c WHERE c.price > @minPrice",
  parameters: [{ name: "@minPrice", value: 500 }]
};

const { resources } = await container.items
  .query<Product>(crossPartitionQuery, {
    enableCrossPartitionQuery: true
  })
  .fetchAll();

// Aggregate across partitions
const aggregateQuery: SqlQuerySpec = {
  query: "SELECT VALUE COUNT(1) FROM c WHERE c.category = @category",
  parameters: [{ name: "@category", value: "electronics" }]
};

const { resources: countResult } = await container.items
  .query<number>(aggregateQuery, { enableCrossPartitionQuery: true })
  .fetchAll();

console.log(`Total count: ${countResult[0]}`);

Projection Queries

// Select specific fields
const projectionQuery: SqlQuerySpec = {
  query: `
    SELECT c.id, c.name, c.price, c.category
    FROM c
    WHERE c.inStock = true
  `
};

interface ProductSummary {
  id: string;
  name: string;
  price: number;
  category: string;
}

const { resources } = await container.items
  .query<ProductSummary>(projectionQuery)
  .fetchAll();

// Computed properties
const computedQuery: SqlQuerySpec = {
  query: `
    SELECT 
      c.id,
      c.name,
      c.price,
      c.price * 0.9 AS discountedPrice,
      CONCAT(c.category, "-", c.id) AS sku
    FROM c
  `
};

Array Queries (JOIN)

interface Order {
  id: string;
  customerId: string;
  items: OrderItem[];
}

interface OrderItem {
  productId: string;
  quantity: number;
  price: number;
}

// Query items within arrays
const arrayQuery: SqlQuerySpec = {
  query: `
    SELECT 
      o.id AS orderId,
      o.customerId,
      i.productId,
      i.quantity,
      i.price
    FROM orders o
    JOIN i IN o.items
    WHERE i.quantity > @minQuantity
  `,
  parameters: [{ name: "@minQuantity", value: 5 }]
};

// Check if array contains value
const containsQuery: SqlQuerySpec = {
  query: `
    SELECT * FROM c 
    WHERE ARRAY_CONTAINS(c.tags, @tag)
  `,
  parameters: [{ name: "@tag", value: "featured" }]
};

Aggregate Functions

// COUNT
const countQuery = { query: "SELECT VALUE COUNT(1) FROM c" };

// SUM, AVG, MIN, MAX
const statsQuery: SqlQuerySpec = {
  query: `
    SELECT 
      COUNT(1) AS totalProducts,
      SUM(c.price) AS totalValue,
      AVG(c.price) AS averagePrice,
      MIN(c.price) AS minPrice,
      MAX(c.price) AS maxPrice
    FROM c
    WHERE c.category = @category
  `,
  parameters: [{ name: "@category", value: "electronics" }]
};

interface ProductStats {
  totalProducts: number;
  totalValue: number;
  averagePrice: number;
  minPrice: number;
  maxPrice: number;
}

const { resources } = await container.items
  .query<ProductStats>(statsQuery, { enableCrossPartitionQuery: true })
  .fetchAll();

ORDER BY and TOP

// Order by with TOP
const topQuery: SqlQuerySpec = {
  query: `
    SELECT TOP 10 *
    FROM c
    WHERE c.category = @category
    ORDER BY c.price DESC
  `,
  parameters: [{ name: "@category", value: "electronics" }]
};

// Multiple ORDER BY
const multiOrderQuery: SqlQuerySpec = {
  query: `
    SELECT * FROM c
    ORDER BY c.category ASC, c.price DESC
  `
};

// OFFSET and LIMIT (alternative to continuation tokens)
const offsetQuery: SqlQuerySpec = {
  query: `
    SELECT * FROM c
    ORDER BY c.createdAt DESC
    OFFSET @offset LIMIT @limit
  `,
  parameters: [
    { name: "@offset", value: 20 },
    { name: "@limit", value: 10 }
  ]
};

String Functions

const stringQuery: SqlQuerySpec = {
  query: `
    SELECT * FROM c
    WHERE CONTAINS(c.name, @searchTerm, true)
      OR STARTSWITH(c.name, @prefix)
  `,
  parameters: [
    { name: "@searchTerm", value: "phone" },
    { name: "@prefix", value: "Smart" }
  ]
};

// Case-insensitive search with LOWER
const caseInsensitiveQuery: SqlQuerySpec = {
  query: `
    SELECT * FROM c
    WHERE LOWER(c.name) LIKE @pattern
  `,
  parameters: [{ name: "@pattern", value: "%laptop%" }]
};

FeedOptions Reference

interface FeedOptions {
  /** Max items per page */
  maxItemCount?: number;
  
  /** Continuation token from previous page */
  continuationToken?: string;
  
  /** Enable cross-partition queries */
  enableCrossPartitionQuery?: boolean;
  
  /** Max parallelism for cross-partition queries */
  maxDegreeOfParallelism?: number;
  
  /** Partition key for scoped queries */
  partitionKey?: PartitionKey;
  
  /** Enable scan in queries (avoid if possible) */
  enableScanInQuery?: boolean;
  
  /** Populate index metrics in response */
  populateIndexMetrics?: boolean;
}

Query Performance Tips

  1. Always specify partition key — Avoids expensive cross-partition queries

  2. Use parameterized queries — Enables query plan caching

  3. Project only needed fields — Reduces response size and RU consumption

  4. Avoid cross-partition aggregates — Very expensive; consider materialized views

  5. Use continuation tokens — More efficient than OFFSET/LIMIT

  6. Check index metrics — Use populateIndexMetrics: true to diagnose slow queries

See Also

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Microsoft
Publisher
Microsoft (Official)
Source
microsoft/skills · .github/plugins/azure-sdk-typescript/skills/azure-cosmos-ts · commit 0696600
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download azure-cosmos-ts.zip