JavaScript 2.0 Cookbook

Getting Started

To get started with the JavaScript driver, please visit the Gitana JavaScript 2.0 Driver Page.

This JavaScript driver, in contrast to the Gitana JavaScript 1.0 Driver, fully supports ECMAScript promises, which makes it easier to seamlessly integrate with your javascript apps.

Connecting to Gitana

You can connect and then use this driver in three different but equivalent ways:

  1. Async / Await
  2. Promises
  3. Callbacks

Async / Await

const cloudcms = require("cloudcms");

const apiKeys = {
    "clientKey": "",
    "clientSecret": "",
    "username": "",
    "password": ""    
};

(async function() {

    var session = await cloudcms.connect(apiKeys);

    // code
        
})();

Promises

const cloudcms = require("cloudcms");

const apiKeys = {
    "clientKey": "",
    "clientSecret": "",
    "username": "",
    "password": ""    
};

cloudcms.connect(apiKeys).then(function(session) {

    // code
});

Callbacks

const cloudcms = require("cloudcms");

const apiKeys = {
    "clientKey": "",
    "clientSecret": "",
    "username": "",
    "password": ""    
};

cloudcms.connect(apiKeys, function(err, session) {

    // code
});

API Keys

You can either pass in your API Keys object to the connect() method or you can have the driver pick up the API keys from the following files in the local directory:

  • gitana.json
  • cloudcms.json

For example, the following code will simply read from disk:

const cloudcms = require("cloudcms");

(async function() {

    var session = await cloudcms.connect();
    
})();

Code Samples

Here are some code samples to help you get started. In these scenarios, we assume you're using the async/await pattern, but remember that promises and callacks will also work for all methods. We will also assume that the API keys are implicitly being loaded from a local gitana.json file.

In Gitana, you can have as many repositories as you'd like. And each repository can multiple branches (similar to Git). The first thing you should do is connect and get the branch that you want to work on.

For the purpose of most of these examples, we'll assume the repository ID is 1234567890abcdef1234 and the branch is master.

Node Creation

Create a Node

{
    "title": "Custom Article",
    "_qname": "custom:article",
    "type": "object",
    "properties": {
        "title": {
            "type": "string"
        },
        "body": {
            "type": "string"
        },
        "categories": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "rating": {
            "type": "number"
        }
    }
}

Create a content instance of this type like this:

const cloudcms = require("cloudcms");

var repositoryId = "1234567890abcdef1234";
var branchId = "master";

(async function() {

    var session = await cloudcms.connect();
        
    // create node
    var obj = {
        "_type": "custom:article",
        "title": "Article #1",
        "body": "A still more glorious dawn awaits",
        "categories": [
            "category1",
            "category2",
            "category3"
        ],
        "rating": 5
    };
    var node = await session.createNode(repository, branchId, obj);
});

Create a Folder

In Gitana, folders are just nodes with the f:container feature on them. It must be associated to the root node to be considered part of file folder tree.

const cloudcms = require("cloudcms");

var repositoryId = "1234567890abcdef1234";
var branchId = "master";

(async function() {

    var session = await cloudcms.connect();
        
    var obj = {
        "title": "My Folder"
    };

    var options = {
        "parentPath": "/"
    };

    var node = await session.createNode(repository, branchId, obj, options);
});

Associations

In Gitana, any two nodes can be connected via an association. You can use an out-of-the-box association type, such as a:linked or a:owned or you can create your own.

Let's imagine that we have two articles that look like this:

  • Article #1
{
    "_doc": "1234567890abcdef1111",
    "_type": "custom:article",
    "title": "Article #1",
    "body": "a mote of dust suspended in a sunbeam",
    "categories": ["category1", "category2"],
    "rating": 1
}
  • Article #2
{
    "_doc": "1234567890abcdef2222",
    "_doc": "",
    "_type": "custom:article",
    "title": "Article #2",
    "body": "harvesting star light",
    "categories": ["category2", "category3"],
    "rating": 2
}

