little cubes

How to use Gremlin Cosmos DB with Azure Functions

The story that Microsoft docs, for some reason, refuse to tell you

So you’ve got a Functions project, and you’ve created a Cosmos DB in the azure portal, now how do you connect them?

The way it’s supposed to work (but doesn’t)Section titled: The way it’s supposed to work (but doesn’t)

The way that the interaction of writing to a database from within an Azure Function is supposed to work is via a binding.

You create an entry in your function.json file for an output binding to Cosmos DB:

{
"name": "employeeDocument",
"type": "cosmosDB",
"databaseName": "my-database",
"collectionName": "myCollection",
"createIfNotExists": true,
"connectionStringSetting": "MyAccount_COSMOSDB",
"direction": "out"
}

And then you use the context.bindings object to write to the property you specified in the name field above:

export default async function (context) {
let document = {
slug: 4,
name: 'Zach',
employeeslug: '004',
address: '221b Baker St',
}
context.bindings.employeeDocument = document
}

But that DOES NOT WORK!

All that happens when this code runs is a vertex with no data is created in the Cosmos DB.

Why is that? Because Microsoft does not currently support binding to a Cosmos DB using the Gremlin API.

All of their examples (those are three separate links) use the SQL API, without anywhere saying that these bindings are only supported in the SQL API, and not in the Gremlin API.

I’m guessing that the SQL API came out before the Gremlin API, and was at the time this documentation was written the only available API. So the Microsoft MVPs that wrote these “documentation” articles weren’t aware that they should state that what they were doing was specific to SQL.

The way it actually worksSection titled: The way it actually works

Now that I’ve hopefully helped you avoid the pitfall that stole hours of my life, what we’re actually going to do is make use of the gremlin NPM package.

Remember that because we’re developing our functions locally, we can install NPM packages and then the node_modules directory automatically gets pushed into Azure with your function code so that everything continues to work in the cloud.

Terminal window
npm i gremlin

After installing the NPM package, first thing you need to do is define some configuration variables about your database. At the root of your project create a SharedCode directory and inside of that create a gremlinConfig.ts file.

SharedCode/gremlinConfig.ts
export default {
endpoint: 'wss://my-cosmos-db.gremlin.cosmos.azure.com:443',
primaryKey: 'superSuperSecretKey==',
database: 'my-database',
collection: 'myCollection',
}

You can find each of these data in your Cosmos DB on the Azure portal:

  • endpoint can be found on the Overview tab
  • primaryKey can be found on the Keys tab
  • database can be found on the Data Explorer tab, it is the top level item in the DATA section
  • collection can be found by expanding the (just described) database to show its collections

Once you have the configuration down, you can then use that information to create a Gremlin client that you’ll use to communicate with the database:

MyFunction/index.ts
import config from '../SharedCode/gremlinConfig'
export default async function (context) {
let authenticator = new Gremlin.driver.auth.PlainTextSaslAuthenticator(
`/dbs/${config.database}/colls/${config.collection}`,
config.primaryKey,
)
let client = new Gremlin.driver.Client(config.endpoint, {
authenticator,
traversalsource: 'g',
rejectUnauthorized: true,
mimeType: 'application/vnd.gremlin-v2.0+json',
})
}

After the Client has been crated, you can finally create your first vertex!

MyFunction/index.ts
let query = `g.addV(label).property('pk', partitionKey).property('name', name)`
await client.submit(query, {
label: 'person',
name: 'Zach',
partitionKey: 'pk',
})

As you can see, the syntax here is quite unfortunate. You have to craft this query as a string. Values with quotes around them get treated as strings, and values without quotes get interpreted as keys in the object that you pass as the second argument to client.submit().

Gremlin is not supposed to be written like that, but has to be when working with Cosmos DB’s Gremlin API because Cosmos does not support Gremlin’s nicer bytecode syntax.

In ConclusionSection titled: In Conclusion

In this post, we’ve seen that Microsoft doesn’t exactly make it easy to work with Cosmos DB’s Gremlin API inside of an Azure Function. It doesn’t have any bindings, and doesn’t support the backend features necessary to make it easy to work with the gremlin NPM package either.

As I hinted at earlier, I’ve created a wrapper around the gremlin NPM package to make it easier to work with Cosmos’ Gremlin API; it’s called gremlin-cosmos.