Azure Cosmos DB SDK for JavaScript

Introduction to Azure Cosmos DB SDK for JavaScript

The Azure Cosmos DB SDK for JavaScript provides a powerful and flexible way to interact with your Azure Cosmos DB data from Node.js and browser applications. This SDK simplifies common operations such as creating, reading, updating, and deleting (CRUD) items, querying data with SQL or other supported languages, managing databases and containers, and leveraging advanced features like the Change Feed and transactions.

Whether you're building a new application or integrating Cosmos DB into an existing one, this SDK offers a high-level abstraction to manage your data efficiently and reliably.

Getting Started

To begin using the Azure Cosmos DB SDK for JavaScript, you first need to install it using npm or yarn.

Installation


# Using npm
npm install @azure/cosmos

# Using yarn
yarn add @azure/cosmos
                

Basic Usage: Connecting to Cosmos DB

Once installed, you can connect to your Azure Cosmos DB account and perform basic operations.


const { CosmosClient } = require("@azure/cosmos");

// Replace with your endpoint and key
const endpoint = "https://YOUR_COSMOS_DB_ACCOUNT.documents.azure.com:443/";
const key = "YOUR_PRIMARY_KEY";

const client = new CosmosClient({ endpoint, key });

async function runSample() {
    // Create a database (if it doesn't exist)
    const { database } = await client.databases.createIfNotExists({ id: "myDatabase" });
    console.log(`Created database: ${database.id}`);

    // Create a container (if it doesn't exist)
    const { container } = await database.containers.createIfNotExists({ id: "myContainer", partitionKey: { paths: ["/categoryId"] } });
    console.log(`Created container: ${container.id}`);

    // Create an item
    const newItem = { categoryId: "A1", name: "Sample Item", description: "This is a sample item." };
    const { resource: createdItem } = await container.items.create(newItem);
    console.log(`Created item with id: ${createdItem.id}`);

    // Read an item
    const { resource: readItem } = await container.item(createdItem.id, createdItem.categoryId).read();
    console.log(`Read item: ${JSON.stringify(readItem, null, 2)}`);

    // Query items
    const querySpec = {
        query: "SELECT * FROM c WHERE c.categoryId = @categoryId",
        parameters: [
            { name: "@categoryId", value: "A1" }
        ]
    };
    const { resources: results } = await container.items.query(querySpec).fetchAll();
    console.log("Query results:", results);
}

runSample().catch(error => {
    console.error("An error occurred:", error);
});
                

Core Concepts

Understanding the core concepts of Azure Cosmos DB is crucial for effectively using the SDK.

Client Configuration

The CosmosClient is the entry point for all SDK operations. It requires your Cosmos DB account's endpoint and primary key.


const { CosmosClient } = require("@azure/cosmos");

const client = new CosmosClient({
    endpoint: "YOUR_COSMOS_DB_ENDPOINT",
    key: "YOUR_PRIMARY_KEY",
    // Optional: configure consistency level, retry options, etc.
    // consistencyLevel: "Strong",
    // retryOptions: {
    //     maxRetryAttempts: 5,
    //     maxRetryIntervalInMs: 3000
    // }
});
                

Database Operations

Databases are logical containers for your containers. You can create, read, update, and delete databases.

Database Operations API

Create Database


await client.databases.create({ id: "newDatabaseId" });
                        

Read Databases


const { resources: databases } = await client.databases.list().fetchAll();
console.log(databases);
                        

Get Database by ID


const database = client.database("existingDatabaseId");
                        

Delete Database


await client.database("databaseToDeleteId").delete();
                        

Container Operations

Containers hold your data items and are the primary resource for storing and querying data. Each container requires a partition key definition.

Container Operations API

Create Container


// Assuming 'database' is an instance of Database
await database.containers.create({
    id: "newContainerId",
    partitionKey: {
        paths: ["/partitionKeyPath"] // e.g., ["/userId"]
    }
});
                        

Read Containers


// Assuming 'database' is an instance of Database
const { resources: containers } = await database.containers.list().fetchAll();
console.log(containers);
                        

Get Container by ID


// Assuming 'database' is an instance of Database
const container = database.container("existingContainerId");
                        

Delete Container


// Assuming 'database' is an instance of Database
await database.container("containerToDeleteId").delete();
                        

Item Operations

Items are the individual documents or records within a container. They are represented as JSON objects.

Item Operations API

Create Item


const itemBody = {
    id: "uniqueItemId", // Optional, Cosmos DB can generate one
    partitionKeyPath: "value", // Required if partition key is defined
    // other properties...
};
// Assuming 'container' is an instance of Container
const { resource: createdItem } = await container.items.create(itemBody);
console.log(createdItem);
                        

Read Item


// Assuming 'container' is an instance of Container
const itemId = "existingItemId";
const partitionKeyValue = "partitionKeyValue"; // Required if partition key is defined
const { resource: item } = await container.item(itemId, partitionKeyValue).read();
console.log(item);
                        

Upsert Item

Upsert creates an item if it doesn't exist or replaces it if it does.


// Assuming 'container' is an instance of Container
const itemToUpsert = {
    id: "itemToUpsertId",
    partitionKeyPath: "value",
    updatedProperty: "new value"
};
const { resource: upsertedItem } = await container.items.upsert(itemToUpsert);
console.log(upsertedItem);
                        

Update Item

Update replaces an existing item. It requires the item's ID and partition key value.


// Assuming 'container' is an instance of Container
const itemToUpdate = {
    id: "itemIdToUpdate",
    partitionKeyPath: "value",
    newProperty: "updated value",
    _etag: "existingEtagValue" // Optional but recommended for concurrency control
};
const { resource: updatedItem } = await container.item("itemIdToUpdate", "partitionKeyValue").replace(itemToUpdate);
console.log(updatedItem);
                        