Let's create an association that points from the first article (1234567890abcdef1111) to the second article (1234567890abcdef2222), like this:

const cloudcms = require("cloudcms");

var repositoryId = "1234567890abcdef1234";
var branchId = "master";

(async function() {

    var session = await cloudcms.connect();

    var article1 = await session.readNode(repositoryId, branchId, "1234567890abcdef1111");
    var article2 = await session.readNode(repositoryId, branchId, "1234567890abcdef2222");

    var association = await session.associate(repositoryId, branchId, node1, node2, "a:linked", "OUTGOING");
});

You can also find all associations around article1. This will include associations that are INCOMING, OUTGOING and MUTUAL.

var associations = await session.listNodeAssociations(repositoryId, branchId, nodeId);

Or find only the INCOMING associations:

var associations = await session.listNodeAssociations(repositoryId, branchId, nodeId, null, "INCOMING");

Or find only the OUTGOING associations of type a:linked:

var associations = await session.listNodeAssociations(repositoryId, branchId, nodeId, "a:linked", "OUTGOING");

Query

Let's now look at querying for content. This makes use of MongoDB's query language to express some pretty powerful queries. We recommend further reading on Query within Gitana.

Let's assume that we have the following:

  • Article #1
{
    "_type": "custom:article",
    "title": "Article #1",
    "body": "a mote of dust suspended in a sunbeam",
    "categories": ["category1", "category2"],
    "rating": 1
}
  • Article #2
{
    "_type": "custom:article",
    "title": "Article #2",
    "body": "harvesting star light",
    "categories": ["category2", "category3"],
    "rating": 2
}
  • Article 3
{
    "_type": "custom:article",
    "title": "Article #3",
    "body": "we are star stuff",
    "categories": ["category3", "category1"],
    "rating": 3
}

Basic Querying

Find all of the nodes that have a category with the value category1.

const cloudcms = require("cloudcms");

var repositoryId = "1234567890abcdef1234";
var branchId = "master";

(async function() {

    var session = await cloudcms.connect();
        
    var query = {
        "_type": "custom:article",
        "category": "category1"
    };

    var nodes = await session.queryNodes(repositoryId, branchId, query);
    for (var j = 0; j < nodes.rows.length; j++)
    {
        var node = nodes.rows[j];

        console.log("Repository: " + repository._doc + ", Branch: " + branchId + ", Node: " + node._doc);
    }
});

Pagination

If you have a lot of potential query hits, you'll want to paginate. Here is an example where we return results starting at index 2 and hand back at most 5 results.

const cloudcms = require("cloudcms");

var repositoryId = "1234567890abcdef1234";
var branchId = "master";

(async function() {

    var session = await cloudcms.connect();
        
    var query = {
        "_type": "custom:article",
        "category": "category1"
    };

    var pagination = {
        "skip": 2,
        "limit": 5
    };

    var nodes = await session.queryNodes(repositoryId, branchId, query, pagination);
    for (var j = 0; j < nodes.rows.length; j++)
    {
        var node = nodes.rows[j];

        console.log("Repository: " + repository._doc + ", Branch: " + branchId + ", Node: " + node._doc);
    }
});

Sorting

Use the pagination option to sort the results as you see fit. Here we sort descending on rating.

const cloudcms = require("cloudcms");

var repositoryId = "1234567890abcdef1234";
var branchId = "master";

(async function() {

    var session = await cloudcms.connect();
        
    var query = {
        "_type": "custom:article",
        "category": "category1"
    };

    var pagination = {
        "sort": {
            "rating": -1
        }
    };

    var nodes = await session.queryNodes(repositoryId, branchId, query, pagination);
    for (var j = 0; j < nodes.rows.length; j++)
    {
        var node = nodes.rows[j];

        console.log("Repository: " + repository._doc + ", Branch: " + branchId + ", Node: " + node._doc);
    }
});

