Skip to main content

azure-cosmos-ts

"Implement — Azure Cosmos DB JavaScript/TypeScript SDK (@azure/cosmos) for data plane operations. Use for CRUD operations"

Informations de source

Dépôt
thiagofernandes1987-create/APEX
Dernière activité de la source
18 avril 2026 à 09:35
Langue détectée de SKILL.md
anglais
Étoiles
2
Forks
0

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
azure-cosmos-ts
description
"Implement — Azure Cosmos DB JavaScript/TypeScript SDK (@azure/cosmos) for data plane operations. Use for CRUD operations"
# @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 ``` 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",
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub