The JavaScript ecosystem is wide and incorporates a large number of platforms and execution environments. To handle this, the Nimbu npm module contains special versions of the SDK tailored to use in Node.js
and React Native
environments. Not all features make sense in all environments, so using the appropriate package will ensure that items like local storage, user sessions, and HTTP requests use appropriate dependencies.
This documentation is work in progress. Some documentation is missing. For the most up-to-date answers to your questions you can consult the Nimbu JS SDK code on github.
To use the npm modules for a browser based application, include it as you normally would:
var Nimbu = require('nimbu-js-sdk');
For React Native applications, include nimbu-js-sdk/react-native
:
var Nimbu = require('nimbu-js-sdk/react-native');
To initialize the Nimbu SDK, call the initialize
method:
Nimbu.initialize("YOUR_ACCESS_TOKEN");
// optionally pass an API endpoint or Installation Id (for Analytics only)
Nimbu.initialize("YOUR_ACCESS_TOKEN", "https://api.on-premise.nimbu.cloud", "asdf23ede..234fls");
// The initialization is asynchronous. To wait upon completion, convert it to a promise:
async function() {
await Nimbu.initialize("YOUR_ACCESS_TOKEN").asPromise();
// continue with other code depending on Nimbu SDK
...
}
Our JavaScript SDK is originally based on the popular Backbone.js framework and inspired by Parse.
Accessing objects in Nimbu is based around Nimbu.Object
. Each Nimbu.Object
contains key-value pairs of JSON-compatible data. Keys must be alphanumeric strings. Values can be strings, numbers, booleans, or even arrays and dictionaries - anything that can be JSON-encoded.
Each Nimbu.Object
is an instance of a specific subclass with a class name that you can use to distinguish different sorts of data. For example, we could call the book object a Book.
To create a new subclass, use the Nimbu.Object.extend
method. Any Nimbu.Query
will return instances of the new class for any Nimbu.Object
with the same classname. If you’re familiar with Backbone.Model
, then you already know how to use Nimbu.Object
. It’s designed to be created and modified in the same ways.
The parameter given to extend
is corresponding to the slug of your data type in Nimbu. It can be a predefined data type, like "customers", "products", "orders" or "coupons". Or any slug from a Nimbu channel, i.e. "books".
// Simple syntax to create a new subclass of Nimbu.Object.
var Book = Nimbu.Object.extend("books");
// Create a new instance of that class.
var theBible = new Book();
// Alternatively, you can use the typical Backbone syntax.
var Book = Nimbu.Object.extend({
className: "books"
});
You can add additional methods and properties to your subclasses of Nimbu.Object
.
// A complex subclass of Nimbu.Object
var Book = Nimbu.Object.extend("books", {
// Instance methods
isHeavy: function () {
return this.get("nbPages") > 300;
},
// Instance properties go in an initialize method
initialize: function (attrs, options) {
this.cover = "hardcover"
}
}, {
// Class methods
setup: function(nbPages) {
var book = new Book();
book.set("nbPages", nbPages);
return book;
}
});
var theBible = Book.setup(700);
alert(theBible.get('nbPages')); // Displays 700.
alert(theBible.cover); // Displays hardcover.
If you’re already using ES6 in your codebase, the JavaScript SDK is compatible with ES6 classes. You can subclass Nimbu.Object with the extends keyword:
class Book extends Nimbu.Object {
// Initialization
constructor() {
// Pass the ClassName to the Nimbu.Object constructor
super('books');
// All other initialization
this.cover = "hardcover"
}
// Instance methods
isHeavy: function () {
return this.get("nbPages") > 300;
}
{
// Class methods
static setup(nbPages) {
var book = new Book();
book.set("nbPages", nbPages);
return book;
}
}
However, when using extends, the SDK is not automatically aware of your subclass. If you want objects returned from queries to use your subclass of Nimbu.Object
, you will need to register the subclass.
// After specifying the Book subclass...
Nimbu.Object.registerSubclass('Book', Book);
Let’s say you want to save the Book described above to the Nimbu channel books. The interface is similar to a Backbone.Model
, including the save method:
const Book = Nimbu.Object.extend("books");
const book = new Book();
book.set("nbPages", 700);
book.set("title", "The Glass Prophecy");
book.set("fiction", true);
book.save()
.then((book) => {
// Execute any logic that should take place after the object is saved.
alert('New object created with objectId: ' + book.id);
}, (error) => {
// Execute any logic that should take place if the save fails.
// error is a Nimbu.Error with an error code and message.
alert('Failed to create new object, with error code: ' + error.message);
});
There are also a few fields you don’t need to specify that are provided as a convenience. objectId
is a unique identifier for each saved object. createdAt
and updatedAt
represent the time that each object was created and last modified in the cloud. Each of these fields is filled in by Nimbu, so they don’t exist on a Nimbu.Object
until a save operation has completed.
If you prefer, you can set attributes directly in your call to save instead.
const Book = Nimbu.Object.extend("books");
const book = new Book();
book.save({
nbPages: 700,
title: "The Glass Prophecy"
fiction: true
})
.then((book) => {
// The object was saved successfully.
}, (error) => {
// The save failed.
// error is a Nimbu.Error with an error code and message.
});
Saving data to the cloud is nice, but it’s even better to get that data out again. If the Nimbu.Object
has been uploaded to the server, you can use the objectId
to get it using a Nimbu.Query
:
const Book = Nimbu.Object.extend("books");
const query = new Nimbu.Query(Book);
query.get("54b36df1676f6e2cc3ad9001")
.then((book) => {
// The object was retrieved successfully.
}, (error) => {
// The object was not retrieved successfully.
// error is a Nimbu.Error with an error code and message.
});
To get the values out of the Nimbu.Object
, use the get method.
var nbPages = book.get("nbPages");
var title = book.get("title");
var fiction = book.get("fiction");
The four special reserved values are provided as properties and cannot be retrieved using the ‘get’ method nor modified with the ‘set’ method:
var objectId = book.id;
var updatedAt = book.updatedAt;
var createdAt = book.createdAt;
var acl = book.getACL();
If you need to refresh an object you already have with the latest data that is in Nimbu, you can call the fetch method like so:
book.fetch().then((book) => {
// The object was refreshed successfully.
}, (error) => {
// The object was not refreshed successfully.
// error is a Nimbu.Error with an error code and message.
});
Updating an object is simple. Just set some new data on it and call the save method. For example:
const Book = Nimbu.Object.extend("books");
const book = new Book();
book.set("nbReaders", 0);
book.set("title", "The Glass Prophecy");
book.set("fiction", true);
book.save()
.then((book) => {
book.set("nbReaders", 1);
book.set("title", "The Phone Book")
book.set("fiction", false)
return book.save();
});
The Nimbu SDK automatically figures out which data has changed so only dirty fields will be sent back to Nimbu.
In the above example the “nbReaders” field is a counter that we’ll need to continually update whenever someone is reading the book. Using the above method works but it’s cumbersome and can lead to problems if you have multiple clients trying to update the same counter.
To help with storing counter-type data, the Nimbu SDK provides methods that atomically increment (or decrement) any number field. So, the same update can be rewritten as:
book.increment("nbReaders");
book.save();
// You can also increment by any amount by passing in a second argument to increment. When no amount is specified, 1 is used by default.
book.increment("nbReaders", 2);
book.save();
To delete an object from a channel:
book.destroy().then((book) => {
// The object was deleted from the "books" channel
}, (error) => {
// The delete failed.
// error is a Nimbu.Error with an error code and message.
});
Objects may have relationships with other objects. For example, in a library application, a Book
object may have one Author
object and many Reader
objects. Nimbu supports these kind of relationships, including one-to-one or one-to-many.
To create a new Book
with a single Author
, you could write:
// Declare the types.
var Book = Nimbu.Object.extend("books");
var Author = Nimbu.Object.extend("authors");
// Create the book
var myBook = new Book();
myBook.set("title", "The Glass Epiphany");
myBook.set("content", "What can we write here?");
// Create the author
var myAuthor = new Author();
myAuthor.set("name", "Joe Foxy");
// Add the author as a value in the book
myBook.set("author", myAuthor);
// This will save both myAuthor and myBook
myAuthor.save().then(function() {
myBook.save()
});
Many-to-many relationships are modeled using Nimbu.Relation
. This works similar to storing an array of Nimbu.Object
s in a key, except that you don’t need to fetch all of the objects in a relation at once.
In addition, this allows Nimbu.Relation
to scale to many more objects than the array of Nimbu.Object
approach. For example, a User
may have many Posts
that she might like. In this case, you can store the set of Posts
that a User
likes using relation. In order to add a Post
to the “likes” list of the User
, you can do:
var user = Nimbu.Customer.current();
var relation = user.relation("likes");
relation.add(post);
user.save();
You can remove a post from a Nimbu.Relation:
relation.remove(post);
user.save();
You can call add and remove multiple times before calling save:
relation.remove(post1);
relation.remove(post2);
user.save();
You can also pass in an array of Nimbu.Object to add and remove:
relation.add([post1, post2, post3]);
user.save();
By default, the list of objects in this relation are not downloaded. You can get a list of the posts that a user likes by using the Nimbu.Query
returned by query:
relation.query().find({
success: function(list) {
// list contains the posts that the current user likes.
}
});
If you want only a subset of the Posts, you can add extra constraints to the Nimbu.Query
returned by query like this:
var query = relation.query();
query.equalTo("title", "The Glass Epiphany");
query.find({
success:function(list) {
// list contains post liked by the current user which have the title "The Glass Epiphany".
}
});
If you want to list all references (only their id's and classNames), you can use the list()
method on a relation:
var user = Nimbu.Customer.current();
var relation = user.relation("likes");
relation.list();
// ^ Will return [{"id": "617a96898bf7472bd2c57e56", "className": "like"}, ...]
In many cases, get isn’t powerful enough to specify which objects you want to retrieve. Nimbu.Query
offers different ways to retrieve a list of objects rather than just a single object.
The general pattern is to create a Nimbu.Query
, put conditions on it, and then retrieve an Array of matching Nimbu.Objects using find. For example, to retrieve the books that have a particular authorName, use the equalTo method to constrain the value for a key.
const Book = Nimbu.Object.extend("books");
const query = new Nimbu.Query(Book);
query.equalTo("authorName", "Joe Foxy");
const results = await query.find();
alert("Successfully retrieved " + results.length + " scores.");
// Do something with the returned Nimbu.Object values
for (let i = 0; i < results.length; i++) {
var object = results[i];
alert(object.id + ' - ' + object.get('authorName'));
}
There are several ways to put constraints on the objects found by a Nimbu.Query
. You can filter out objects with a particular key-value pair with notEqualTo
:
query.notEqualTo("authorName", "Joe Roxy");
You can give multiple constraints, and objects will only be in the results if they match all of the constraints. In other words, it’s like an AND of constraints.
query.notEqualTo("authorName", "Joe Roxy");
query.greaterThan("nbReaders", 2);
You can limit the number of results by setting limit. By default, results are limited to 25.
query.limit(10); // limit to at most 10 results
If you want exactly one result, a more convenient alternative may be to use first instead of using find.
const Book = Book.Object.extend("books");
const query = new Nimbu.Query(Book);
query.equalTo("authorName", "Joe Foxy");
const object = await query.first();
You can skip the first results by setting skip
:
query.skip(10); // skip the first 10 results
For sortable types like numbers and strings, you can control the order in which results are returned:
// Sorts the results in ascending order by the nbReaders field
query.ascending("nbReaders");
// Sorts the results in descending order by the nbReaders field
query.descending("nbReaders");
For sortable types, you can also use comparisons in queries:
// Restricts to nbReaders < 10
query.lessThan("nbReaders", 10);
// Restricts to nbReaders <= 10
query.lessThanOrEqualTo("nbReaders", 10);
// Restricts to nbReaders > 3
query.greaterThan("nbReaders", 3);
// Restricts to wins >= 50
query.greaterThanOrEqualTo("nbReaders", 3);
If you want to retrieve objects matching any of the values in a list of values, you can use containedIn
, providing an array of acceptable values. This is often useful to replace multiple queries with a single query. For example, if you want to retrieve book written by any author in a particular list:
query.containedIn("authorName",
["Joe Foxy", "John Doe", "Santa Claus"]);
If you want to retrieve objects that do not match any of several values you can use notContainedIn, providing an array of acceptable values.
query.notContainedIn("authorName",
["Joe Foxy", "John Doe", "Santa Claus"]);
If you want to retrieve objects that have a particular key set, you can use exists. Conversely, if you want to retrieve objects without a particular key set, you can use doesNotExist.
// Finds objects that have the nbPages set
query.exists("nbPages");
// Finds objects that don't have the nbPages set
query.doesNotExist("nbPages");
You can restrict the fields returned by calling select with a list of keys. To retrieve documents that contain only the authorName and nbPages fields (and also special built-in fields such as objectId, createdAt, and updatedAt):
var Book = Nimbu.Object.extend("books");
var query = new Nimbu.Query(Book);
query.select("authorName", "nbPages");
query.find().then(function(results) {
// each of results will only have the selected fields available.
});
The remaining fields can be fetched later by calling fetch on the returned objects:
query.first().then(function(result) {
// only the selected fields of the object will now be available here.
return result.fetch();
}).then(function(result) {
// all fields of the object will now be available here.
});
Use startsWith to restrict to string values that start with a particular string. Similar to a MySQL LIKE operator, this is indexed so it is efficient for large datasets:
var query = new Nimbu.Query(Book);
query.startsWith("title", "The Big");
The above example will match any Book objects where the value in the “title” String key starts with “The Big”. For example, both “The Big Bang” and “The Big Party” will match, but “The big” or “Watch it: The Big Bang Theory” will not.
Similarly there is also the endsWith
operator.
Regular expression queries should be avoided due to performance considerations. Queries that have regular expression constraints are therefore very expensive, especially for channels with over 100,000 records. Consider restricting how many such operations can be run on a particular app at any given time.
query.matches("authorName", "Joe", “i”);
There are several ways to issue queries for relational data. If you want to retrieve objects where a field matches a particular Nimbu.Object
, you can use equalTo just like for other data types. For example, if each Book has a Author object in its author field, you can fetch books for a particular Author:
// Assume Nimbu.Object myAuthor was previously created.
var query = new Nimbu.Query(Book);
query.equalTo("author", myAuthor);
// comments now contains the books for myAuthor
const books = await query.find().asPromise();
In some situations, you want to return multiple types of related objects in one query. You can do this with the include method. For example, let’s say you are retrieving the last ten books, and you want to retrieve their related author objects at the same time:
var query = new Nimbu.Query(Book);
// Retrieve the most recent ones
query.descending("createdAt");
// Only retrieve the last ten
query.limit(10);
// Include the post data with each comment
query.include("author");
// Books now contains the last ten books, and the "author" field with all fata
const books = await query.find();
// has been populated. For example:
for (var i = 0; i < books.length; i++) {
// This does not require a new API fetch.
var author = books[i].get("author");
var name = author.get("name")
}
You can also do multi level includes using dot notation. If you wanted to include the author for a book and the author’s publisher as well you can do:
query.include(["author.publisher"]);
If you just need to count how many objects match a query, but you do not need to retrieve all the objects that match, you can use count instead of find. For example, to count how many books have been written by a particular author:
var query = new Nimbu.Query("books");
query.equalTo("authorName", "Joe Roxy");
const count = await query.count();
alert("Joe Roxy has written " + count + " books");
For more complex queries you might need compound queries. A compound query is a logical combination (e. g. “and” or “or”) of sub queries.
If you want to find objects that match one of several queries, you can use Nimbu.Query.or
method to construct a query that is an OR of the queries passed in. For instance if you want to find books who either have a lot of readers or no readers, you can do:
var lotsOfReaders = new Nimbu.Query("books");
lotsOfReaders.greaterThan("nbReaders", 25);
var noreaders = new Nimbu.Query("books");
fewWins.equalTo("nbReaders", 0);
var mainQuery = Nimbu.Query.or(lotsOfReaders, noreaders);
mainQuery.find()
.then(function(results) {
// results contains a list of players that either have a lot of readers or no readers.
})
.catch(function(error) {
// There was an error.
});
If you want to find objects that match all conditions, you normally would use just one query. You can add additional constraints to the newly created Nimbu.Query
that act as an ‘and’ operator.
var query = new Nimbu.Query("Author");
query.greaterThan("age", 45);
query.greaterThan("nbBooks", 15);
query.find()
.then(function(results) {
// results
})
.catch(function(error) {
// There was an error.
});
coming soon
coming soon
coming soon
coming soon
coming soon
coming soon