Business Logic Reference

Reference

The Business Logic reference is split into two sections:

Kinvey Requests

There are two types of business logic requests: collection hooks and custom endpoints. Collection hooks allow server-side processing pre and post data collection requests, while custom endpoints are independent on-demand requests.

A request to the Kinvey service goes through several standardized steps, similar to a pipeline.

Collection Hook Pipeline

For collection hooks, the pipeline is:

  1. Requst from client to Kinvey backend
  2. Authentication of client (verify that client has access to this backend)
  3. Pre-processing hooks
  4. Database actions
  5. Post-processing hooks
  6. Clean-up and response formatting
  7. Response is returned to the requesting client

All requests to and from Kinvey execute steps 1, 2, 6 and 7. The other steps are optional. For example, if the response.complete() method is called during pre-processing (step 3), then processing jumps to steps 6 and 7, completing immediately.

If there is an error in step 4 (Database actions) any post-procssing hooks will not be executed. This can happen, for example, if a requested entity is not found in the database. If your logic requires a hook to be performed in all cases, then it must be a pre-processing hook.

You can customize the behavior of this pipeline with the following functions:

Function (Hook)When Called
onPreFetch(request, response, modules)(Step 3) Called before a query or load (HTTP verb GET).
onPreSave(request, response, modules)(Step 3) Called before a save or update (HTTP verb POST or PUT).
onPreInsert(request, response, modules)(Step 3) Called before an insert (HTTP verb POST).
onPreUpdate(request, response, modules)(Step 3) Called before an update (HTTP verb PUT).
onPreDelete(request, response, modules)(Step 3) Called before a delete (HTTP verb DELETE).
onPreLogin(request, response, modules)(Step 3) Called before a request to /login endpoint (HTTP verb POST).
onPostFetch(request, response, modules)(Step 5) Called after a query or load (HTTP verb GET).
onPostSave(request, response, modules)(Step 5) Called after a save or update (HTTP verb POST or PUT).
onPostInsert(request, response, modules)(Step 5) Called after an insert (HTTP verb POST).
onPostUpdate(request, response, modules)(Step 5) Called after an update (HTTP verb PUT).
onPostDelete(request, response, modules)(Step 5) Called after a delete (HTTP verb DELETE).
onLoginSuccess(request, response, modules)(Step 5) Called after a successful user login (HTTP verb POST).
onLoginFailure(request, response, modules)(Step 5) Called after a failed user login (HTTP verb POST).

These can be modified by clicking on the pre- or post-request buttons on the console, and then selecting the action you want to have Business logic.

Custom Endpont Pipeline

Custom endpoints have a more abbreviated request pipeline:

  1. Requst from client to Kinvey backend
  2. Authentication of client (verify that client has access to this backend)
  3. Custom Endpoint processing
  4. Clean-up and response formatting
  5. Response is returned to the requesting client

All custom endpoints use the onRequest function for server-side logic:

Function (Hook)When Called
onRequest(request, response, modules)(Step 3) Called upon a HTTP verb POST.

Parameters

The parameters to all Business Logic hooks are:

NameValue
requestThe request that Kinvey received from a client
responseThe response that Kinvey will send to a client
modulesVarious JavaScript libraries available for use in your business logic

Request

The request object has the following fields/values:

FieldValue
methodThe HTTP method of the request, GET, PUT, POST or DELETE
headersAll headers that were set on the originating request
bodyAn object representing the parsed body of the original request
paramsA list of all parameters in the URL query string
username DEPRECATED The username of the requesting user - use requestContext instead
entityIdThe requested entity, will be undefined if no entity present in request
collectionNameThe name of the collection that's executing this logic
appKeyThe app key for the current Kinvey backend

Response

The response object has the following fields/values:

FieldValue
headersThe headers that will be set on the outgoing response
bodyThe current body of the outgoing response
completeA function that indicates the business logic is complete and that no further processing of the request should happen
continueA function that indicates the business logic is complete and that normal request processing should continue
errorA function that indicates the business logic has encountered an error

The three response functions (complete, continue, and error) should be called following a return statement. For example:

return response.complete(201);

The response object is available during pre-process business logic, however it may not be fully populated, since normal processing is not yet complete. Make sure to verify any required structure in JavaScript objects.

Only response.complete and response.error are available for custom endpoints. response.continue is invalid in the custom endpoint context and will throw an exception if used.

response.body can be set during pre-process business logic, however Kinvey may change the contents of response.body during further processing.

Modules

The following modules are available for use in your JavaScript:

Logger

Use the logger module to log a message that you can view in the console. Logging can be filtered and traced to a specific request.

Available Methods:

MethodAction
info(message, callback)Log message as an informational message
warn(message, callback)Log message as a warning
error(message, callback)Log message as an error
fatal(message, callback)Log message as a fatal error

For example:

var logger = modules.logger;
logger.info('Started BL processing', function(err, result) {
    if (!request.body.name) {
      logger.fatal('Request did not include a name...');
      response.complete(400);
    }
}
 );

...

The logging level (info, warning, error, fatal) is just used in logging and will not affect the request in any way.

Logging calls are asynchronous in nature. They can be invoked without a callback, but are not guaranteed to complete before continuing to the next statement. However, once invoked they will complete the writing to the log, even if the business logic is terminated via response.complete, response.continue, or response.error.

Collection Access

Use the collection access module to access your data in MongoDB. collectionAccess provides an asynchronous interface to MongoDB collections.

Available Methods:

MethodActionOptions Supported
objectID(string)Convert string to an internal Kinvey ObjectID representationN/A
collection(collectionName)Return the internal collection object for the collection collectionNameN/A
collectionExists(callback)Checks to see if the current collection existsN/A
find(query, options, callback)Find entities matching query, always returns an arraylimit, sort, fields, skip
findOne(query, options, callback)Find an entity matching query, always returns a single objectsort, fields, skip
insert(doc, callback)Insert the document doc or an array of documents into the collection.None
remove(query, callback)Remove all docs matching query from the collectionNone
save(doc, callback)Insert or Update doc in collectionNone
distinct(key, query, callback)Return all distinct values for the given keysNone
update(query, doc, options, callback)Updates doc in collectionupsert, multi, sort
count(query, callback)Return the number of items matching the queryNone
findAndModify(query, sort, doc, options, callback)Find just one entity matching the query and modify it with doc. If query matches multiple entities, use sort to determine which single entity to modify.remove, upsert, new
findAndRemove(query, sort, callback)Find just one entity matching the query and delete it. If query matches multiple entities, use sort to determine which single entity to delete.None
mapReduce(map, reduce, options, callback)Perform a mapReduce jobssort, limit
group(keys, condition, initial, reduce, finalize, command, callback)Group entities by keysNone
geoNear(x, y, options, callback)Find entities near x (longitude) and y (latitude)query, num, maxDistance, distanceMultiplier, spherical, includeLocs

You can find complete documentation for these methods and the correct parameters for the arguments and options on at the MongoDB documentation Collection() website. Kinvey's collectionAccess implementation supports a subset of these options as specified in the table above.

All collectionAccess methods are asynchronous, so actions that need data from a collectionAccess method need to be performed in the callback to the method (or in the promise fulfillment function, if using promises).

Below you can see examples of the find(), findAndModify() and update() methods:

