---
title: "Node.js Cookbook"
description: "Explore our comprehensive Node.js Cookbook to master connecting, querying, and managing nodes with Cloud CMS. Learn asynchronous chaining and more. (Gitana 4.0)"
product: "Gitana"
version: "4.0"
url: "/documentation/gitana/4.0/developers/cookbooks/nodejs"
---

# Node.js Cookbook

## Getting Started

To get started with the Node.js driver, please visit the [Cloud CMS Node.js Driver Page](https://gitana.io/documentation/gitana/4.0/developers/drivers/javascript.html "Cloud CMS Node.js Driver Page").

One thing to keep in mind is that the Node.js driver is based on the JavaScript driver. As such, they're pretty similar. That said, the Node.js driver can do a few important tricks that you can't do in the JavaScript driver.

## Connecting to Cloud CMS

To connect, create a file called `gitana.json` in your application root. For information on how to acquire this file, please read up on [Acquiring your API Keys](/documentation/gitana/4.0/developers/apikeys/overview.md).

You can then simply do:

```
Gitana.connect(function(err) {
  
    var platform = this;
    
});
```

Alternatively, you can pass in your API keys as an argument to the `connect()` method.

```
Gitana.connect({
    "clientKey": "{clientKey}",
    "clientSecret": "{clientSecret}",
    "username": "{username}",
    "password": "{password}",
    "baseURL": "https://api.cloudcms.com"
}, function(err) {
  
    var platform = this;
    
});
```

If a problem was encountered during the authentication, the `err` variable will be populated. If not, the `this` variable will be the `Platform` instance you can use to start doing interesting things!

If you supply an `application` argument in your API Keys, you'll get back a helper object that you can use to access all of the resources on your application's associated Stack.

```
Gitana.connect({
    "clientKey": "{clientKey}",
    "clientSecret": "{clientSecret}",
    "username": "{username}",
    "password": "{password}",
    "baseURL": "https://api.cloudcms.com"
}, function(err) {
  
    var helper = this;
    
    var repository = helper.datastore("content");    
    
});
```

## Asynchronous Chaining

The driver is unique and unusual in that it offers a Chaining based solution to making asynchronous series of API calls. It's similar to Promises (but pre-dates them!) and makes it possible to make a series of API calls without having to write as much code.

For example, with this driver, you can write the following:

```
platform.readRepository("1234567890abcdef1234").readBranch("master").then(function() {
    this.createNode({"title": "Article #1"});
    this.createNode({"title": "Article #2"});
    this.createNode({"title": "Article #3"});
});
```

This might look similar to Promises but it has a few differences. One important difference is that the `then()` method modifies the `this` to be the result. This allows you to chain calls as shown above, or even like this:

```
platform.readRepository("1234567890abcdef1234").readBranch("master").createNode({"title": "Article #1"});
```

Which is effectively the same, in concept, as a callback approach where you might do something like:

```
readRepository(platform, function(err, repository) {

    if (err) {
        return handleError(err);
    }
    
    readBranch(repository, "master", function(err, branch) {
    
        if (err) {
            return handleError(err);
        }
    
        createNode(branch, {"title": "Article #1"}, function(err, node) {
        
            if (err) {
                return handleError(err);
            }
            
            // yay I made it!
        
        });
    })'
});
```

Not so with our driver. You can keep your code lean and clean. You just need to be mindful of the changes in scope (the `this` variable). Which is unique. But there it is.

## Code Samples

Here are some code samples to help you get started.

In these examples, we'll assume that you're connecting to Cloud CMS using API keys that either provide the `application` value or they do not. The examples below show an essential skeleton for a JS application that uses the driver for both cases.

In all of the examples that follow, assume that your code gets inserted into the `TODO` section.

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

For cases where the repository and branch must be explicitly referenced, we'll assume that the repository ID is `1234567890abcdef1234` and the branch ID is `master`.

### API Keys with `application`

```
Gitana.connect(function(err) {

    if (err) {
        return console.log("Failed to connect", err);
    }

    var helper = this;
    
    var repository = helper.datastore("content");
        
    repository.readBranch("master").then(function() {
        var branch = this;
        
        // TODO: add your custom code here
    });    
    
});
```

### Standard API Keys (without `application`)

Use the `readRepository` method to fetch a specific repository. The user you've logged in as must have `READ` permissions to the repository. If they do not have read rights, a null response is expected.

Use the `readBranch` method to fetch a specific branch. The user you've logged in as must have `READ` permissions to the branch. If they do not have read rights, a null response is expected.

```
Gitana.connect(function(err) {

    if (err) {
        return console.log("Failed to connect", err);
    }

    var platform = this;
        
    platform.readRepository("1234567890abcdef1234").then(function() {
        var repository = this;
        
        this.readBranch("master").then(function() {
            var branch = this;
            
            // TODO: add your custom code here
        });    
    });
    
});
```

## Node Creation

### Create a Node

Create a basic node of type `n:node` (the default type).

```
var branch = this;

branch.createNode({
    "title": "My First Node"
}).then(function() {

    // NOTE: this = node
    
    // add your custom code here
});            
```

### Create a Node for a Custom Content Type

Suppose we have a custom content type called `custom:article` that looks like this:

```
{
    "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:

```
var branch = this;

branch.createNode({
    "_type": "custom:article",
    "title": "Article #1",
    "body": "A still more glorious dawn awaits",
    "categories": ["category1", "category2", "category3"],
    "rating": 5            
}).then(function() {

    // NOTE: this = node
    
    // add your custom code here
});            
```

### Create a Folder

In Cloud CMS, folders are just nodes with the `f:container` feature on them.

```
var branch = this;

branch.createNode({
    "title": "My Folder",
    "_features": {
        "f:container": {}
    }
}).then(function() {

    // NOTE: this = node
    
    // add your custom code here
});            
```

If you want your folder to appear in the general folder hierarchy, you must link it to a parent folder that is already a member of the folder hierarchy. The main folder hierarchy descends from the branch root node.

Here is the long way of doing this:

```
var branch = this;

branch.rootNode().then(function() {

    var rootNode = this;

    Chain(branch).createNode({
       "title": "My Folder",
       "_features": {
           "f:container": {}
       }
    }).then(function() {
        var folder = this;
        
        Chain(rootNode).associate(folder, {
            "_type": "a:child"
        }).then(function() {
            // done!
        });
    });
    
});   
```

An easier way to do this is to use the following special properties when the node is created:

- `_parentFolderPath` - the path to the parent folder where the created node should be linked. The folder must exist. If it does not exist, the operation will fail.
- `_fileName` - the `f:filename` to be assigned to the node once it is created.

Alternatively, you can use:

- `_filePath` - an absolute path where the file should be created. This allows you to specify the parent folder path and the file name at the same time.

You can also adjust the association type (between the discovered parent folder and your newly created item).

- `_associationTypeString` - the QName for the newly created association. The default is `a:child`.

Thus, you could do:

```
var branch = this;

branch.createNode({
   "title": "My Folder",
   "_features": {
       "f:container": {}
   },
   "_parentFolderPath": "/",
   "_fileName": "my_folder"
}).then(function() {
    var folder = this;        
});
```

Or you could do:

```
var branch = this;

branch.createNode({
   "title": "My Folder",
   "_features": {
       "f:container": {}
   },
   "_parentFolderPath": "/",
   "_filePath": "/my_folder"
}).then(function() {
    var folder = this;        
});
```

## Associations

In Cloud CMS, 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 `a:linked` association that points from the first article (`1234567890abcdef1111`) to the second article (`1234567890abcdef2222`), like this:

```
var branch = this;

branch.associate("1234567890abcdef1111", "1234567890abcdef2222", "a:linked').then(function() {
    var association = this;
});
```

Or we can do this using the nodes themselves:

```
var branch = this;

branch.readNode("1234567890abcdef1111").then(function() {
    var article1 = this;
    
    Chain(branch).readNode("1234567890abcdef2222").then(function() {
        var article2 = this;
        
        Chain(article1).associate(article2, "a:linked").then(function() {        
            var association = this;
        });    
    });
});   
```

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

```
article1.associations().each(function() {
    console.log("Found association: " + this._doc);
});
```

Or find only the `INCOMING` associations:

```
article1.associations({
    "direction": "incoming"
}).each(function() {
    console.log("Found incoming association: " + this._doc);
});
```

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

```
article1.associations({
    "direction": "outgoing",
    "type": "a:linked"
}).each(function() {
    console.log("Found outgoing linked association: " + this._doc);
});
```

## Query for Nodes

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](/documentation/gitana/4.0/engine/query.md) within Cloud CMS.

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 articles that have a `category` with the value `category1`.

```
var branch = this;

branch.queryNodes({
    "_type": "custom:article",
    "category": "category1"
}).count(function(c) {
    console.log("The size was: " + c);
}).each(function() {
    console.log("Found a result, title: " + this.title + ", rating: " + this.rating);
}).then(function() {
    // done!
});
```

### 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.

```
var branch = this;

branch.queryNodes({
    "_type": "custom:article",
    "category": "category1"
}, {
    "skip": 2,
    "limit": 5    
}).each(function() {
    console.log("Found a result, title: " + this.title + ", rating: " + this.rating);
}).then(function() {
    console.log("The size was: " + this.size()); // 1
    console.log("The offset was: " + this.offset()); // 2
    console.log("The totalRows was: " + this.totalRows()); // 3
});
```

### Sorting

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

```
var branch = this;

branch.queryNodes({
    "_type": "custom:article",
    "category": "category1"
}, {
    "sort": {
        "rating": -1
    }   
}).each(function() {
    console.log("Found a result, title: " + this.title + ", rating: " + this.rating);
});
```

The results will look like:

```
Found a result, title: Article #3, rating: 3
Found a result, title: Article #2, rating: 2
Found a result, title: Article #1, rating: 1
```

### 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.

```
var branch = this;

branch.queryNodes({
    "_type": "custom:article",
    "rating": {
        "$gte": 2
    }
}).each(function() {
    console.log("Found a result, title: " + this.title + ", rating: " + this.rating);
});
```

## Search for Nodes

Let's now look at searching for content. This makes use of ElasticSearch's DSL to express some very powerful full text and structured searches. We recommend further reading on [Search within Cloud CMS](/documentation/gitana/4.0/engine/query.md).

We also suggest reading up on [Query Strings](/documentation/gitana/4.0/engine/search/query-string.md) 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!

```
var branch = this;

branch.searchNodes("star").each(function() {    
    console.log("Found a result, title: " + this.title + ", rating: " + this.rating);
});
```

The results will be:

```
Found a result, title: Article #2, body: harvesting star light
Found a result, title: Article #3, body: we are star stuff
```

## Node Attachments

You can store binary files onto Nodes as attachments.

Note: If you're working with the JavaScript driver in the browser, there are some limitations in terms of the kinds of payloads you can send to the Cloud CMS API. This is because the browser does not let you stream files from local disk. You are limited to the kinds of content that can be expressed within JavaScript itself (usually text, JSON and similar formats).

Attachments have the following methods:

- `getId()` returns the ID of the attachment
- `getLength()` returns the length of the attachment in bytes
- `getContentType()` returns the mimetype of the attachment
- `getFilename()` returns the optional filename of the attachment
- `getUri()` returns a datastore-relative path to the attachment
- `getDownloadUri()` returns a driver baseURI-relative path to the attachment
- `del()` deletes the attachment
- `download()` downloads the attachment and hands it back to you. The exact format depends on the mimetype. For text files, this will be a string. For JSON documents, this will be a JavaScript object literal. Otherwise, it will be a byte array.
- `stream()` streams back the attachment (only available in Node.js)

## Listing Attachments of a Node

To list the attachments of a Node, do the following:

```
var node = ...;

node.listAttachments().each(function() {
   console.log("The node: " + node._doc + " has an attachment: " + this.getId() + " (mimetype = " + this.getContentType() + ", length: " + this.getLength() + ")");
});
```

## Uploading an Attachment to a Node

To upload an attachment, do the following:

```
var node = ...;

node.attach("default", "text/plain", "Hello World", "helloworld.txt");
```

## Downloading an Attachment of a Node

To download an attachment:

```
var node = ...;

node.attachment("default").download(function(data) {
   console.log("Downloaded: " + data);
});
```

## Attachments as Streams

To upload an attachment using a stream:

```
const fs = require("fs");
var readStream = fs.createReadStream("./images/photo.png");

var node = ...;

node.attach("default", "image/png", readStream, "photo.png").then(function() {
   console.log("Attachment created");
});
```

To read a stream for an attachment, try this:

```
const fs = require("fs");

var node = ...;

node.attachment("default").stream(function(err, readStream) {
   var writeStream = fs.createWriteStream("./downloads/photo.png");
   readStream.pipe(writeStream);
   
   console.log("Attachment downloaded");
});
```

Note that stream support is only available when running inside of Node.js.
