TypeScript client for Azure Cosmos DB

Your Cosmos DB bill,type-checked.

CosmosQL turns your schema into types and your partition key into a compile-time requirement. Point reads stay at 1 RU, scans across partitions are opt-in, and the package ships with zero runtime dependencies.

$ npm install cosmosql
query.ts
const users = container("users", {
id: field.string(),
email: field.string(),
name: field.string(),
}).partitionKey("email");
const user = await db.users.findUnique({
where: { id: "u_1" },
});
// ^? { id: string; email: string; name: string } | null
checking…tsc --noEmit
runtime dependencies
0
per point read
1 RU
types from your schema
TS 5+
and Bun 1.0+
Node 18+
open source
MIT

§01/The bill

Every query is a line item.

Cosmos DB bills in request units, and the way you address a document decides how many you spend. CosmosQL makes the cheap path the default and the expensive one explicit.

01

Point read

findUnique({ where: { id, email } })
1

The default. Won't compile without the partition key.

02

Partition query

findMany({ partitionKey, where })
3–5

Filters, sorting and paging inside one partition.

03

Cross-partition scan

findMany({ enableCrossPartitionQuery: true })
~100

Refused unless you opt in, by name, in the call.

§02/Compared

Same lookup. Fewer ways to get it wrong.

The official SDK will happily run a query without a partition key and hand you back any. CosmosQL reads the partition key off your schema and won't build until you supply it.

@azure/cosmosCompiles. Fans out to every partition.
import { CosmosClient } from "@azure/cosmos";

const client = new CosmosClient(connectionString);
const container = client
  .database("app")
  .container("users");

const { resources } = await container.items
  .query({
    query: "SELECT * FROM c WHERE c.id = @id",
    parameters: [{ name: "@id", value: "u_1" }],
  })
  .fetchAll();

const user = resources[0]; // any
Result type
any
Partition key
optional, easy to forget
Runtime dependencies
11 direct
cosmosqlPoint read. One partition, 1 RU.
import { createClient } from "cosmosql";
import { users } from "./schema";

const db = await createClient({
  connectionString,
  database: "app",
}).withContainers({ users });

const user = await db.users.findUnique({
  where: { id: "u_1", email: "ada@lovelace.dev" },
});
// { id: string; email: string; name: string } | null
Result type
inferred from your schema
Partition key
required by the compiler
Runtime dependencies
0

§03/The API

Prisma-shaped. Partition-aware.

If you have used Prisma, you already know the method names. The difference is that every one of them knows where your partition key lives.

import { container, field } from "cosmosql";

export const users = container("users", {
  id: field.string(),
  email: field.string(),
  name: field.string(),
  bio: field.string().optional(),
  age: field.number(),
  isActive: field.boolean().default(true),
  createdAt: field.date(),
}).partitionKey("email");

Start with one container.

  1. 01Install
  2. 02Define a container
  3. 03Query it
$ npm install cosmosql
Five-minute quickstart