find()
// Ignore the incoming request and return all items tagged with an age greater than 10
modules.collectionAccess.collection('myCollection').find({"age": {"$gt": 10}}, function (err, docs) {
  if (err) {
    logger.error('Query failed: '+ err);
  } else {
    response.body = docs;
    response.complete(200);
  }
});
findAndModify()
// Find the user with the highest score on Easy difficulty and update his "firstPlace" to true
var myCollection = modules.collectionAccess.collection('myCollection');
myCollection.findAndModify({"difficulty": "Easy"}, [["score", -1]], { $set: { "firstPlace": "true" }}, function (err, docs) {
  if (err) {
    logger.error('Query failed: '+ err);
  } else {
    response.body = docs;
    response.complete(200);
  }
});
update()
// Find all documents where the "age" has value of 18 or higher and set their "isAdult" to "true"
var myCollection = modules.collectionAccess.collection('myCollection');
myCollection.update({"age": {"$gt": 17}}, { $set: { "isAdult": "true" }}, { "multi": true }, function (err, status) {
  if (err) {
    logger.error('Query failed: '+ err);
  } else {
    response.complete(200);
  }
});

The collection access module gives you complete control over your data in MongoDB. This gives you the power to manipulate your data however you want, however it comes at a price. You also have the possibility of a rogue query on a remove removing all of your data. Additionally, Kinvey stores metadata and ACL information within your data. This data is not guaranteed to stay the same or constant, and modifying this data may result in your data becoming unavailable or accidentally releasing your data to everyone.

Regular expressions need to be anchored (prefixed with ^), and case sensitive. To do case insensitive search, create a normalized (i.e. all lowercase) field in your collection and perform the match on that field.

ObjectID warning

When an entity is created without explicitly specifying an _id attribute, that entity's _id will not be a regular JavaScript string; instead, it will be represented as a Kinvey ObjectID, which is a special internal object type. When searching for an entity with an _id attribute that is stored as an ObjectID, you can use the collectionAccess.objectID helper function to convert your string into the special type.

Since the Kinvey REST API returns JSON, and JSON only supports basic types, any entity you fetch from Kinvey through the REST API or a client library will appear to have a String _id field, regardless of whether or not that entity's _id is stored as a String or as an ObjectID. The Kinvey REST API automatically checks whether the _id field of an incoming request is an ObjectID, and acts accordingly. However, when using collectionAccess through business logic, you interact directly with your raw data, and thus you must handle any type casting yourself.

Example using String _id
// To find an entity that has a String _id field, we must use the same to query for it
modules.collectionAccess.collection('myCollection').findOne({ _id: myIncomingStringId }, function(err, result) {
    ...
});
Example using ObjectId _id
// To find an entity that has an ObjectID _id field, we must use the same to query for it
modules.collectionAccess.collection('myCollection').findOne({ _id: modules.collectionAccess.objectID(myIncomingStringId) }, function(err, result) {
    ...
});
Promises API

All asynchronous collectionAccess methods have sibling methods that return Promise objects. (For more info on promises, see the Bluebird section below). The method signatures are the same as the callback-style methods, except "Async" is appended to the method name and there is no callback argument passed. Methods returning promises are:

MethodActionOptions Supported
collectionExistsAsync()Checks to see if the current collection exists. Returns a Promise.N/A
findAsync(query, options)Find entities matching query, always returns an array. Returns a Promise.limit, sort, fields, skip
findOneAsync(query, options)Find an entity matching query, always returns a single object. Returns a Promise.sort, fields, skip
insertAsync(doc)Insert the document doc or an array of documents into the collection. Returns a Promise.None
removeAsync(query)Remove all docs matching query from the collection. Returns a Promise.None
saveAsync(doc)Insert or Update doc in collection. Returns a Promise.None
distinctAsync(key, query)Return all distinct values for the given keys. Returns a Promise.None
updateAsync(query, doc, options)Updates doc in collection. Returns a Promise.upsert, multi, sort
countAsync(query)Return the number of items matching the query. Returns a Promise.None
findAndModifyAsync(query, sort, doc, options)Find just one entity matching the query and modify it with doc. If query matches multiple entities, use sort to determine which single entity to modify. Returns a Promise.remove, upsert, new
findAndRemoveAsync(query, sort)Find just one entity matching the query and delete it. If query matches multiple entities, use sort to determine which single entity to delete. Returns a Promise.None
mapReduceAsync(map, reduce, options)Perform a mapReduce jobs. Returns a Promise.sort, limit
groupAsync(keys, condition, initial, reduce, finalize, command)Group entities by keys. Returns a Promise.None
geoNearAsync(x, y, options)Find entities near x (longitude) and y (latitude). Returns a Promise.query, num, maxDistance, distanceMultiplier, spherical, includeLocs

Here's an example similar to the one above, using findAsync() to return a Promise:

// Ignore the incoming request and return all items tagged with an age
// greater than 10.
var findData = modules.collectionAccess.collection('myCollection').findAsync({"age": {"$gt": 10}});

findData.then(function (docs) {
    response.body = docs;
    response.complete(200);
},
function(err){
    logger.error('Query failed: '+ err);
});

Push

Use the push module to send a push notification to a device registered with your app.

Available Methods:

MethodAction
broadcastMessage(message, callback)Broadcasts a simple text-only message to all devices registered with this app
sendMessage(users, message, callback)Sends a simple text-only message to the users in users
send(users, message, callback)Alias for sendMessage()
sendPayload(users, iOSAps, iOSExtras, androidPayload, callback)Sends a custom payload to the users in users which allows for sending advanced options and data
broadcastPayload(iOSAps, iOSExtras, androidPayload, callback)Broadcasts custom payloads to all devices registered with this app which allows for sending advanced options and data

For example, to send a broadcast message:

    var push = modules.push;
    if (request.body.sendMessageToAll){
        push.broadcastMessage(request.body.message, function(err, result) {
           response.continue();
         });
    }

The users argument to send is either an array of Kinvey User documents or a single user document. The message will be sent to all iOS or Android devices that are contained in the documents in users. You can query the user collection with the collectionAccess module to get a list of users.

For example:

    var push = modules.push, collectionAccess = modules.collectionAccess;
    collectionAccess.collection('user').find({"firstName": request.body.firstName}, function (err, userColl) {
      if (err) {
        logger.error('Query failed: '+ err);
      } else {
        userColl.forEach(function (user) {
          logger.info('Pushing message to ' + user);
          push.sendMessage(user, "People who are named " + user.firstName + " are awesome!", function(err, result) {
          });
        });
      }
    });
Payloads

The methods sendPayload and broadcastPayload allow for more robust push messages. For iOS, the payload consists of two arguments, iOSAps and iOSExtras. iOSAps is a JSON object that can have the following properties:

FieldValue
alertAn alert message to display to the user
badgeThe badge number to appear on the notification
soundThe name of the sound file in the application package to play
content-availableIf set to 1, the remote notification acts as a "silent" notification

iOSExtras is a JSON object with custom name-value pairs to be included in the push notification.

For Android, the payload is contained in the androidPayload attribute, which is any JSON object to be sent to Google Cloud Messaging (GCM).

Each of the arguments above are null-safe.

For example, to send a broadcast with payloads:

var iOSAps = { alert: "You have a new message", badge: 2, sound: "notification.wav" }
var iOSExtras = {from: "Kinvey", subject: "Welcome to Business Logic"}
var androidPayload = {message: "You have a new Message", from: "Kinvey", subject: "Welcome to BL" }
var push = modules.push;
push.broadcastPayload(iOSAps, iOSExtras, androidPayload, callback);

Push calls are asynchronous in nature. They can be invoked without a callback, but are not guaranteed to complete before continuing to the next statement. However, once invoked they will complete the sending of the push messages, even if the business logic is terminated via response.complete, response.continue, or response.error.

