azure-cosmos-ts
microsoft/skills
@azure/cosmos TypeScript/JavaScript SDK를 사용하여 Azure Cosmos DB NoSQL API에서 CRUD 작업, 쿼리, 대량 작업 및 컨테이너 관리를 수행합니다.
...모든 것을 확장하십시오@azure/cosmos (TypeScript/JavaScript)
Azure Cosmos DB NoSQL API 작업을 위한 데이터 플레인 SDK — 문서에 대한 CRUD, 쿼리, 일괄 처리 작업.
⚠️ 데이터 플레인 vs 관리 플레인
- 이 SDK (@azure/cosmos): 문서에 대한 CRUD 작업, 쿼리, 저장 프로시저
- 관리 SDK (@azure/arm-cosmosdb): ARM을 통해 계정, 데이터베이스, 컨테이너 생성
설치
npm install @azure/cosmos @azure/identity
현재 버전: 4.9.0
Node.js: >= 20.0.0
환경 변수
COSMOS_ENDPOINT=https://<account>.documents.azure.com:443/
COSMOS_DATABASE=<database-name>
COSMOS_CONTAINER=<container-name>
# 키 기반 인증 전용 (AAD 권장)
COSMOS_KEY=<account-key>
AZURE_TOKEN_CREDENTIALS=prod # 프로덕션에서 DefaultAzureCredential을 사용할 경우에만 필요
</account-key></container-name></database-name></account>인증
Microsoft Entra 토큰 자격 증명 (권장)
import { CosmosClient } from "@azure/cosmos";
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
// 로컬 개발: DefaultAzureCredential. 프로덕션: AZURE_TOKEN_CREDENTIALS=prod 또는 AZURE_TOKEN_CREDENTIALS=<specific_credential> 설정
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// 또는 프로덕션에서 특정 자격 증명을 직접 사용:
// 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,
});
</specific_credential>키 기반 인증
import { CosmosClient } from "@azure/cosmos";
// 옵션 1: 엔드포인트 + 키
const client = new CosmosClient({
endpoint: process.env.COSMOS_ENDPOINT!,
key: process.env.COSMOS_KEY!,
});
// 옵션 2: 연결 문자열
const client = new CosmosClient(process.env.COSMOS_CONNECTION_STRING!);
리소스 계층 구조
CosmosClient
└── Database
└── Container
├── Items (문서)
├── Scripts (저장 프로시저, 트리거, UDF)
└── Conflicts
핵심 작업
데이터베이스 및 컨테이너 설정
const { database } = await client.databases.createIfNotExists({
id: "my-database",
});
const { container } = await database.containers.createIfNotExists({
id: "my-container",
partitionKey: { paths: ["/partitionKey"] },
});
문서 생성
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);
</product>문서 읽기
const { resource } = await container
.item("product-1", "electronics") // id, partitionKey
.read<product>();
if (resource) {
console.log(resource.name);
}
</product>문서 업데이트 (바꾸기)
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);
}
</product></product>문서 업서트
const item: Product = {
id: "product-1",
partitionKey: "electronics",
name: "Laptop Pro",
price: 1299.99,
};
const { resource } = await container.items.upsert<product>(item);
</product>문서 삭제
await container.item("product-1", "electronics").delete();
문서 패치 (부분 업데이트)
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);
</product>쿼리
기본 쿼리
const { resources } = await container.items
.query<product>("SELECT * FROM c WHERE c.price </product>매개변수화된 쿼리 (권장)
import { SqlQuerySpec } from "@azure/cosmos";
const querySpec: SqlQuerySpec = {
query: "SELECT * FROM c WHERE c.partitionKey = @category AND c.price (querySpec)
.fetchAll();
페이지네이션이 있는 쿼리
const queryIterator = container.items.query<product>(querySpec, {
maxItemCount: 10, // 페이지당 항목 수
});
while (queryIterator.hasMoreResults()) {
const { resources, continuationToken } = await queryIterator.fetchNext();
console.log(`Page with ${resources?.length} items`);
// 필요한 경우 다음 페이지를 위해 continuationToken 사용
}
</product>크로스 파티션 쿼리
const { resources } = await container.items
.query<product>(
"SELECT * FROM c WHERE c.price > 500",
{ enableCrossPartitionQuery: true }
)
.fetchAll();
</product>일괄 처리 작업
일괄 처리 작업 실행
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 파티션 키
기본 파티션 키
const { container } = await database.containers.createIfNotExists({
id: "products",
partitionKey: { paths: ["/category"] },
});
계층적 파티션 키 (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,
},
});
// 작업에는 파티션 키 값의 배열이 필요함
const { resource } = await container.items.create({
id: "order-1",
tenantId: "tenant-a",
userId: "user-123",
sessionId: "session-xyz",
total: 99.99,
});
// 계층적 파티션 키로 읽기
const { resource: order } = await container
.item("order-1", ["tenant-a", "user-123", "session-xyz"])
.read();
오류 처리
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;
}
낙관적 동시성 (ETag)
// ETag으로 읽기
const { resource, etag } = await container
.item("product-1", "electronics")
.read<product>();
if (resource && etag) {
resource.price = 899.99;
try {
// ETag이 일치하는 경우에만 바꾸기
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");
}
}
}
</product>TypeScript 타입 참조
import {
// 클라이언트 및 리소스
CosmosClient,
Database,
Container,
Item,
Items,
// 작업
OperationInput,
BulkOperationType,
PatchOperation,
// 쿼리
SqlQuerySpec,
SqlParameter,
FeedOptions,
// 파티션 키
PartitionKeyDefinition,
PartitionKeyDefinitionVersion,
PartitionKeyKind,
// 응답
ItemResponse,
FeedResponse,
ResourceResponse,
// 오류
ErrorResponse,
} from "@azure/cosmos";
모범 사례
- Microsoft Entra 토큰 자격 증명 사용 — 로컬 개발에는
DefaultAzureCredential사용; 프로덕션에는ManagedIdentityCredential또는WorkloadIdentityCredential사용 - 항상 매개변수화된 쿼리 사용 — 주입 방지, 계획 캐싱 개선
- 파티션 키 지정 — 가능한 경우 크로스 파티션 쿼리 피하기
- 일괄 처리 작업 사용 — 여러 쓰기에는
executeBulkOperations사용 - 429 오류 처리 — 지수 백오프가 있는 재시도 로직 구현
- 동시성을 위해 ETag 사용 — 동시 시나리오에서 손실 업데이트 방지
- 종료 시 클라이언트 닫기 — 정리 단계에서
client.dispose()호출
일반적인 패턴
서비스 레이어 패턴
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> {
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>): 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;
}
}
</product></product></product></product></product></product></product>관련 SDK
| SDK | 목적 | 설치 |
|---|---|---|
| `@azure/cosmos` | 데이터 플레인 (이 SDK) | `npm install @azure/cosmos` |
| `@azure/arm-cosmosdb` | 관리 플레인 (ARM) | `npm install @azure/arm-cosmosdb` |
| `@azure/identity` | 인증 | `npm install @azure/identity` |
---
name: azure-cosmos-ts
description: Perform CRUD operations, queries, bulk operations, and container management on Azure Cosmos DB NoSQL API using the @azure/cosmos TypeScript/JavaScript SDK.
license: MIT
---
# @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>
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
```
## Authentication
### Microsoft Entra Token Credential (Recommended)
```typescript
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
```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 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
```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` |
모든 파일
0개 파일azure-cosmos-ts 설치
스킬 파일을 다운로드하여 .claude/skills/ 디렉토리에 추출하세요.
ZIP 다운로드저장소를 클론하고 스킬 파일을 프로젝트에 복사하세요.
git clone https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/azure-cosmos-ts # Copy SKILL.md to your .claude/skills/ directory
복사





집