Query for values in a range

Find all of the articles where the rating is greater than or equal to 2.
This demonstrates the power of MongoDB's query language.

const cloudcms = require("cloudcms");

var repositoryId = "1234567890abcdef1234";
var branchId = "master";

(async function() {

    var session = await cloudcms.connect();
        
    var query = {
        "_type": "custom:article",
        "rating": {
            "$gte": 2
        }
    };

    var nodes = await session.queryNodes(repositoryId, branchId, query);
    for (var j = 0; j < nodes.rows.length; j++)
    {
        var node = nodes.rows[j];

        console.log("Repository: " + repository._doc + ", Branch: " + branchId + ", Node: " + node._doc);
    }
});

Search for Nodes

Let's now look at searching for content. This makes use of Elastic Search's DSL to express some very powerful full text and structured searches. We recommend further reading on Search within Gitana.

We also suggest reading up on Query Strings to understand how you can write searches textually (in addition to structuring them as JSON objects):

Let's assume that we have the following:

  • Article #1
{
    "_type": "custom:article",
    "title": "Article #1",
    "body": "a mote of dust suspended in a sunbeam",
    "categories": ["category1", "category2"],
    "rating": 1
}
  • Article #2
{
    "_type": "custom:article",
    "title": "Article #2",
    "body": "harvesting star light",
    "categories": ["category2", "category3"],
    "rating": 2
}
  • Article 3
{
    "_type": "custom:article",
    "title": "Article #3",
    "body": "we are star stuff",
    "categories": ["category3", "category1"],
    "rating": 3
}

Full Text Search

Find all of the nodes with the word star. Simple enough!

const cloudcms = require("cloudcms");

var repositoryId = "1234567890abcdef1234";
var branchId = "master";

(async function() {

    var session = await cloudcms.connect();
    var nodes = await session.searchNodes("star");
    
    for (var j = 0; j < nodes.rows.length; j++)
    {
        var node = nodes.rows[j];

        console.log("Repository: " + repository._doc + ", Branch: " + branchId + ", Node: " + node._doc);
    }
});

GraphQL

You can make queries for specific fields or content in related items by using GraphQL. Here we do a simple query for just the title and body of our article type:

let query = `query {
    n_nodes {
        title
    }
}`;

let results = await session.graphqlQuery(repository, branch, query);

Custom Session

You can supply your own session implementations to add your own methods.

Define your session class:

var DefaultSession = require("cloudcms/session/default/session");

class CustomSession extends DefaultSession
{
    /**
     * Creates an article.
     *
     * @param repository
     * @param branch
     * @param obj
     */
    createArticle(repository, branch, obj)
    {
        var callback = this.extractOptionalCallback(arguments);
    
        if (!obj) {
            obj = {};
        }
        
        obj._type = "my:article";
        
        // call through to the createNode method on the default session
        return this.createNode(repository, branch, obj, callback);
    }
}

module.exports = CustomSession;

This extends the session object with a new method called createArticle.

And then do the following to use it:

const cloudcms = require("cloudcms");

(async function() {

    var customSession = require("custom-session");
    cloudcms.session(customSession);

    var session = await cloudcms.connect();
    
    var article = await session.createArticle(repository, branch, { "title": "Hello World" });

})();

If you want to add a new asynchronous method that adhere to the session's async support for callbacks, Promises and/or await/async, you can use the Helper.sessionFunction method like this:

var DefaultSession = require("cloudcms/session/default/session");
var Helper = require("cloudcms/helper");

class CustomSession extends DefaultSession
{
    test()
    {
        // use the Helper.sessionFunction method to support Promise, callback or async/await
        // put your work into the finish method
        return Helper.sessionFunction.call(this, arguments, function(finish) {
            return setTimeout(function() {
                finish(null, 101);
            }, 250);
        });
    }
}

module.exports = CustomSession;