Email

Use the email module to send email to any valid email address.

Available Methods:

MethodAction
send(from, to, subject, text_body, reply_to, html_body, cc, bcc, callback)Send an email to to from from using the subject and body. reply_to is an optional argument for the reply to line of the message. For the email body, text_body is for the plain-text version of the email and is required. html_body is an optional parameter that accepts an HTML-formatted string, which creates an optional html body for the email message. cc and bcc are optional arguments for adding cc and bcc users.

For example, to send a text email:

    var email = modules.email;
    email.send('my-app@my-app.com',
               request.body.friendEmail,
               'Join my octagon in my-app!',
               "You've been invited to join " + request.body.name + "'s octagon in my-app!", function(err, result) {
               response.continue();
               });

To send an HTML email, it is important to note that the reply-to must be included; if you want the reply-to to be the same as the from address, you can either also pass the from address in the reply_to argument, or simply pass a null value (which will default the reply to to the from address). For example:""

    var email = modules.email;
    email.send('my-app@my-app.com',
           request.body.friendEmail,
           'Join my octacgon in my-app!',
           "You've been invited to join " + request.body.name + "'s octagon in my-app!",
           null,
           '<html>You've been invited to join  <b>'+request.body.first_name+'</b>'s octagon in my-app!</html>', callback);

Email calls are asynchronous in nature. They can be invoked without a callback, but are not guaranteed to complete before continuing to the next statement. However, once invoked they will complete the sending of the email, even if the business logic is terminated via response.complete, response.continue, or response.error.

Validation

Use the validation module to validate input on a request.

Available Methods:

MethodAction
doValidation(spec, doc)Validate doc using spec

For example:

var validation = modules.validation
  , result, spec, doc;


spec = {
    someRequiredField: {required: true},
    age: {min: '0', max: '200'},
    someTextField: {pattern: ['a regular expression']}
};

doc = request.body;

result = validation.doValidation(spec, doc);

if(result.length > 0) {
  response.body.debug = result;
  response.complete(400);
} else {
  response.continue();
}

The spec syntax is an object where the properties are fields that are expected to be present in the doc argument to doValidation, and the values are validation operators. If an property is missing, doValidation returns a string describing the failure. Extra properties are allowed to be present in doc. A property is allowed to have multiple operators

Allowed validation operators are:

OperatorAccepted values
requiredAccepts the argument true, if you don't want a property to be required, remove the required operator
minAccepts a number argument. doc is rejected if the property's value is numerically less than the argument
maxAccepts a number argument. doc is rejected if the property's value is numerically greater than the argument
patternAccepts a regular expression as an argument. The regular expression must not have the leading and trailing / character. The doc is rejected if the pattern fails to match the property

Utils

Use the utils module for various utility helper functions.

Available Objects:

MethodAction
renderTemplate(spec, data)Renders the template using the mustache template engine with specification spec using data
kinveyEntity(options)DEPRECATED (Please see the Kinvey Entity section of the Kinvey module for working with Kinvey Entities) Returns a JSON object with Kinvey metadata for use in AppData collections.
tempObjectStoreKey-value store for persisting temporary data between a pre- and post-hook.
base64Encode and decode strings to/from base64
convertConvert between Kinvey data formats
renderTemplate

Used to render templates using the mustache template engine.

For example:

var render = modules.utils.renderTemplate
  , spec
  , output;

spec = 'Hello there {{data.name}} happy {{data.age}}th birthday!';
output = render(spec, {data: request.body});
response.body = {"message": output};
response.complete(200);

The render method can take either a plain text or HTML-formatted string as a value for spec.

A complete guide on how to use mustache can be found in the Mustache Manual.

tempObjectStore

A key-value store for persisting temporary data between a pre- and post-hook. tempObjectStore is an ephemeral store that only lives for the duration of a single request, but will allow for data that is written in a pre-hook to be read in a post-hook. The object implements three methods:

MethodBehavior
set(key, value)Writes a value for the given key to the tempObjectStore
get(key)Returns the value of the supplied key
getAll()Returns all key/values in the tempObjectStore as an object

For example, to store an item in an onPreSave hook:

function onPreSave(request, response, modules) {
  var tempObjectStore = modules.utils.tempObjectStore;
  tempObjectStore.set('token', request.body.token);
  response.continue();
}

Then, to retrieve it:

function onPostSave(request, response, modules) {
  var tempObjectStore = modules.utils.tempObjectStore;
  var token = tempObjectStore.get('token');
  response.body = { "message": "Object for token " + token + " was saved successfully."};
  response.complete();
}

tempObjectStore is meant for storing small, temporary amounts of data to be passed between pre- and post- hooks. Large amounts of data / large objects should not be stored in the tempObjectStore, as there is a limit on the size of the payload that passes through Kinvey Business Logic.

base64

A utility for encoding utf8 strings to base64, and decoding base64 strings to utf8.

MethodBehavior
encode(str)Encodes a string to base64
decode(str)Decodes a base64 string to utf8

For example, to encode a string to base64:

function onPreSave(request, response, modules) {
  var encoded = modules.utils.base64.encode(request.body.username);
  request.body.username = encoded;
  response.continue();
}

To decode a string from base64:

function onPreSave(request, response, modules) {
  var decoded = modules.utils.base64.decode(request.body.username);
  request.body.username = decoded;
  response.continue();
}

String arguments to the encode function are limited to a size of 16 kb, and string arguments to the decode function are limited to a size of 21 kb. If a string is too large, the base64 functions will return null.

Convert

A utility for converting data.

MethodBehavior
toKinveyDateString(date)Converts a Date object, a moment, or a date string into the Kinvey date format. If the passed in date is not a valid date, "Invalid date" will be returned.
fromKinveyDateString(str, format)Decodes a Kinvey date string into the specified format. Valid formats are 'date', 'moment' and 'string'. If no format is provided, a JavaScript Date object is returned. If the passed in str is not a valid date, "Invalid date" will be returned.

For example, to convert a date to to a Kinvey Date string:

myDate = new Date();
kinveyDate = modules.utils.convert.toKinveyDateString(myDate);

To convert a Kinvey Date string to a JavaScript date object:

myDate = modules.utils.convert.fromKinveyDateString(kinveyDate, 'date');
// note:  this could also be accomplished via modules.utils.convert.fromKinveyDateString(kinveyDate);

To convert a Kinvey Date string to a moment:

myMoment = modules.utils.convert.fromKinveyDateString(kinveyDate, 'moment');

To convert a Kinvey Date into a standard ISO-8601 date string

myDateString = modules.utils.convert.fromKinveyDateString(kinveyDate, 'string');

Request

Available Methods:

MethodAction
request(options, callback)Make an arbitrary HTTP request
get(options, callback)request with method set to GET
put(options, callback)request with method set to PUT
post(options, callback)request with method set to POST
del(options, callback)request with method set to DELETE

callback is a function taking 3 arguments, error, response and body:

  • error -- Set to null if no error, otherwise set to an error object with the failure
  • response -- The full response from the server (containing headers, body and status)
  • body -- The returned HTTP body from the server.

options is an object that may contain the following properties:

