DocumentDB TypeScript API
This Node module provides the 'missing' TypeScript 2 API for Microsoft's awesome SQL-queried schema-free Cloud NoSQL database, DocumentDB.
No TypeScript required — you can use this module with plain JavaScript too, and enjoy enhanced Intellisense in an editor that supports TypeScript 2 definition files, such as VS Code.
NOTE: The author of this module is not affiliated with Microsoft.
Goals
This module was written with the following goals in mind:
- Streamline common DocumentDB use cases;
- Enable a better developer experience with accurate Intellisense;
- Reduce clutter by adding classes and combining methods;
- Use idiomatic TypeScript 2 (es6 for Node JS) internally and externally;
- Enable asynchronous programming with
async/await
and/or Promises (native Node JS).
Project Status
I just needed something quick, so at this point parts of the DocumentDB feature set are still missing. If your app needs stored procedures, or users and permissions, for example, then please add to this code (preferably as new classes). Pull requests are greatly appreciated!
Installation
Use npm
to install this module (TypeScript optional):
npm install documentdb-typescript
Then import this module into your JavaScript or TypeScript code:
const DB = require("documentdb-typescript");
const client = new DB.Client();
import * as DB from "documentdb-typescript";
const client = new DB.Client();
import { Client } from "documentdb-typescript";
const client = new Client();
Usage
This module exports only the following symbols:
Client
class: contains methods for dealing with the connection to DocumentDB account.Database
class: represents a database.Collection
class: represents a collection, and contains methods for dealing with documents.DocumentStream
class: contains methods for reading query results (used as a return type only).StoreMode
enum: lists different modes of storing documents in a collection.concurrencyLimit
(number): controls how many requests should be outstanding globally at any time; defaults to 25.
Where is Document? —
There is no 'Document' class because documents are really just plain JavaScript objects (of type any
), which may or may not have some extra properties (such as _self
) depending on where they come from, and are very hard to pin down as such. Also, the results of a query may or may not be full documents, which makes it impossible to predict exact return types. Adding another abstraction layer (à la the .NET API with its set- / getPropertyValue methods) doesn't seem like the right thing to do in JavaScript code.
Client
Start here if you need to work with multiple databases, or set advanced connection options.
async function main(url, masterKey) {
var client = new Client(url, masterKey);
client.enableConsoleLog = true;
console.log(await client.getAccountInfoAsync());
await client.openAsync();
var dbs = await client.listDatabasesAsync();
console.log(dbs.map(db => db.id));
client.close();
}
The original DocumentClient from the documentdb
module is kept in the documentClient
property, after the openAsync
method is called.
Database
Start here if you need to list all collections in a database, or delete it from the account. Nothing else here.
async function main(url, masterKey) {
var client = new Client(url, masterKey);
var db = new Database("sample", client);
var db2 = new Database("sample", url, masterKey);
await db.openOrCreateAsync();
await db.openAsync();
var colls = await db.listCollectionsAsync();
console.log(colls.map(c => c.id));
await db.deleteAsync();
}
Collection
This is where most of the functionality lives. Finding and/or creating a collection, optionally along with the database is easy:
async function main(url, masterKey) {
var client = new Client(url, masterKey);
var db = new Database("sample", client);
var coll = new Collection("test", db);
var coll2 = new Collection("test", "sample", client);
var coll3 = new Collection("test", "sample", url, masterKey);
await coll.openOrCreateDatabaseAsync();
await coll.openOrCreateAsync();
await coll.openAsync();
await coll.deleteAsync();
}
The Collection
instance has methods for setting and getting provisioned throughput levels:
async function main(url, masterKey) {
var client = new Client(url, masterKey);
var coll = new Collection("test", "sample", client);
await coll.openOrCreateDatabaseAsync();
await coll.setOfferInfoAsync(500);
console.log(await coll.getOfferInfoAsync());
}
Storing documents
This module abstracts away most of the work involved in creating, updating, and upserting documents in a collection.
async function main(url, masterKey) {
var client = new Client(url, masterKey);
client.enableConsoleLog = true;
var coll = new Collection("test", "sample", client);
await coll.openOrCreateDatabaseAsync();
var doc: any = { id: "abc", foo: "bar" };
doc = await coll.storeDocumentAsync(doc, StoreMode.CreateOnly);
doc.foo = "baz";
doc = await coll.storeDocumentAsync(doc, StoreMode.UpdateOnly);
doc.foo = "bla";
doc = await coll.storeDocumentAsync(doc, StoreMode.UpdateOnlyIfNoChange);
var doc2: any = { id: "abc", foo: "bar" };
var doc3: any = { id: "abc", foo: "baz" };
await Promise.all([
coll.storeDocumentAsync(doc, StoreMode.Upsert),
coll.storeDocumentAsync(doc)
]);
await coll.deleteDocumentAsync(doc);
}
Finding documents
There are a number of ways to find a document or a set of documents in a collection.
async function main(url, masterKey) {
var coll = await new Collection("test", "sample", url, masterKey)
.openOrCreateDatabaseAsync();
var doc = await coll.findDocumentAsync("abc");
try {
var user = await coll.findDocumentAsync({
isAccount: true,
isInactive: false,
email: "foo@example.com"
});
console.log("Found " + user._self);
}
catch (err) {
console.log("User not found");
}
var stream = coll.queryDocuments();
var stream2 = coll.queryDocuments("select * from c");
var stream3 = coll.queryDocuments({
query: "select * from c where c.foo = @foo",
parameters: [
{ name: "@foo", value: "bar" }
]
});
}
Iterating over query results
The queryDocuments
method on the Collection
class is one of the few methods that does not return a Promise. Instead, it returns a DocumentStream
instance which can be used to iterate over the results or load them all in one go.
async function main(url, masterKey) {
var coll = await new Collection("test", "sample", url, masterKey)
.openOrCreateDatabaseAsync();
var q = coll.queryDocuments();
var allDocs = await q.toArray();
var q2 = await coll.queryDocuments("select * from c");
q2.forEach(doc => {
console.log(doc._self);
});
q.reset();
while (true) {
var rdoc = await q.read();
if (!rdoc) break;
console.log(rdoc._self);
}
var ids = await coll.queryDocuments("select * from c")
.mapAsync(doc => doc.id);
var newest = await coll.queryDocuments(
"select top 1 c._ts from c order by c._ts desc")
.read();
if (!newest)
console.log("No documents");
else
console.log("Last change " +
(Date.now() / 1000 - newest._ts) +
"s ago");
}