Delete Item


// Assuming 'container' is an instance of Container
await container.item("itemIdToDelete", "partitionKeyValue").delete();
console.log("Item deleted.");
                        

Advanced Topics

Explore advanced features to optimize your application's performance and functionality.

Querying Data

Use the SQL query language to retrieve specific data. The SDK supports various query options and pagination.


const querySpec = {
    query: "SELECT VALUE r.name FROM root r WHERE r.status = @status",
    parameters: [
        { name: "@status", value: "active" }
    ],
    // Optional: for ordered results or specific properties
    // orderBy: [{ item: "name", direction: "ascending" }]
};

// Assuming 'container' is an instance of Container
const { resources: queryResults } = await container.items.query(querySpec).fetchAll();
console.log(queryResults);
                

For complex queries, consider using stored procedures or user-defined functions (UDFs).

Transactions

Perform atomic operations across multiple items within a single logical partition using stored procedures.

Tip: Transactions in Cosmos DB are implemented using JavaScript stored procedures executed server-side.

// Example stored procedure (JavaScript)
/*
function transactItems() {
    var context = getContext();
    var request = context.getRequest();
    var items = request.getBody();
    var collection = getSelfLink("col"); // Assuming you know the collection link

    var response = { success: true };

    for (var i = 0; i < items.length; i++) {
        var item = items[i];
        var result = createDocument(collection, item); // Use createDocument, replaceDocument, etc.
        if (result.getStatusCode() >= 400) {
            response.success = false;
            response.error = result.getExecutionOutput();
            break;
        }
    }

    context.getResponse().setBody(response);
}
*/

// SDK code to execute stored procedure
// Assuming 'container' is an instance of Container
/*
const storedProcedureId = "transactItems";
const itemsToProcess = [ { id: "item1", value: 10 }, { id: "item2", value: 20 } ];
const { resource: result } = await container.storedProcedures.execute(storedProcedureId, [], itemsToProcess);
console.log(result);
*/
                

Change Feed

The Change Feed provides a persistent log of changes to items in a container, allowing you to build reactive applications.

Consuming the Change Feed

// Assuming 'container' is an instance of Container
const { read } = container.items.getChangeFeedIterator();

async function processChanges() {
    const response = await read({
        // Optional: continuationToken for resuming after a break
        // continuationToken: "your_previous_token"
        maxItemCount: 10 // Number of items to retrieve per call
    });

    if (response.resources && response.resources.length > 0) {
        response.resources.forEach(item => {
            console.log("Change detected:", item);
            // Process the changed item here
        });
        // Store the continuationToken for next iteration
        const nextToken = response.continuationToken;
        console.log("Next continuation token:", nextToken);
    }

    // To process continuously, you would poll this or use a loop
    // setTimeout(processChanges, 5000);
}

// Initial call
// processChanges();
                        

Bulk Operations

The SDK offers a bulkOperations method for efficiently performing multiple create, upsert, or delete operations in a single request.


// Assuming 'container' is an instance of Container
const operations = [
    {
        operationType: "Upsert",
        resourceBody: { id: "bulkItem1", partitionKeyPath: "val1", name: "Bulk Item One" }
    },
    {
        operationType: "Create",
        resourceBody: { id: "bulkItem2", partitionKeyPath: "val2", name: "Bulk Item Two" }
    },
    {
        operationType: "Delete",
        resourceId: "bulkItem3", // Use resourceId for delete
        partitionKeyPath: "val3"
    }
];

// Note: Bulk operations might require specific container configuration for optimal performance.
// const response = await container.items.bulkOperations(operations);
// console.log("Bulk operations response:", response);
// Handle individual operation statuses from the response
                

Performance Tuning

To optimize performance:

  • Choose the right partition key: Ensure even distribution of requests and data to avoid hot partitions.
  • Request Units (RUs): Monitor RU consumption and provision adequate throughput for your workload.
  • Indexing Policies: Customize indexing policies to include only necessary paths for faster queries.
  • Batching and Bulk Operations: Group operations to reduce network latency and improve throughput.
  • Connection Pooling: The SDK manages connections efficiently, but be mindful of creating too many clients in resource-constrained environments.
  • Client-side Request Options: Utilize options like maxItemCount for queries and change feed to control payload size.

API Reference

For detailed information on all available methods, classes, and properties, please refer to the official Azure Cosmos DB SDK for JavaScript API documentation.

Examples

Explore comprehensive code examples for various scenarios:

Troubleshooting

Common issues and their resolutions:

Common Issues

1. Throttling (HTTP 429 Too Many Requests)

Cause: Exceeding provisioned Request Units (RUs) for the container or database.

Solution: Increase the RU throughput, optimize queries, or implement retry logic with exponential backoff. The SDK has built-in retry policies.

2. Partition Key Not Found

Cause: When creating or accessing items, the partition key value for the item is missing or incorrect in the SDK call.

Solution: Ensure that every item you create or access has the correct partition key property, and that you provide the corresponding value in SDK operations.

3. High Latency

Cause: Network issues, inefficient queries, or hot partitions.

Solution: Check your application's network connectivity to Azure. Analyze query performance using the Azure portal. Review your partition key strategy. Consider using indexes effectively.

4. Item Not Found (HTTP 404)

Cause: The item ID or partition key provided does not match any existing item.

Solution: Verify the item ID and partition key value. Ensure the item has been created successfully.

5. Authentication Errors (HTTP 401/403)

Cause: Incorrect endpoint, key, or insufficient permissions.

Solution: Double-check your Cosmos DB account endpoint and primary key. Ensure the key has the necessary permissions (e.g., read, write).