PropertyDescriptionIs Required?
uriThe HTTP endpoint to requestYes
qsAn object containing querystring values to append to the uri (NB: Query params may be directly placed in the URI)No
methodThe HTTP method to use (May be one of "HEAD", "PUT", "POST", "GET" or "DELETE")No
headersAn object containing the HTTP headers to setNo
bodyThe entity body for POST or PUT, must be string or bufferNo
formUsed to send 'Content-type: application/x-www-form-urlencoded; charset=utf-8' data to a formNo
jsonWhen set to an object sends JSON encoded object to server, also JSON.decodes responseNo
followRedirectFollow GET HTTP 3XX responses, defaults to trueNo
followAllRedirectsFollow all HTTP method 3XX responses, defaults to falseNo
maxRedirectsMaximum number of times to follow a 3XX redirect, defaults to 10No
encodingSpecify the string encoding for the responseNo
timeoutSpecifies the timeout for the request, must be less than 1500msNo
oauthOptions for OAUTH HMAC-SHA1 signingNo
strictSSLSet to true to force SSL cert validation defaults to falseNo
awsObject containing AWS signing information (needs key, secret and possibly bucketNo

For example:

var req = modules.request;
req.get({uri: 'http://api.example-api.com/v1/abcdefg',
             method: 'GET'},
            function(error, res, body){

            if (error){
                response.body = {error: error.message};
                response.complete(434);
            } else {
                response.body = JSON.parse(body);
                response.complete(res.status);
            }
});

For information on using this module with OAuth, see OAuth Signing.

The body value passed to your callback will be encoded differently based on the data returned by the API you are requesting data from. All Kinvey libraries expect the data returned via response.body to be application/json, so you should convert the response from request to a JavaScript object prior to assigning it to response.body. If you've used the json property in the options object, then this step has already been completed. Otherwise, for JSON data you need to call JSON.parse(body).

This module is called "request" and one of the parameters for the callback is called "response", these should not be confused with the request and response parameters to your Business Logic function.

Security (SSL)

The request module supports SSL encryption. To enable SSL encryption, simply specify a uri starting with https://. The module will use SSL if required. By default the request module does not validate SSL certificates, which decreases the security of https requests. To force SSL verification set strictSSL to true in your options. This is recommended for the best security, but may limit your ability to access some servers.

The request module does not verify SSL certificates by default. This can lead to a man-in-the-middle attack vulnerability. If you would like maximum security, then set strictSSL to true in your options.

Promises API

All request methods have sibling methods that return Promise objects. (For more documentation, see the Bluebird section below). The method signatures are the same as the callback-style methods, except "Async" is appended to the method name and there is no callback argument passed. Methods returning promises are:

MethodAction
requestAsync(options)Make an arbitrary HTTP request. Returns a Promise object.
getAsync(options)request with method set to GET. Returns a Promise object.
putAsync(options)request with method set to PUT. Returns a Promise object.
postAsync(options)request with method set to POST. Returns a Promise object.
delAsync(options)request with method set to DELETE. Returns a Promise object.

An example using the request module to return a Promise:

var req = modules.request;
var fetchData = req.getAsync({uri: 'http://api.example-api.com/v1/abcdefg'});

fetchData.then(
   function(res, body){
       response.body = JSON.parse(body);
       response.complete(res.status);
   },
   function(error){
       response.body = {error: error.message};
       response.complete(434);
   }
);

For more information on the request module, see Request -- Simplified HTTP request method.

Lodash

A fork of underscore.js, lodash is a JavaScript toolkit which provides cross-platform methods for manipulating objects, collections, and more in a performant fashion.

The following methods from v4.0.0 of lodash are supported in Kinvey Business Logic:

Array
chunkcompactconcatdifference
differenceBydifferenceWithdropdropRight
dropRightWhiledropWhilefillfindIndex
findLastIndexfirstflatMapflatten
flattenDeepfromPairsheadindexOf
initialintersectionintersectionByintersectionWith
joinlastlastIndexOfpull
pullAllpullAllBypullAtremove
slicesortedIndexsortedIndexBysortedIndexOf
sortedLastIndexsortedLastIndexBysortedLastIndexOfsortedUniq
sortedUniqBytailtaketakeRight
takeRightWhiletakeWhileunionunionBy
unionWithuniquniqByuniqWith
unzipunzipWithwithoutxor
xorByxorWithzipzipObject
zipWith.prototype.reverse
Collection
countByeacheachRightevery
filterfindfindLastforEach
forEachRightgroupByincludesinvokeMap
keyBymaporderBypartition
reducereduceRightrejectsample
sampleSizeshufflesizesome
sortBy
Date
now
Function
afterarybeforebind
bindKeycurrycurryRightdebounce
deferdelayflipmemoize
negateonceoverArgspartial
partialRightreargrestspread
throttleunarywrap
Lang
clonecloneDeepcloneDeepWithcloneWith
eqgtgteisArguments
isArrayisArrayLikeisArrayLikeObjectisBoolean
isDateisElementisEmptyisEqual
isEqualWithisErrorisFiniteisFunction
isIntegerisLengthisMatchisMatchWith
isNaNisNativeisNilisNull
isNumberisObjectisObjectLikeisPlainObject
isRegExpisSafeIntegerisStringisSymbol
isTypedArrayisUndefinedltlte
toArraytoIntegertoLengthtoNumber
toPlainObjecttoSafeIntegertoString
Math
addceilfloormax
maxBymeanminminBy
roundsubtractsumsumBy
Number
clampinRangerandom
Object
assignassignInassignInWithassignWith
atcreatedefaultsdefaultsDeep
extendextendWithfindKeyfindLastKey
forInforInRightforOwnforOwnRight
functionsfunctionsIngethas
hasIninvertinvokekeys
keysInmapKeysmapValuesmerge
mergeWithomitomitBypick
pickByresultsetsetWith
toPairstoPairsIntransformunset
valuesvaluesIn
Seq
_chaintapthru
flatMapprototype[Symbol.iterator]atchain
commitnextplantreverse
toJSONvaluevalueOf
String
camelCasecapitalizedeburrendsWith
escapeescapeRegExpkebabCaselowerCase
lowerFirstpadpadEndpadStart
parseIntrepeatreplacesnakeCase
splitstartCasestartsWithtemplate
toLowertoUppertrimtrimEnd
trimStarttruncateunescapeupperCase
upperFirstwords
Util
attemptbindAllcondconforms
constantflowflowRightidentity
iterateematchesmatchesPropertymethod
methodOfmixinnoConflictnoop
nthArgoveroverEveryoverSome
propertypropertyOfrangerangeRight
runInContexttimestoPathuniqueId
Methods
_.templateSettings.imports._
stringSize

The lodash module also includes the following properties:

Properties
VERSION
templateSettings
templateSettings.escape
templateSettings.evaluate
templateSettings.imports
templateSettings.interpolate
templateSettings.variable

For example:

function onPreFetch(request, response, modules){
  var _ = modules.lodash; // Get lodash reference
  var myArray = [1, 2, 3];

  var reversedArray = _.reverse(myArray); // [3, 2, 1]
}

For reference on the lodash API and each of the methods listed above, see the lodash documentation.

json2csv

json2csv is a module that converts JSON to CSV.

Signature: json2csv(options, callback)

Example usage:

function onPostFetch(request, response, modules) {
  var json2csv = modules.json2csv;
  var fields = ['name', 'price']; // CSV columns
  var bookResults = [
    {
      name: "Publication 1",
      price: 19.50
    },
    {
      name: "Publication 2",
      price: 4.00
    }
  ]; // These objects represent results from a Kinvey collection fetch

  bookJson = JSON.stringify(bookResults); // json2csv input be serialized JSON

  // Convert
  json2csv({
    data: bookResults,
    fields: fields
  }, function(err, csv) {
    if (err)
      response.complete(500);

    var returnResult = { csvStringData: csv };

    response.body=returnResult;
    response.complete(200);
  });
}

The contents of csvStringData would be:

name, price
"Publication 1", 19.50
"Publication 2", 4.00

Only properties in the fields array will be processed and included in CSV output.

You can also use dot notation to create CSV columns with nested JSON fields:

function onPostFetch(request, response, modules) {
  var json2csv = modules.json2csv;
  var fields = ['book.name', 'book.publisher', 'price', 'type']; // CSV columns
  var bookResults = [
    {
      book: {
        name: "Publication 1",
        publisher: "Acme Publishing, Inc"
      },
      price: 19.50,
      type: "paperback"
    },
    {
      book: {
        name: "Publication 2",
        publisher: "Acme Publishing, Inc"
      },
      price: 4.00,
      type: "paperback"
    }
  ]; // These objects represent results from a Kinvey collection fetch

  bookJson = JSON.stringify(bookResults); // json2csv input must be serialized JSON

  // Convert
  json2csv({
    data: bookResults,
    fields: fields
  }, function(err, csv) {
    if (err)
      response.complete(500);

    var returnResult = { csvStringData: csv };

    response.body=returnResult;
    response.complete(200);
  });
}

The contents of csvStringData would be:

book.name, book.publisher, price, type
"Publication 1", "Acme Publishing, Inc", 19.50, "paperback"
"Publication 2", "Acme Publishing, Inc", 4.00, "paperback"

For more examples and a complete options reference, see the json2csv Github page.

Moment

A date library for parsing, validating, manipulating, and formatting dates, using moment.js. A new moment can be initialized with no arguments (to get the current date and time) or with the following arguments:

  • date -- A string date or JavaScript Date()
  • format -- A moment.js format string
  • locale -- The locale of the moment

The following methods from v2.10.3 of moment.js are supported in Kinvey Business Logic:

Methods
addsubtractdates
datedaydays
daysInMonthdayOfYeardiff
durationhumanizeformat
fromNowfromget
setcalendarhour
hoursisBeforeisSame
isAfterisLeapYearisDST
isValidisMomentlang
minmaxmillisecond or milliseconds
minute or minutesmonth or monthssecond or seconds
week or weeksisoWeek or isoWeeksyear or years
startOfendOftoDate
toArraytoJSONtoISOString
utclocalunix
weekdayisoWeekdayweekYear
isoWeekYearzoneparseZone
isDSTShiftedisLocalisUtcOffset
isUTCisUtczoneAbbr
zoneName

In addition, the moment module supports moment duration objects with the following methods:

Methods
humanizemillisecondsasMillisecons
secondsasSecondsminutes
asMinuteshoursasHours
daysasDaysmonths
asMonthsyearsasYears
addsubtractas
get

To declare a new moment object, create a modules.moment, and call any of the supported methods on that object.

function onPreFetch(request, response, modules){
  var theMoment = modules.moment();

  theMoment.add('days', 7);

  response.body={"thisTimeNextWeek" : theMoment};
  response.complete(200);
}

To declare and use a duration object, create a modules.moment object and invoke the duration method, and then call any of the supported methods on the duration object.

function onPreFetch(request, response, modules){
  var duration = modules.moment().duration(24, 'hours');

  response.body={"duration" : duration.humanize()};
  response.complete(200);
}

For reference on the moment.js API and each of the methods listed above, see the moment.js documentation.

Moment Timezone

An extension of moment.js which adds extra methods for parsing and displaying dates in any time zone. The moment-timezone constructor takes the same parameters as moment but uses the final paremter as a time zone identifier.

var theMoment = moment.tz(1453234509, 'America/New_York').format() // 1970-01-17T14:40:34-05:00

In addition to all of the moment.js methods above, the following methods from v0.5.0 of moment-timezone.js are supported in Kinvey Business Logic:

Methods
formatzoneAbbrzoneName
setDefaultguessadd
linkloadzone
namesunpackunpackBase60
packpackBase60createLinks
filterYearsfilterLinkPack

The moment-timezone.js module also supports Zone object, which offers the following methods:

Methods
abbr
offset
parse

You can retrieve the currently-set timezone programatically through the name property in the zone namespace

To declare a new moment-timezone object:

function onPreFetch(request, response, modules){
  var theMoment = modules.momentTimezone.tz('America/New_York'); // Creates a new moment using current date/time and 'America/New_York' time zone

  theMoment.add('days', 7);

  response.body={"thisTimeNextWeekWithTimeZone" : theMoment.format()};
  response.complete(200);
}

To modify the time zone of an existing moment:

function onPreFetch(request, response, modules){
  var theMoment = modules.momentTimezone('2000-01-01').tz('America/New_York');

  response.body={"dateWithTimeZone" : theMoment.format()};
  response.complete(200);
}

To use the zone namespace:

function onPreFetch(request, response, modules){
  var offset = modules.momentTimezone.tz.zone('America/New_York').offset(1453236443); // 480
  var name = modules.momentTimezone.tz.zone('America/New_York').name; // America/New_York

  response.body={
    "tzOffset" : offset,
    "tzName" : name
  };
  response.complete(200);
}

For reference on the moment-timezone.js API and each of the methods listed above, see the moment-timezone.js documentation.

xml2json

xml2json is a utility module which converts XML to JSON and JSON to XML. The following methods from v0.9.0 of xml2json are supported in Kinvey Business Logic:

Methods
toJson(xml, options)Converts XML to JSON. If options param is null, xml2json uses its own defaults (see below)
toXml(json)Converts JSON to XML

Example usage:

function onPreFetch(request, response, modules){
  var parser = modules.xml2json;

  // xml to json conversion
  var xml = "<foo>bar</foo>";
  var jsonResult = parser.toJson(xml); // Returns stringified JSON

  response.body=JSON.parse(jsonResult); // { foo: bar }
  response.complete(200);
}

toJson options param defaults:

var options = {
    object: false,
    reversible: false,
    coerce: false,
    sanitize: true,
    trim: true,
    arrayNotation: false
};

For reference on the xml2json API and each of the methods listed above, see the xml2json Github page.

xmldoc

xmldoc is a module that allows parsing of XML documents.

function onPreFetch(request, response, modules){
  var xmldoc = modules.xmldoc;
  var document = new xmldoc.XmlDocument('<hello>world</hello>');
  ...
}
Properties
nameNode name. I.e. 'foo' for <foo>. Note: XML namespaces are ignored by the underlying parser. <foo:bar> returns 'foo:bar'
attrDictionary containing attribute properties. E.g. fooNode.attr.bar for <foo bar="...">
valString value of the node. E.g. 'bar' for <foo>bar</foo>
childrenan XmlElement array containing child nodes
firstChildElement's first child. null if non-existent
lastChildElement's last child. null if non-existent
lineElement's line in the original XML string
columnElement's column in the original XML string
positionElement's position in the original XML string
startTagPositionElement's start tag position in the original XML string

Empty XmlDocument and XmlElement properties return 'sensible' values, such as [] for children, {} for attr, and '' for val

Methods
eachChild(callback)Similar to forEach. Calls func(child, index, array) for each child node
childNamed(name)Returns first child node named name, or undefined
childrenNamed(name)Same as childNamed but returns all results in an array (or [] if no children found)
childWithAttribute(name, value)Returns first child with the given name attribute value. If value is omitted, returns first node with given attribute definition
descendantWithPath(path)Searches for a specific path with dot notation
valueWithPath(path)Similar to descendantWithPath but goes deeper and extracts the node value
toString([options])JavaScript toString override. Used for debugging purposes

XmlDocument and XmlElement objects contain the same properties and methods

Example usage:

function onPreFetch(request, response, modules){
  var xmldoc = modules.xmldoc;
  var document = new xmldoc.XmlDocument('<foo>bar</foo>');

  var returnResult = {};
  returnResult.name = document.name; // foo
  returnResult.val = document.val; // bar

  response.body=returnResult; // { name: 'foo', val: 'bar' }
  response.complete(200);
}

For reference on the xmldoc API and each of the methods and properties above, see the xmldoc Github page.

Async

Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript, using async.js. The following methods from v1.4.2 of async.js are supported in Kinvey Business Logic:

Methods
eachforEacheachSeries
forEachSeries eachLimitforEachLimit
mapmapSeriesmapLimit
filterselectfilterSeries
selectSeriesrejectrejectSeries
reduceinjectfoldl
rejectRightfoldrdetect
detectSeriessortBysome
anyeveryall
concatconcatSeriesseries
parallelparallelLimitwhilst
doWhilstuntildoUntil
queuecargotimes
timesSeriesnoConflictnextTick
setImmediateeachOfforEachOf
eachOfSeriesforEachOfSerieseachOfLimit
forEachOfLimitreduceRightfilterLimit
selectLimitrejectLimitsomeLimit
everyLimitdetectLimitauth
retrywaterfalliterator
applyduringdoDuring
priorityQueuelogdir
memoizeunmemoizetimesLimit
seqcomposeapplyEach
applyEachSeriesforeverensureAsync
constantasyncifywrapSync

For example:

function onPreFetch(request, response, modules){
  var async = modules.async;
  var body = response.body;
  var moment = modules.moment().format();
  var logger = modules.logger.error;
  var debug = modules.logger.info;
  var req = modules.request;

  var req = modules.request;
  var getDate = function(callback) {
    modules.request.get('http://date.jsontest.com', function(error, resp, body){
      if (error){
        callback(error);
      } else {
        callback(null, (JSON.parse(body)).date);
            }
    });
  };

  var getTime = function(callback) {
    req.get('http://time.jsontest.com', function(error, resp, body){
      if (error){
        callback(error);
      } else {
        callback(null, (JSON.parse(body)).time);
            }
    });
  };


  var callback = function(error, results) {
    if (error) {
      response.body = {error: error.message};
      response.complete(400);
      return;
    }
    response.body.date = results[0];
    response.body.time = results[1];
    response.complete(200);
    return;
  };

  async.parallel([getDate, getTime], callback);
}

For reference on the async.js API and each of the methods listed above, see the async.js Github page.

backendContext

Used to return data and perform functions against the current backend context.

MethodBehavior
getAppKey()Returns the current backend appKey
getAppSecret()Returns the current backend appSecret
getMasterSecret()Returns the current backend masterSecret
getSecurityContext()Returns the context of the current user - possible values are user for a user account, app for the app account with app secret, and master for the app account with master secret
getAuthenticatedUsername() DEPRECATED Returns the username of the user who submitted the request.

One common use case for these functions is for calling requests against the current backend to provide a method to obtain the correct authentication credentials. This allows for BL scripts to be migrated from backend to backend without code changes, such as in the case of seperate development and production backends.

function onRequest(request, response, modules){

  var context = modules.backendContext;
  var utils = modules.utils;

  var appKey = context.getAppKey();
  var masterSecret = context.getMasterSecret();

  var uri = 'https://' + request.headers.host + '/appdata/'+ appKey +'/myCollection/';
  var authString = "Basic " + utils.base64.encode(appKey + ":" + masterSecret);
  var requestOptions = {
    uri:uri,
    headers: {
      "Authorization":authString
    }
  };

  var auth = modules.request.get(requestOptions,function(error, res, body){
    if (error){
      response.error(error);
    } else {
      response.body = JSON.parse(body);
      response.complete(res.status);
    }
  });

}

requestContext

Used to return data and perform functions against the current request context.

MethodBehavior
getAuthenticatedUserId()Returns the ID of the user who submitted the request. When the app or master secret is used to authenticate, the application ID is returned.
getAuthenticatedUsername()Returns the username of the user who submitted the request.
getAuthoritativeUsername()Returns the authoritative username of the user who submitted the request. The authoritative username is determined based on the type of user. If the user logged in with a token obtained from Mobile Identity Connect, and a MIC Enterprise ID is defined, this method returns the value defined in that field from the enterprise identity source. If the user logged in in with a MIC token and no MIC enterprise id is defined, or a social login, the social provider's id is used. If the user logs in with a Kinvey native login, the user.username value is used.
Custom Request Properties

Kinvey Client libraries provide API to pass Custom Request Properties when communicating with Kinvey services. The requestContext module provides methods to read and manipulate those properties.

MethodBehavior
getCustomRequestProperty(propertyName)Takes string value propertyName and returns value of key propertyName from the Custom Request Properties object. Returns undefined if key propertyName does not exist.
setCustomRequestProperty(propertyName, propertyValue)Creates Custom Request Property propertyName using the provided string propertyName, and assigns propertyValue as its value. If propertyName doesn't exist, it will be created. propertyValue can be any valid JSON value.

The Custom Request Properties object will be available throughout the entire Kinvey Request pipeline. For example, you can add/modify a Custom Request Property in a preFetch hook, and those changes will be available in a postFetch hook on the same transaction.

Example Code
//Assume {officeLocation: 'Paris'} was provided as the Custom Request Properties object

function onPreFetch(request, response, modules){

  var requestContext = modules.requestContext;
  var officeLocation = requestContext.getCustomRequestProperty('officeLocation');

  if (officeLocation === 'Paris'){
        try {
           //Perform some 'preprocessing' logic for requests from Paris
           //...
           //Set didPreprocess to true in the Custom Request Properties
           requestContext.setCustomRequestProperty('didPreprocess', true);
        } catch (error) {
         //If preprocessing fails due to an error, set didPreprocess to false
         requestContext.setCustomRequestProperty('didPreprocess', false);
        }  
  }

  //Continue the execution
  response.continue();

}

The above code checks the Custom Request Properties for a property, officeLocation. If the officeLocation is Paris, then some custom pre-processing is done as part of the pre-fetch hook. If the pre-processing succeeds, we set didPreprocess to true in the Custom Request Properties. If an error is encountered during preprocessing, we set didPreprocess to false.

We could implement a post-fetch hook that looks for the didPreprocess flag on the Custom Request Properties object:

//Assume {officeLocation: 'Paris', didPreprocess: true} was provided as the Custom Request Properties object

function onPostFetch(request, response, modules){

  var requestContext = modules.requestContext;
  var officeLocation = requestContext.getCustomRequestProperty('officeLocation');
  var didPreprocess = requestContext.getCustomRequestProperty('didPreprocess');

  if (officeLocation === 'Paris'){
        if (didPreprocess === true) {
            //perform some post-processing specific to pre-process success
        } else {
            //optionally perform some other post-processing
        }
  }

  //Finish the execution
  response.complete(200);

}

For details on passing Custom Request Properties to Kinvey backend services, please see the API reference for the Kinvey Client Library of your choice.

Client App Version

All of the Kinvey client libraries provide API to pass a client-app-specific version string (for implementation details, see the Kinvey Dev Center reference documentation for the library of your choice). The requestContext module provides a clientAppVersion namespace, which includes API to retrieve details about the version of the client app making the request.

Kinvey recommends (but does not require) using version strings that conform to the pattern major.minor.patch, where all values are integers and minor and patch are optional. The majorVersion(), minorVersion() and patchVersion() methods defined below are convenience methods designed to work with version strings conforming to this pattern. If major, minor or patch contain any non-digit characters (i.e. 'v1' contains a non-digit character, 'v'), the corresponding convenience method will return NaN.

The requestContext.clientAppVersion namespace provides the following methods:

MethodBehavior
stringValue()Returns the client app version value as a String. Returns null if no client app version is passed in.
majorVersion()Returns the first dot-separated field of the client app version string (or the entire version string if there are no 'dot' characters) as a Number, or NaN if that field contains any non-digit characters. See A note on version numbering above for details on formatting version strings.
minorVersion()Returns the second dot-separated field of the client-app version string as a Number, or NaN if that field contains any non-digit characters. See A note on version numbering above for details on formatting version strings.
patchVersion()Returns the third dot-separated field of the client-app version string as a Number, or NaN if that field contains any non-digit characters. See A note on version numbering above for details on formatting version strings.
Example Code

Assuming a client app version string of "1.1.5":


function onRequest(request, response, modules){
  var context = modules.requestContext;

  var majorVersion = context.clientAppVersion.majorVersion(); //majorVersion will be 1
  var minorVersion = context.clientAppVersion.minorVersion(); //minorVersion will be 1
  var patchVersion = context.clientAppVersion.patchVersion(); //patchVersion will be 5

  if (majorVersion < 2) {                                //will be true
      //Perform some version 1 compatible logic
      if ((minorVersion >= 1) && (patchVersion === 5)) {    //Will be true
          //perform some logic for a specific patch release
      }
  } else if (majorVersion >= 2) {                        //Will be false
      //Perform some version 2+ compatible logic
  }


  //Finish the execution
  response.complete(200);

}

Assuming a client app version string of "1.0.1-beta":


function onRequest(request, response, modules){
  var context = modules.backendContext;

  var versionString = context.clientAppVersion.stringValue(); //versionString will be "1.0.1-beta"
  var majorVersion = context.clientAppVersion.majorVersion();   //majorVersion will be 1
  var patchVersion = context.clientAppVersion.patchVersion();   //patchVersion will be NaN

  if (majorVersion < 2) {                         //Will be true
      //Perform some version 1 compatible logic
      if (patchVersion > 1) {                        //Will be FALSE as patchVersion is NaN
          //Perform some patch-specific logic
      }
  }

  if (versionString.match("beta") !== null){    //Will be true
      //Perform some beta-specific logic
  }

  //Finish the execution
  response.complete(200);

}

Bluebird (Promises)

Promises functionality is provided through the Bluebird library. Promises present an alternative to callbacks for managing asynchronous program flow. They allow asynchronous activities to be 'chained' together rather than 'nested' as callback arguments to asynchronous functions. This can allow code that executes asynchronously to read like synchronous code.

For examples and further reading, see:

For specific information about the Bluebird library, refer to the following:

Promises in Kinvey-provided modules

The Kinvey-provided modules that expose async APIs also have "promisified" methods that will return promises instead of requiring callbacks as parameters. See the examples in the Collection Access and Request modules.

Kinvey

The Kinvey module provides APIs to access Kinvey-specfic data and features.

Kinvey Entity

A Kinvey Entity is a JSON object containing Kinvey-specific metadata for use with AppData collections. Kinvey Entities also contain methods to manipulate Kinvey metadata.

The Kinvey module provides the following Kinvey Entity methods:

MethodBehavior
entity()Returns a Kinvey Entity with system generated _id.
entity(id)Returns a Kinvey Entity with the supplied id as the _id property.
entity(JsonObject)Updates JsonObject with Kinvey-specific metadata and functions, and returns a Kinvey Entity as the result.
isKinveyEntity(someObject)Returns true if someObject is a Kinvey Entity returned from one of the entity() methods above.

For example, to create a Kinvey Entity with only Kinvey metadata:

function onPreFetch(request, response, modules){
  response.body=modules.kinvey.entity();
  response.complete(200);
}

The response would contain an entity containing no additional attributes, except for Kinvey metatada attributes:

{
  "_acl": {
    "creator": "kid_VT5YYv2KWJ"
  },
  "_kmd": {
    "lmt": "2013-05-08T13:48:36+00:00",
    "ect": "2013-05-08T13:48:36+00:00"
  },
  "_id": "518a57b4d79b4d4308000002"
}

To use your own _id, you can simply pass that ID as a String into the method:

function onPreFetch(request, response, modules){
  var myID = "000-22-2343";
  response.body=modules.kinvey.entity(myID);
  response.complete(200);
}
{
  "_acl": {
    "creator": "kid_VT5YYv2KWJ"
  },
  "_kmd": {
    "lmt": "2013-05-08T13:51:02+00:00",
    "ect": "2013-05-08T13:51:02+00:00"
  },
  "_id": "000-22-2343"
}

You can add Kinvey metatada to your own JavaScript object by passing that object into the function.

function onPreFetch(request, response, modules){
  function employee() {
    this.firstName = "John";
    this.lastName = "Doe";
    this.status = "Active";
    this.dob = "5/5/1985";
    this.doh = "1/12/2012";
  }

  response.body=modules.kinvey.entity(new employee());
  response.complete(200);
}

This example results in the Kinvey metadata being added to your employee object.

{
  "firstName": "John",
  "lastName": "Doe",
  "status": "Active",
  "dob": "5/5/1985",
  "doh": "1/12/2012",
  "_acl": {
    "creator": "kid_VT5YYv2KWJ"
  },
  "_kmd": {
    "ect": "2013-05-08T13:57:11+00:00",
    "lmt": "2013-05-08T13:57:11+00:00"
  },
  "_id": "518a59b7d79b4d4308000003"
}

Note that you can add properties directly to a Kinvey Entity and pass it directly into a collectionAccess method that takes an entity as an argument:

function onPreFetch(request, response, modules) {
    var entity = modules.kinvey.entity();
    entity.name = "Sachin";
    modules.collectionAccess.collection("People").save(entity, function(err){
        if (err !== null){
            //handle error
        }else{
            response.continue();
        }
    });
}

If you call entity(JsonObject) with an object that already contains Kinvey metadata, the original metadata will be maintained. Only lmt will be updated with the current date and time.

_acl Namespace

Kinvey Entities returned from entity() are also imbued with an _acl namespace containing methods for accessing that entity's user and group access privileges:

MethodBehavior
_acl.getCreator()Return entity creator user ID as a String
_acl.getReaders()Return Array of String containing user IDs with read access
_acl.getWriters()Return Array of String containing user IDs with write access
_acl.getReaderGroups()Return Array of String containing group IDs with read access
_acl.getWriterGroups()Return Array of String containing group IDs with write access
_acl.addReader(userId)Grant read access to userId, passed as String. Returns the _acl namespace for method chaining.
_acl.addWriter(userId)Grant write access to userId, passed as String. Returns the _acl namespace for method chaining.
_acl.addReaderGroup(groupId)Grant read access to groupId, passed as String. Returns the _acl namespace for method chaining.
_acl.addWriterGroup(groupId)Grant write access to groupId, passed as String. Returns the _acl namespace for method chaining.
_acl.removeReader(userId)Revoke read access for userId , passed as String. Returns the _acl namespace for method chaining. (userId will retain read access if part of a group with read access)
_acl.removeWriter(userId)Revoke write access for userId , passed as String. Returns the _acl namespace for method chaining. (userId will retain write access if part of a group with write access)
_acl.removeReaderGroup(groupId)Revoke group read access for groupId, passed as String. Returns the _acl namespace for method chaining.
_acl.removeWriterGroup(groupId)Revoke group write access for groupId, passed as String. Returns the _acl namespace for method chaining.
_acl.getGloballyReadable()Return bool indicating whether entity is readable by all users
_acl.getGloballyWritable()Return bool indicating whether entity is writable by all users
_acl.setGloballyReadable(gr)Set global readability to gr, passed as bool. Returns the _acl namespace for method chaining.
_acl.setGloballyWritable(gw)Set global writability to gw, passed as bool. Returns the _acl namespace for method chaining.
ACL method chaining

All _acl methods can be chained to the modules.kinvey.entity() by chaining the _acl namespace followed by the desired method. Furthermore the _acl modifier methods (those that return the _acl namespace) can be chained to each other. Since the _acl modifier methods return the _acl namespace, there's no need to restate the _acl namespace for the next method in the chain.

For example, the following line will create a Kinvey Entity, add a reader, add a writer, then return all readers. NOTE: Since getReaders() returns an array, no futher _acl methods can be chained to it.

modules.kinvey.entity(someObject)._acl.addReader(readUserId).addWriter(writeUserId).getReaders();
Example

The example below uses kinvey.entity()._acl along with the Request Context module to grant write access on the requested entity to the user making the request:

function onPreFetch(request, response, modules) {
    var currentUser = modules.requestContext.getAuthenticatedUserId();
    var collectionName = request.collectionName;
    var entityId = request.entityId;
    modules.collectionAccess.collection(collectionName).findOne({"_id":entityId}, {}, function(err, doc){
          if (err !== null){
            //handle error
          } else if (doc) {

            var entity = modules.kinvey.entity(doc)._acl.addWriter(currentUser);
            //Note we directly pass the Kinvey Entity to the save method.
            modules.collectionAccess.collection(collectionName).save(entity, function(err){
              response.continue();
            });
          } else {
                  // entity not found
                  response.error("Entity not found");
                }
    });
}

Twilio

The Twilio module provides support for integrating with Twilio for outbound SMS support along with communications functions. This library exposes official Node Twilio Library The following methods from v2.5.1 of node-twilio are supported in Kinvey Business Logic:

Methods
accountSidauthTokenhost
apiVersiontimeoutaccounts
listgetpost
createavailablePhoneNumbersoutgoingCallerIds
incomingPhoneNumbersmessagessms
applicationsconnectAppsauthorizedConnectApps
callsconferencesqueues
recordingstokenstranscriptions
notificationsusagesip
addressessendSmssendMms
sendMessagelistSmslistMessages
getSmsgetMessagemakeCall
listCallsgetCall

For example:

function onPreFetch(request, response, modules){
  //require the Twilio module and create a REST client
  var twilio = modules.twilio('ACCOUNT_SID', 'AUTH_TOKEN');

  //Send an SMS text message
  twilio.sendMessage({

    to:'+16175551242', // Any number Twilio can deliver to
    from: '+15085551234', // A number you bought from Twilio and can use for outbound communication
    body: 'Sending an SMS note.' // body of the SMS message

  }, function(err, responseData) {
    if (err) {
      return response.error(err);
    } else {
      modules.logger.info(responseData);
      response.body = {sentSMS: true}
      return response.complete(200);
    }
});
}

For reference on the twilio-node API and each of the methods listed above, see the twilio-node documentation.

Slack

The Slack module provides support for integrating with Slack via the Slack Web API. This library exposes the Slack.js library. The following methods from v7.5.6 of slack are supported in Kinvey Business Logic:

Methods
apiauthhost
channelschatdescribe
dndemojifiles
groupsimmpim
oauthpinsreactions
searchstarsteam
usergroupsusers

For example, to send a message to a channel or user:

function onPreFetch(request, response, modules){
  //require the Slack module
  var slack = modules.slack;

  //Send a chat message
    slack.chat.postMessage({
      token: 'someaccesstoken',
      channel: '@someuser',
      message: 'Can you attend a meeting at 9:00 AM?'
      }, function(err, data) {
        if (err) {
          return response.error(err);
        }
       response.body = data;
       response.complete();
   });

}

For reference on the slack API and each of the methods listed above, see the slack.js documentation.

HipChat

The HipChat module provides support for integrating with HipChat. This library exposes the hipchat-client library. The hipchat client exposes access to rooms and users APIs.

The following methods from the rooms object of v1.0.5 of hipchat-client are supported in Kinvey Business Logic:

Methods
createdeletehistory
listmessagetopic
showgetByNamegetByXmppJid

The following methods from the users object of v1.0.5 of hipchat-client are supported in Kinvey Business Logic:

Methods
createdeletelist
showundeleteupdate
getByMentionNamegetByNamegetByEmail
getByStatus

For example, to send a message to a channel:

function onPreFetch(request, response, modules){
  //require the HipChat module
  var HipChatClient = modules.hipchat;

  var hipchat = new HipChatClient('auth_token');

  hipchat.api.rooms.message({
    room_id: 'Frontend',
    from: 'HipChat Client',
    message: 'Hello world!'
  }, function (err, res) {
     if (err) {
       return response.error(err); }
     }
     response.body = res;
     response.complete();
});
}

For reference on the slack API and each of the methods listed above, see the hipchat-client documentation.

Facebook Messenger

The Facebook Messenger module provides support for integrating with the Facebook Messenger Send API. This library exposes the facebook-send-api library. The following methods from v1.0.3 of facebook-send-api are supported in Kinvey Business Logic:

Methods
sendToFBsendMessageToFBsendGenericMessage
sendButtonMessagesendToFBsendMessageToFB
sendGenericMessagesendButtonMessagesendTextMessage
sendQuickRepliessendSenderActionsendTypingIndicators
sendReadReceipt

For example, to send a message to a channel or user:

function onPreFetch(request, response, modules){
  //require the Facebook Messenger module
  var token = 'xyz';
  var fb = modules.facebookMessenger;
  var FBPlatform = new fb(token);

  FBPlatform.sendTextMessage('someSenderId', 'hello');
  response.complete();
}

For reference on the slack API and each of the methods listed above, see the Facebook Send API documentation.

IBM Watson

The Watson module provides support for integrating with the IBM Watson APIs. This library exposes the watson-developer-cloud library. The following methods from v1.12.2 of watson-developer-cloud are supported in Kinvey Business Logic:

Methods
visual_recognitionspeech_to_texttext_to_speech
concept_insightstradeoff_analyticspersonality_insights
natural_language_classifierauthorizationlanguage_translator
tone_analyzerdialogretrieve_and_rank
document_conversionsearchalchemy_language
alchemy_visionalchemy_data_newsconversation
relationship_extraction

For example, to use the conversation service to determine the intent of a message:

function onPreFetch(request, response, modules){
  //require the watson module
  var watson = modules.watson;

  var conversation = watson.conversation({
    username: '<username>',
     password: '<password>',
     version: 'v1',
     version_date: '2016-07-01'
  });

  conversation.message({
  input: 'What\'s the weather?',
  workspace_id: '<workspace id>'
  }, function(err, res) {
     if (err) {
       response.error(err);
     } else {
       response.body = res;
       response.complete();
     }
});
}

For reference on the slack API and each of the methods listed above, see the watson-developer-cloud documentation.