Overview

Backendless Custom Business Logic is a system designed to allow extensibility and customization of an app backend with custom server-side code. Using Business Logic developers can override and extend the built-in Backendless services or deploy custom API services. The system supports the following types of custom business logic:

API Services

Hosted Services
These services are created based on the deployed Java and JavaScript/Node.js code. For each service Backendless automatically generates REST routes and native SDKs.
Overview
Quick Start Guide

 

Imported Services
These services are created from an external definition such as a WSDL or RAML document or AWS Lambda Functions.

API Event Handlers

This is the code overriding or extending behavior of the built-in Backendless services (User, Data, Geo, etc services).
Overview
Quick Start Guide

Timers

Java or Javascript/Node.js code which runs according to a schedule.
Overview

The diagram below illustrates all business logic components within Backendless. The green blocks represent custom server-side code:

server-code-diagram

API Services

Overview

Backendless supports two types of API services: hosted and imported. The hosted API services are created from the code deployed to Backendless. The imported API services are created from a service description such as WSDL and RAML or they can be constructed to proxy to the Amazon Lambda functions.

 

What is a Service?

An API Service is a logical unit with one or more API endpoints. These endpoints are available in two formats:

via HTTP REST endpoint URI
via native SDK

 

Backendless automatically generates both types of endpoints for the deployed code. Access to an API Service is guarded by Backendless security, where individual service operations can be allowed or denied for application's users and roles. Additionally, Backendless gathers analytics for the service' API usage. The diagram below visually demonstrates these concepts:

What-is-API-Service

 

The workflow for developing and deploying a hosted service is very straight-forward:

1. Developer writes the service code;
2. Developer debugs and tests the service code using Backedless CodeRunner;
3. Developer uses Backendless CodeRunner or Backendless Console to deploy the code;
4. Backendless processes the deployed code and creates an API Service;
5. Backendless automatically generates native client-side SDKs as well as REST API routes for the service operations.

 

Code deployed to Backendless automatically becomes an API service when it follows a set of simple rules documented below. For the code to be an API Service, it means it  is accessible through the standard networking mechanisms, such as generated REST API as well as native SDKs created by Backendless.

 

Developer must observe the following rules when creating service code:

Service class must contain at least one method. (Backendless generates API routes based on the methods declared in the class).
Service class must be registered with the following API:

Backendless.ServerCode.addService(SERVICE-CLASS);

 

Consider the following example:

class SampleService {
  /**
   * @param {string} zipCode
   */
  getWeather(zipCode) {
    // skipping implementation for brevity
  }

  /**
   * @return {number}
   */
  getServerTime() {
    // skipping implementation for brevity
  }
 
  /**
   * @param {string} to
   * @param {string} subject
   * @param {string} body
   */
  sendEmail( to, subject, body) {
    // skipping implementation for brevity
  }
}
 
Backendless.ServerCode.addService(SampleService);

Limitations

There are several considerations and limitations for the runtime behavior of the invoked methods:

The code cannot use native File IO, instead, it must be the Backendless File Service API to work with the application's file storage.
The execution time of a service method is limited for each Backendless Cloud pricing plan. Check the pricing table on the website for details. This limitation does not exist in the Backendless Pro and Managed Backendless versions of the service.
 

Quick Start Guide

This Quick Start guide will walk you through the process of creating a Backendless API service by editing the service code online using Backendless Console. By the time you finish the guide, you will have a working API service hosted in Backendless. You will be able to experience REST-based method invocations and download generated client SDKs.

 

1. Create a Backendless account if you do not have one. You can register for your free account at https://develop.backendless.com/registration
2. Login to Backendless Console and select your app.
3. Click the Business Logic icon to enter the server-side code management area.
4. Click the CODING menu item and select JS from the language dropdown list as shown below:
js-coding-biz-logic.zoom50
 
5. Right click the services node in the tree and select New file.
new-file-in-services
 
6. Enter a name for the JS file which will contain the service code. For example, MyService.js and click CREATE:
js-service-file-name
 
7. Backendless creates the file and opens it for editing. Paste the following code into the file editor:
class MyService {
  /**
   * @param {String} name
   */ 
  helloWorld( name ) {
   return "hello " + name;
 }
}

Backendless.ServerCode.addService( MyService );
8. The code above is a JavaScript class (MyService) with a method (helloWorld). Notice the @param annotation in the JS code comment. The annotation describes to Backendless the type(s) for the method argument(s). Click the Save and Deploy icon/button to deploy the code to Backendless.
save-and-deploy
 
9. Once the code is deployed, Backendless Console will inform you with a notification popup in the upper right corner:
notification-popup
 
10. Click API SERVICES to switch to the service browser screen. The MyService service will appear in the list with the first method selected. Enter a value for the name parameter and click INVOKE. The service method is invoked and console displays the result in the RESPONSE panel:
invoke-hello-world
 
11. Click CODE to see a cURL command to invoke the method with the entered values of the parameter. You can copy the entire text of the command and paste in a terminal window to run it outside of the browser:
curl-service
 
12. Additionally, Backendless generates various client SDKs for the API service. You can learn how to use the generated SDKs in the Client SDKs section.
download-native-sdk

Advanced Quick Start Guide

This Quick Start guide will walk you through the process of creating a Backendless API service by deploying and running JavaScript code in Backendless API Engine. By the time you finish the guide, you will have a working API service hosted in Backendless. You will learn how to debug and deploy your code using Backendless CodeRunner.

Setup

Setup

Before you start with the guide, you will need the following:

1. Create a Backendless account, register for your free account at https://develop.backendless.com/registration
2. Download and install Node.js version 4 or above and NPM.
3. Create a new project, where you can put your Backendless Server Code logic. The simplest approach is to use the Download Project Template function in Backendless Console (see the instructions below).
4. Login to Backendless Console and select your app.
5. Click the Download Project Template button at the top of the screen:
download-project-template-button.zoom60
 
6. In the displayed popup double-click JAVASCRIPT and click API SERVICE.
download-api-service-template.zoom80
 
7. Click the DOWNLOAD button to download a project template for your app.
8. Unzip the downloaded file into a directory. Let's call that directory "Project root".
9. Open a command prompt/Terminal window and change to the "Project root" directory.
10. Run the following command to install required dependencies:

npm install

11. Now the project is setup and is ready for development. Notice that the downloaded template already includes a sample service.
 

Service Design

The service developed in this guide is a trivialized shopping cart. The APIs generated by Backendless from the code allows adding items to a shopping cart, retrieving and deleting the contents of the cart. Each operation of the service must be stateless, as a result, any intermediary state between the service calls must be stored elsewhere (i.e. Backendless persistent storage, server-side cache, etc). In the design of the code, the state is a JavaScript object representing a shopping cart. The object is stored in Backendless Cache between the service invocations. Every method call retrieves the cart object from cache, updates it as necessary and then places it back into cache. The code identifies every shopping cart by a logical name, thus the same service code will be able to handle multiple cart objects. The "purchase" API call persists the contents of the shopping cart in the Backendless database. The diagram below illustrates the design:

API-Service-Design

Service Development

Service implementation will consist of four classes:

The API service implementation is in the ShoppingCartService class.
There are two data model classes: ShoppingItem and Order, they are used to represent data objects stored in Backendless Data Service
The ShoppingCart class is used by the service implementation to store any intermediate state between the service calls.

 

1. Create the /app/models directory in the root directory of your project. The directory will contain classes defining your data model.
2. Create shopping-item.js in the /app/models/ directory with the following contents:

'use strict';

/**
 * @property {String} objectId
 * @property {String} product
 * @property {Number} price
 * @property {Number} quantity
 */
class ShoppingItem extends Backendless.ServerCode.PersistenceItem {

}

module.exports = Backendless.ServerCode.addType(ShoppingItem);

Notice the following details about the code above:

The class extends Backendless.ServerCode.PersistentItem. Since instances of the ShoppingItem class are stored in the Backendless data storage, this inheritance adds for convenience several instance methods like save, remove, loadRelations, .., and static class methods like find, findLast, findById, ...
The Backendless.ServerCode.addType method registers the class as one of the types used during server code invocation. That means that for any service method which uses the class as an argument, CodeRunner™ will be able to adapt/cast/convert incoming argument objects into instance of the ShoppingItem class. For added convenience, the addType method returns the passed object.
The ShoppingItem class is added to module.exports. This is done to enable access to this class from other modules.
The annotation in the comment for the method is very important. It is processed by Backendless and is used for service API generation.
3. Create order.js in the /app/models/ directory with the following contents:

'use strict';

class Order extends Backendless.ServerCode.PersistenceItem {
  constructor(items) {
    super();

    /**
     * @type {ShoppingItem[]}
     */
    this.items = items || [];

    /**
     * @type {Number}
     */
    this.orderPrice = this.items.reduce((sum, item) => {
      return (sum || 0) + (item.price * item.quantity);
    }, 0);
  }
}

module.exports = Backendless.ServerCode.addType(Order);

4. Create shopping-cart.js in the /app/models/ directory with the following contents:

'use strict';

class ShoppingCart {
  constructor(opts) {
    opts = opts || {};

    this.name = opts.name;
    this.items = opts.items || [];
    this.___class = ShoppingCart.name;
  }

  addItem(item) {
    item.objectId = null;

    this.items.push(item);
  }

  deleteItem(product) {
    const idx = this.items.findIndex(item => item.product === product);

    if (idx === -1) {
      throw new Error(`No ${product} in cart`);
    }

    this.items.splice(idx, 1);

    return this;
  }

  setQuantity(product, quantity) {
    const productItem = this.items.find(item => item.product === product);

    if (!productItem) {
      throw new Error(`No [${product}] in cart`);
    }

    productItem.quantity = quantity;

    return this;
  }

  getItems() {
    return this.items;
  }

  destroy() {
    return Backendless.Cache.remove(this.name, this);
  }

  save() {
    return Backendless.Cache.put(this.name, this);
  }

  static get(name, mustExist) {
    Backendless.Cache.setObjectFactory(ShoppingCart.name, ShoppingCart);

    return Backendless.Cache.get(name).then(cart => {
      if (!cart && mustExist) {
        throw new Error(`Shopping cart [${name}] does not exist`);
      }

      return cart;
    });
  }
}

module.exports = ShoppingCart;

5. Create the /app/services directory in the project's root directory.
6. Create shopping-cart-service.js in the /app/services/ directory with the following contents:

'use strict';

const Order        = require('../models/order'),
      ShoppingCart = require('../models/shopping-cart');

class ShoppingCartService {

  /**
   * @param {String} cartName
   * @param {ShoppingItem} item
   * @returns {Promise.<void>}
   */
  addItem(cartName, item) {
    return this.addItems(cartName, [item]);
  }

  /**
   * @param {String} cartName
   * @param {Array.<ShoppingItem>} items
   * @returns {Promise.<void>}
   */
  addItems(cartName, items) {
    return ShoppingCart.get(cartName).then(cart => {
      if (!cart) {
        cart = new ShoppingCart({ name: cartName });
      }

      items.forEach(item => cart.addItem(item));

      return cart.save();
    });
  }

  /**
   * @param {String} cartName
   * @param {String} product
   * @returns {Promise.<void>}
   */
  deleteItem(cartName, product) {
    return ShoppingCart.get(cartName, true).then(cart => cart.deleteItem(product).save());
  }

  /**
   * @param {String} cartName
   * @returns {Promise.<Array.<ShoppingItem>>}
   */
  getItems(cartName) {
    return ShoppingCart.get(cartName, true).then(cart => cart.getItems());
  }

  /**
   * @param {String} cartName
   * @param {String} productName
   * @param {Number} quantity
   * @returns {Promise.<void>}
   */
  setQuantity(cartName, productName, quantity) {
    return ShoppingCart.get(cartName, true).then(cart => cart.setQuantity(productName, quantity).save());
  }

  /**
   * @param {String} cartName
   * @returns {Promise.<Order>}
   */
  purchase(cartName) {
    return ShoppingCart.get(cartName, true).then(cart => {
      const order = new Order(cart.getItems());

      return order.save()
        .then(() => cart.destroy())
        .then(() => order);
    });
  }
}

Backendless.ServerCode.addService(ShoppingCartService);

Notice the following details about the code above:

The service class is registered with the Backendless.ServerCode.addService method.
All methods of a class registered as service become individual API endpoints when the service is deployed into CodeRunner™.
The annotations in the comments are used by Backendless to create service API declaration.

 

When you are done with this part of the guide, your project's directory structure should look as in the screenshot below:

js-service-structure

Service Debugging

Backendless supports the "remote-registration-local-debugging" model. This means that the service code can be debugged locally on the developer's computer while the service registration and its API endpoints become available through Backendless. API requests for a service in debug can be sent to Backendless and it routes them to the local debugging process running on the developer's computer. The diagram below illustrates the model:

Local-Debug-Remote-Registration

 

To start a debugging session, run the following command from the project's root directory:

npm run debug

The command inspects the service code, registers it with Backendless for debugging and starts a local copy of CodeRunner™. Successful execution of the command must produce the following output:

15:15:10.417 - CodeRunner(tm) Backendless Debugging Utility
15:15:10.419 - Copyright(C) 2016 Backendless Corp. All rights reserved.
15:15:10.420 - Version 1.4.2
15:15:10.606 - Starting Debug Code Runner...
15:15:10.613 - Building Model..
15:15:11.485 - Model Build completed
15:15:11.486 - Custom Types (2):
15:15:11.486 -   Order (app/models/order.js)
15:15:11.486 -   ShoppingItem (app/models/shopping-item.js)
15:15:11.486 - Services (1):
15:15:11.486 -   ShoppingCartService (app/services/shopping-cart-service.js)
15:15:11.652 - Registering Code Runner on https://api.backendless.com
15:15:12.103 - Runner successfully registered.
15:15:12.104 - Registering Model on https://api.backendless.com
15:15:12.282 - Model successfully registered
15:15:12.282 - Clearing Debug Services
15:15:12.449 - Registering Service ShoppingCartService for debug
15:15:12.668 - Service ShoppingCartService successfully registered
15:15:12.668 - Waiting for Server Code tasks..

 

The service in the debug mode will appear in Backendless console on the Business Logic > API Services screen. There is a label saying DEBUG which indicates the service is in the debug mode. You can use the API Inspector interface in Backendless console to perform service invocations and also download the generated client SDKs to test the service methods from a client application.

Working with the Service

The instructions below apply to both modes of service execution (debug and deployment).

1. Once the service is in the debug mode, Backendless generates the service's REST APIs and the native SDKs. You can see the service and its operations on the Business Logic > API Services screen. For the service reviewed in this guide you will see the following screen when the service is running in the debug mode:
js-service-in-debug-v4.zoom70

 

2. Backendless Console lists all available service operations/methods. To see the details of an operation and to invoke it, click the operation name. For example, click the addItem method. When invoking the method from Backendless Console, the request body must be a JSON object which combines the shopping cart name and the item being added to the cart:
{
  "cartName": "string",
  "item": {
    "objectId": "string",
    "product": "string",
    "price": 0,
    "quantity": 0
  }
}
. However, when the operation is invoked from a native client (Android, iOS, JS), method arguments will be represented by objects from the native programming environment. In other words, you do not need to convert everything to JSON when working with API Services, unless you use service's REST API.

 

3. To invoke the addItem method from Backendless console, you will need to provide argument values. Click the model schema area to copy its contents to the to the argument value area:
click-to-copy-schema

 

4. Modify the argument value as shown below. The objectId element should be removed, it is not needed for this operation.
{
  "cartName": "mycart",
  "item": {
    "product": "Hoverboard",
    "price": 499,
    "quantity": 1
  }
}

 

5. Click INVOKE to make an invocation of the method:
js-invocation-result-v4

 

6. Notice you can get a complete cURL request which executes the same REST API request by switching to the CODE section:
curl-request-api-service

 

7. To see the contents of the shopping cart, click the getItems method. Enter the same name for the shopping cart that was used in the addItem invocation ("mycart") and click INVOKE again:
get-items-invoke-v4.zoom80

Service Deployment

To deploy the code to Backendless, run the following command from the project's root directory. Once the code is deployed, it is packaged as an API service and will run within Backendless:

npm run deploy

 

REST Routes

Backendless supports two ways for creating REST API routes:

1. Service code explicitly declares REST routes.
2. Generate REST routes based on the method signatures in the service class.

 

REST Routes from Explicit Declaration

JavaScript code can use the JSDoc comments to annotate code with information about REST routes. Consider the following example:

class HelloService {
 /**
  * @route POST /say/hello
  */
  helloWorld( ) {
    return "hello";
  }
}

Backendless.ServerCode.addService( HelloService );

Notice the @route annotation in the comment for the helloWorld method. When the service is deployed to Backendless, the REST route associated with the method will be built as shown below:

POST https://api.backendless.com/<App ID>/<REST API key>/services/<service name>/<service version>/say/hello

 

The general format for a service API route is:

POST https://api.backendless.com/<App ID>/<REST API key>/services/<service name>/<service version>/<path from @route>

where:

<App ID> - Application ID from the Manage > App Settings screen in Backendless Console.
<REST API Key> - REST API Key for the application from the Manage > App Settings screen in Backendless Console.
<service name> - Service name as shown in Backendless Console. Service name is the same as the name of the service class.
<service version> - Version number/name assigned to service at the time when code is uploaded/published to Backendless.
<path from @route> - Path specified in the @route comment for the method.

 

The @route annotation has the following syntax:

@route GET | POST | PUT | DELETE /<path>

If HTTP operation (GET|POST|PUT|DELETE) is not specified, the route is GET by default.

 

The /<path> element may include path parameters. The example below declares the {name} path parameter. The parameter's value can be accessed  in the code using this.request.pathParams.<parametername>.

class EchoService {
 /**
  * @route POST /echo/{name}
  */
  sendNameBack( ) {
     return "Hello " + this.request.pathParams.name;
  }
}

Backendless.ServerCode.addService( EchoService );

 

REST Routes from Method Signatures

Backendless uses the following rules when creating REST APIs from method signatures:

Method's name prefix

HTTP command

Route

Argument(s)

addXXX( T )

POST

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

JSON format of T sent
in request body

setXXX( T )

PUT

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

JSON format of T sent
in request body

setXXX( int indexArg, T valueArg )

PUT

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

JSON format of indexArg and
valueArg in request body:
{

   "indexArg" : value,

   "valueArg" : value

}

deleteXXX( T )

DELETE

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

JSON format of XXX sent
in request body

getXXX()

GET

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX

n/a

getXXX( int index )

GET

/<app ID>/<REST API key>/services/<service name>/<service version>/XXX?index=value

The index argument must be
sent as a query argument

none of the above - foobar()

POST

/<app ID>/<REST API key>/services/<service name>/<service version>/foobar


 

where:

<app ID>        - Application ID from the Manage > App Settings screen in Backendless Console.

<REST API Key>        - REST API Key for the application from the Manage > App Settings screen in Backendless Console.

<service name>        - Service name as shown in Backendless Console. Service name is the same as the name of the service class.

<service version>        - Version number/name assigned to service at the time when code is uploaded/published to Backendless.

 

Consider the following service:

'use strict';

class ShoppingService {
  /**
   * @param {ShoppingItem} item
   */
  addItem(item) {
    // skipping implementation for brevity
  }

  /**
   * @param {ShoppingItem} item
   */
  deleteItem(item) {
    // skipping implementation for brevity
  }

  /**
   * @returns {ShoppingItem[]}
   */
  getCartItems() {
    // skipping implementation for brevity
  }

  /**
   * @returns {Number}
   */
  calculateTotal() {
    // skipping implementation for brevity
  }
}

Backendless.ServerCode.addService(ShoppingService);

 

Assuming the name assigned to the service is ShoppingService and version is 1.0, API Engine generates the following REST API routes for the service:

 

addXXX method:

JavaScript function:

/**
 * @param {ShoppingItem} item
 */
addItem(item)

REST API:

POST http://api.backendless.com/<API ID><REST API Key>/services/ShoppingService/1.0/Item
Request body: a JSON object representing ShoppingItem

 

getXXX method:

JavaScript function:

/**
 * @returns {ShoppingItem[]}
 */
getCartItems()

REST API:

GET http://api.backendless.com/<API ID><REST API Key>/services/ShoppingService/1.0/CartItems

 

deleteXXX method:

JavaScript function:

/**
* @param {ShoppingItem} item
*/
deleteItem(item)

REST API:

DELETE http://api.backendless.com/<API ID><REST API Key>/services/ShoppingService/1.0/Item

 

all other methods:

JavaScript function:

/**
 * @returns {Number}
 */
calculateTotal()

REST API:

POST http://api.backendless.com/<API ID><REST API Key>/services/ShoppingService/1.0/calculateTotal

Type Annotations

Backendless uses JSDoc-based annotations in the deployed JavaScript code to extract information about method names, their types and the method return types. This information is used for several purposes. For instance, Backendless Console uses the type information to build service invocation user interface with a form for the input parameters. Additionally, the type information is used for the native client SDK generation.

 

Consider the following example which declares three parameters and a return type:

  /**
   * @param {string} arg1
   * @param {boolean} arg2
   * @param {number} arg3
   * @return {Array.<CustomType>}
   */ 
  foo( arg1, arg2, arg3 ) {
    return [];
  }

The @param annotations declare the argument types and names. The @return annotation declares the return type.

 

The general syntax for the @param annotation is:

@param {type} argument-name

where:

type - a predefined or a custom type. Can be string, boolean, number, Array, Object or a custom object type. Must be surrounded by the curly brackets.
argument-name - name of the argument which will be displayed in Backendless console and used in the generated native SDKs

 

The @return annotation syntax is:

@return {Promise<type> | <type>}

where:

type - a predefined or a custom type. Can be string, boolean, number, void, Array, Object or a custom type.

In either case (whether service method returns Promise or specific data) Backendless will use the concrete type for the code generation purposes.

Arrays

For the array types, the syntax supports type definition for the array elements using the following format:

Array<type>

where type can be recursively defined using the same types applicable for the @param annotation. For example, the following code includes a  type definition for a parameter which can be an array of strings:

  /**
   * @param {Array<string>} arg1
   */ 
  foo( arg1 ) {
   ...
  }

Custom Object Types

Backendless supports two ways for declaring custom object types - the first one is with JS class definition and the second is by using the @typedef annotation. The JS class definition approach requires that a JS class is declared and registered with Backendless. Consider the example below which declares and registers a custom type:

class Order {
  constructor(items) {
    super();
    
    /**
     * @type {Array.<string>}
     */
    this.items = items;

    /**
     * @type {number}
     */
    this.orderPrice = 0;
  }
}

Backendless.ServerCode.addType(Order);

Notice that the properties of the Order class, are also declared using the @type annotations - this helps Backendless to understand and create a complete "schema" for the data type. Additionally, the type must be registered with Backendless using the following call:

Backendless.ServerCode.addType(Order);

where the argument of the addType method is a reference to the custom type class.

 

The second approach for declaring a custom type is by using an annotation. With this approach, a data type class definition is not required. The example below declares a data type which can be used in other @param, @return and @type annotations without the actual class present in the application:

  /**
   * @typedef {Object} ShoppingItem
   * @property {string} objectId
   * @property {string} product
   * @property {number} price
   * @property {number} quantity
   */

The @typedef annotation has the following syntax:

@typedef {base type} TypeName
@property {type} property-name1
@property {type} property-nameN

where:

base type - the parent type for the declared type. If base type is another custom type then the TypeName type inherits all the properties from base type.
TypeName - name assigned to the custom data type
type - a type for the property identified by property-name1..N

 

Once a type is defined with @typedef, it can be used in all {type} definitions, for example:

/**
   * @typedef {Object} Order
   * @property {String} orderNumber
   * @property {Number} customerName
   */

  /**
   * @param {Order} order
   * @returns {void}
   */
  processOrder(order) {
   ...
  }

Method Visibility

By default all methods for a class registered as a service become API service operations. This may not be desirable for every method. To exclude a method from the list of the API operations use the following annotation:

  /**
   * @private
   */
  thisMethodIsPrivate() {
   ...  
  }

Invocation Context

The Backendless container which hosts and runs the services code provides a special object for every method invocation.  The object is available via this.request and provides access to the following data:

Application ID - id of the application in which context the method is invoked.

this.request.context.appId

User ID - objectId of the currently logged in user whose identity is used to invoke the method. A value for this parameter is not present if there is no logged in user (or if the user-token header is not passed for the REST invocations).

this.request.context.userId

User token - value of the user-token header sent by the client application when invoking the service.

this.request.context.userToken

User roles - an array of the role names of the logged-in user (if any) who made the API call.

this.request.context.userRoles

Device type - the type of the device which initiated the method invocation.

this.request.context.deviceType

HTTP headers - HTTP headers sent by the client application when invoking the service.

this.request.headers.<headername>

 

Request path - path in the request URL which identifies the invoked method. This is the part of the URL which comes after <service version>:

https://api.backendless.com/<App ID>/<REST API key>/services/<service name>/<service version>/<path from @route>

the value is available by accessing:

this.request.path

Path parameters - if the code declares a parameterized custom REST route, the parameters can be accessed with the following code:

this.request.pathParams.<parameter name>

for example, for the following parameterized REST route:

@route POST /contacts/{firstname}/{lastname}

and the following request path:

/contacts/Joe/Developer

the service code will receive the following this.request.pathParams object:

"pathParams": {
   "firstname": "Joe",
   "lastname": "Developer"
}

Query parameters - all parameters for the methods recognized or declared as HTTP GET must be sent in the URL's query string. Service code can use the following API to get access to these parameters:

this.request.queryParams.<param name>

 

Custom Errors

Service code may return custom error code and/or error message using the API documented below. The API also supports returning an HTTP status code along with the error code/message. The following sample service demonstrates possible usages of the custom error API:

class ErrorExampleService {
  simpleError() {
    //error code:0
    //error message: 'I am a simple Error'
    //httpStatusCode: -1 (default)
    throw new Error('I am a simple Error');
  }
  
  customError() {
    //error code:245
    //error message: 'I am a specific error'
    //httpStatusCode: -1 (default)
    throw new Backendless.ServerCode.Error(245, 'I am a specific error');
  }

  customErrorWithHttpStatusCode() {
    //error code:245
    //error message: 'I am a specific error'
    //httpStatusCode: 403
    throw new Backendless.ServerCode.Error(245, 'I am a specific error', 403);
  }

}

Backendless.ServerCode.addService(ErrorExampleService);

Versioning

Versioning is an inherent part of the API Engine's service management. Every deployed service is assigned a version number which is used in both REST and native APIs. Multiple versions of the the same service can coexist in the same Backendless application. Each version of the service runs independently. The system can accommodate infinite number of the service versions.

 

Version number is assigned automatically by Backendless - the default value is 1.0.0. Service code can also set the version number by using the version property on the service class:

class WeatherService {
  getWeather() {
    return { temperature:70, condition:"sunny" };
  }
}

SampleService.version = "2.5.0";

Backendless.ServerCode.addService(WeatherService);

 

Backendless Console displays version number for each deployed service:

version-number-in-console.zoom80

REST API

Service's version number is used in the REST API's endpoint URI generated for the service:

https://api.backendless.com/<app ID>/<REST API key>/services/<service name>/<service version>/<path>

Native API

The APIs generated by Backendless hide specifics of the service's remote method invocations. However, the internal implementation uses the version number (and service name) to identify the service:

Backendless.CustomService.invoke( "<service name>", "<service version>", "<method name>", <method arguments> );

Logging

Service code can use the Logging API available in the Java/Android SDK as well as REST. The API is documented at:

https://backendless.com/documentation/utilities/android/ut_logging.htm

 

Documentation for the management and configuration of logging is available at:

https://backendless.com/documentation/manage/mgmt_logging.htm

 

All console.log messages are automatically routed to the Backendless Logging API. They are stored in the log file(s) in the SERVER_CODE category.

Service Listing

Backendless Console lists both deployed and in-debug services on the API SERVICES section of the Business Logic screen:

services-in-debug-deployed1.zoom30

 

 

The deployed services are deployed to and run on the Backendless servers. The in-debug services are the ones for which a local debugging session with CodeRunner has been started. These services function exactly the same way as the deployed services except the service code runs on the developer's computer. As soon as the debug session is stopped, the service is removed from the list.

 

To refresh a list of services, use the refresh icon:

services-refresh-list.zoom30

Operation Listing

Backendless Console displays a list of service operations when a service is selected. To select a service simply click its method name. Operations are shown as REST routes with the HTTP operation and the path name:

service-and-operations-v4

 

Client SDK and Service Invocation

Backendless instantly generates native client side SDKs when a service is registered. This applies to all API services, regardless of the language used to implement them. The SDKs can be downloaded using the Download Client SDK icon:

download-client-sdk2

 

The generated client-side code is very easy-to-use and is self-explanatory. There is a class representing client-side proxy of the remote service. The class contains the same methods as the service. The implementation of the methods hides the complexity of the remote method invocation. To call an API operation, simply create an instance of the proxy class and invoke its methods.

REST Service Invocation

Backendless Console provides a visual interface for invoking service operations via the generated REST interface. To invoke a service operation, click its name. Console renders operation arguments based on the argument type information from the service code. Services implemented in JavaScript must use the JSDoc style annotations for describing method arguments to facilitate proper rendering of the method parameters form.

 

Backendless Console uses the generated REST API when invoking service methods. As a result, you can also see the details of an invocation using the Network tab in the browser's developer tools.

 

HTTP GET operations

Parameters for the HTTP GET operations must be sent as URL's query params. Consider the following example:

  /**
   * @param {String} itemName
   */
  getItem(itemName) {
   ...
  }

The method does not have an explicit @route annotation, however, since the method name starts with "get", Backendless will generate an HTTP GET route for the operation. Notice the @param {String} itemName annotation in the comment for the method. It instructs Backendless that the expected data type for the method argument is String and the argument name is itemName. When the method is selected in console, the parameter is rendered as shown below:

get-item-operation

Notice that you can specify additional parameters on the PARAMETERS tab. This is allowed because it is an HTTP GET operation and all parameters are sent to the server in the URL's query string - thus any parameters not explicitly defined by the method can also be sent to the service. Any "dynamic" parameters can be obtained in the code with :

this.request.queryParams.<param name>

 

HTTP POST|PUT|DELETE operations

Parameters for POST\PUT\DELETE operations must be sent in the payload/body of an HTTP request. Console renders the parameters based on the description in the JSDoc comments. Consider the following example:

  /**
   * @param {string} message
   * @param {boolean} important
   */ 
  addNote( message, important ) {
   ... 
  }

Just like with the GET example above, the method does not have an explicit @route annotation. Backendless creates a REST route based on the method name. Method name starts with "add" and per the REST Routes rules, Backendless will generate an HTTP POST route for the operation. For methods with more than one argument, Backendless combines them into a JSON object on the request side:

invoke-post-method-v4

 

Clicking the body schema area copies its contents into the body field where it can be edited. To invoke the method click the INVOKE button which sends a service invocation request to Backendless.

Service Security

Access to service APIs can be restricted with the user/role-based security of the application where the service runs. A permission either allows or denies invocation of a method for a specific user or all users who belong to a role. Follow the steps below to configure service security:

1. Login to Backendless console, select the application where the service is deployed and click the Business Logic icon.
2. Select a service from the list in the API Services section.
3. Click the Set Permissions icon in the toolbar:
set-permissions-icon

The permissions management user interface has two sections: User Permissions and Roles Permissions. Both sections use the same principle for applying permissions to users/roles for all or specific service methods. Select a method which should be secured using the method selector list or if the permission assignment for a user/role is going to be the same for all methods, use All Methods:

api-services-permission-method-selector.zoom80

 

To change permission assignment, click the icon at the intersection of a user/role row and the Method column. The icon represents the current permission and has three states which can be changes by clicking the icon.

ico-grant - Represents a permission allowing method invocation for the specified user or role.

ico-reject   - Represents a permission denying method invocation for the specified user or role.

ico-checkmark-gray - Represents "inherited" permission allowing method invocation. Since service permissions do not have the concept of global assignments, the gray check mark essentially is the same as the green one.

 

Runtime Permission Handling

The NotAuthenticatedUser role applies to any API request which does not have a user identity identified by the user-token HTTP header. The value of the user-token header is obtained on the client side from the result of the login API.

 

When Backendless receives a service API call, it uses the value from the user-token header to determine the user's identity and all the roles the user belongs to. Once a list of roles is identified, Backendless checks all assigned permissions for the user and his roles for the invoked method. If any of the permissions deny access, the invocation will be denied and an error is returned back to the client.

 

When testing service invocations with Backendless console, an authenticated request can be performed by logging in a user on the HEADERS page for a method:

login-to-impersonate.zoom80

 

Once a user is logged in, Console uses the assigned user-token for all service method invocations made through console.

Analytics

Backendless automatically tracks service API usage. Every service method invocation is captured by the Backendless Analytics module and can be viewed using Backendless Console:

1. Login to Backendless Console and select the app where the service is deployed.
2. Click the Manage icon, then Analytics.
3. Click the Service Name and Method Name checkboxes.
4. Select the service(s) from the Service Name dropdown list to see the usage of the service APIs:
analytics-codless.zoom70

The API usage is categorized by the type of clients initiated method calls. To see a chart corresponding to a specific client type, service and method simply click the checkbox for that row. The Total Calls column shows the number of API calls made by the specific client type (the All Clients rows show the total calls across all client types).

API Event Handlers

Overview

An event handler is a custom, server-side code which responds to an API event. For every API call Backendless generates two types of events - "before" and "after". The before event is fired before the default logic of the API implementation is executed and the "after" event is triggered right after the default API implementation logic. An event handler can respond to either one of these events. A synchronous (blocking) event handler participates in the API invocation chain and can modify the objects in the chain's flow. For example, the "before" event handlers can modify arguments of the API calls, so the default logic gets the modified objects. Similarly, an "after" handler can modify the return value (or exception) so the client application which made the API request receives the modified value. The diagram below illustrates the flow:

backendless-incocation-chain

All built-in Backendless services emit the "before" and "after" events for every API call. Consider the following example which is an API event handler for the "beforeCreate" API event. The event is triggered when a client application saves an object into the "Contact" data table:

/**
* @param {Object} req The request object contains information about the request
* @param {Object} req.context The execution context contains an information about application, current user and event
* @param {Contact} req.item An item to create
*
* @returns {Contact|Promise.<Contact>|void} By returning a value you can stop further event propagation and return
*          a specific result to the caller
*/
Backendless.ServerCode.Persistence.beforeCreate('Contact', function(req) {
  //add your code here
});
There are several important design elements in the code snippet:

Backendless.ServerCode.Persistence identifies the service for which the event handler will be injecting itself into the API invocation handling chain.
beforeCreate indicates the event handler will be invoked before the default logic of the create method in the Backendless Data service is executed.
The first argument of the beforeCreate method specifies the "asset" or in this case the name of the data table for which the code will be called.

Basic Quick Start

By the end of this guide  you will have a Backendless application with a custom API event handler which modifies an object received from a client app before it is saved in Backendless.

 

1. Login to Backendless console and create a new application called OrderManagement. You can use an existing application, however this guide assumes that certain data tables are created, which may cause a conflict in an existing app. Use caution if you decide to use an existing app.
2. Click the Data icon to navigate to the Data/Schema browser section of the console:
click-data
 
3. Click the "plus" icon to create a new data table. The icon is located right above the "APP TABLES" section. The reason a table should be created first is because one of the next steps will be registering an event handler for this table. Name the table "Order".
new-order-table
 
4. Click CREATE to create the table. Click YES in the popup to modify the table schema. Enter "customername" in the Name field as shown below:
new-column-customer-name
 
5. Click CREATE to add the column.
6. Click the Business Logic icon to navigate to the custom business logic section:
click-business-logic
 
7. Click EVENT HANDLERS to switch to the event handler management screen.
8. Click the New Event Handler link and make the selections in the popup as shown in the screenshot below. The
new-js-event-handler
9. Click SAVE to finalize the creation of the event handler. Backendless creates an event handler and displays it in the list. Notice the event handler is color-coded - the blue color indicates the event handler is in the "Draft" model which means the code has not been deployed to Backendless yet:
new-js-handler-quick-start.zoom80
 
10. Backendless supports online code editing and code deployment directly from Backendless Console. Modify the code of the event handler (directly in console) as shown below:
/**
* @param {Object} req The request object contains information about the request
* @param {Object} req.context The execution context contains an information about application, current user and event
* @param {Order} req.item An item to create
*
* @returns {Order|Promise.<Order>|void} By returning a value you can stop further event propagation and return
*          a specific result to the caller
*/
Backendless.ServerCode.Persistence.beforeCreate('Order', function(req) {
  console.log( "Received order object. Customer name - " + req.item.customername );
  req.item.customername = req.item.customername.toUpperCase();
});
11. Click the Save and Deploy button. This will enable the code to run in response to the API which saves objects in the Contact table:
click-save-and-deploy.zoom80
 
12. When the event handler is deployed, the console will show another "beforeCreate" handler color-coded for the Production mode - which is a live code deployed and responding to the API events.
event-handler-production-deployment.zoom73
13. To test the code you must issue an API request to store an object in the Order table. This can be done either in a client application using a Backendless SDK or with the REST API. For example, consider the following cURL request:

curl -H Content-Type:application/json -X POST -d "{\"customername\":\"foobar corp\"}" -v https://api.backendless.com/APP-ID/REST-SECRET-KEY/data/Order

 

14. Alternatively, Backendless Console includes a special tool for generating REST API requests. Click the Data icon in Backendless Console, select the Order table and then click REST CONSOLE:
rest-console-order-table.zoom74
15. Type in (or copy/paste) the following JSON object into the Request Body field and click POST. REST Console sends a REST API request to Backendless to save the new object. The API request is routed to the event handler which changes the "customername" property to upper case. You can see the saved object in the Response Body field:
object-modified-by-event-handler.zoom80

 

This concludes the Quick Start. You can try repeating the process for other event handler or timers. If you run into any problems or have any questions, do not hesitate contacting us at http://support.backendless.com.

Advanced Quick Start

This quick start is an "alternative path" of the Basic Quick Start guide. In the basic guide the code is modified and deployed to Backendless servers directly from Backendless Console. In this guide you will learn how to:

download the generated code from Backendless console
modify the code and run in the debug mode
deploy the code to Backendless servers.

 

1. To get started, go through the steps 1-9 of the Basic Quick Start guide.
2. Click the Download link and select JS
download-js-code-event-handler
3. Extract the files from the downloaded archive.
4. Open a command prompt/terminal window and change to the project's root directory. Run the following command which will install Backendless CodeRunner and Backendless SDK for JS:

npm install

5. Open /handlers/persistence/order/beforeCreate.js in a text/code editor and modify the file as shown below:
/**
* @param {Object} req The request object contains information about the request
* @param {Object} req.context The execution context contains an information about application, current user and event
* @param {Order} req.item An item to create
*
* @returns {Order|Promise.<Order>|void} By returning a value you can stop further event propagation and return
*          a specific result to the caller
*/
Backendless.ServerCode.Persistence.beforeCreate('Order', function(req) {
  console.log( "Received order object. Customer name - " + req.item.customername );
  req.item.customername = req.item.customername.toUpperCase();
});
6. Save the file. Return to the command prompt/terminal window and run the following command:

npm run debug

7. The command starts CodeRunner starts, it produces the following output (the CodeRunner version number may be different):

your-comp-name:bin username$  npm run debug
 
> OrderManagement-servercode@1.0.0 debug /Users/mark/Downloads/OrderManagement
> coderunner debug
 
13:56:01.272 - CodeRunner(tm) Backendless JavaScript CodeRunner v4.3.0-beta.5
13:56:01.273 - Copyright(C) 2017 Backendless Corp. All rights reserved.
13:56:01.335 - Starting Debug Code Runner...
13:56:01.335 - Building Model..
13:56:01.337 - Reading handlers/persistence/order/beforeCreate.js...
13:56:01.338 - Reading models/order.js...
13:56:01.625 - Model Build completed
13:56:01.626 - Event handlers (1):
13:56:01.626 -   persistence.beforeCreate (Order) (handlers/persistence/order/beforeCreate.js)
13:56:01.626 - Custom Types (1):
13:56:01.626 -   Order (models/order.js)
13:56:01.791 - Registering Code Runner on https://api.backendless.com
13:56:02.192 - Runner successfully registered.
13:56:02.193 - Registering Model on https://api.backendless.com
13:56:02.474 - Model successfully registered
13:56:02.474 - Waiting for Server Code tasks..

 

8. At this point CodeRunner™ is connected to Backendless and the code is ready for local debugging. Return to Backendless Console and refresh the EVENT HANDLERS screen. You will see the event handler in the debug mode:
event-handler-in-debug.zoom80
9. To test the event handler, return to the Basic Quick Start and follow the instructions from step 13.
10. When an event handler is in the debug mode and receives API events, it produces additional output which may be helpful for debugging purposes:

14:30:01.905 - [C47C55FA-926D-F386-FFDE-188529D3EF00] New task arrived!
14:30:01.911 - [C47C55FA-926D-F386-FFDE-188529D3EF00] [INVOKE HANDLER] persistence.beforeCreate (Order)
Received order object. Customer name - foobar corp
14:30:01.915 - [C47C55FA-926D-F386-FFDE-188529D3EF00] Processing finished

11. You can stop the debug mode by entering Ctrl+C. To deploy the code from the developer's computer to Backendless servers, run the following command:

npm run deploy

12. When the code is being deployed, CodeRunner produces the following output. Notice how individual event handlers and recognized custom types are listed in the program output:

npm run deploy
 
> OrderManagement-servercode@1.0.0 deploy /Users/mark/Downloads/OrderManagement
> coderunner deploy
 
14:31:39.272 - CodeRunner(tm) Backendless JavaScript CodeRunner v4.3.0-beta.5
14:31:39.273 - Copyright(C) 2017 Backendless Corp. All rights reserved.
14:31:39.322 - Building Model..
14:31:39.323 - Reading handlers/persistence/order/beforeCreate.js...
14:31:39.324 - Reading models/order.js...
14:31:39.657 - Model Build completed
14:31:39.657 - Event handlers (1):
14:31:39.657 -   persistence.beforeCreate (Order) (handlers/persistence/order/beforeCreate.js)
14:31:39.658 - Custom Types (1):
14:31:39.658 -   Order (models/order.js)
14:31:39.658 - Preparing app zip file for deployment..
14:31:40.062 - 2 files added into deployment archive
14:31:40.067 - Publishing Model to server
14:31:42.772 - Successfully published

13. When the event handler is deployed, it will appear in Backendless Console with the green color-code (indicating the production deployment):event-handler-production-deployment.zoom73
 

Event Handler Asset

Event handlers triggered by the APIs for data, messaging, file and geolocation services must declare an "asset". Asset is a general term used to identify one of the following:

a data table for the Data service APIs.
a messaging channel for the Messaging APIs.
a file or a directory for the File service APIs.
a geocategory for the Geolocation APIs.

 

Consider the following example, which is an event handler for the Data Service's beforeUpdate API event:

/**
* @param {Object} req The request object contains information about the request
* @param {Object} req.context The execution context contains an information 
*                             about application, current user and event
* @param {Order} req.item An item with changes
*
* @returns {Order|Promise.<Order>|void} By returning a value you can stop 
*                                          further event propagation and return
*                                          a specific result to the caller
*/
Backendless.ServerCode.Persistence.beforeUpdate('Order', function(req) {
  //add your code here
});
Notice the first argument for the beforeUpdate function, it is the name of the table for which the event handler is registered.. When event handlers are created in Backendless Console, it automatically selects the appropriate asset (table, messaging channel, file/directory, geo category) and puts it into the generated code.

 

It is possible to create a generic event handler, which would respond to all API events for a given service. For example, the event handler below is triggered when a new object is created in any of the application's tables:

/**
* @param {Object} req The request object contains information about the request
* @param {Object} req.context The execution context contains an information about 
*                             application, current user and event
* @param {Object} req.item An item to create
*
* @returns {Object|Promise.<Object>|void} By returning a value you can stop 
*                                            further event propagation and return
*                                            a specific result to the caller
*/
Backendless.ServerCode.Persistence.beforeCreate('*', function(req) {
  //add your code here
});
Notice the first argument of the beforeCreate function is "*".  This indicates that the event handler will be triggered for the beforeCreate events for all tables in the application.

Event Args and Context

All event handlers receive the req which includes request's context as well values for the original API call which triggered the event:

req.context.appId - Application ID.
req.context.userId - objectId of a user logged in in the client application which made the API call. If there is no logged in user, this value is null.
req.context.userToken - value of the user-token header representing session of the currently logged in user in the client application which made the API call. If there is no logged in user, this value is null.
req.context.userRoles - an array of security roles of the user whose identity was used in the original API call. If there is no logged in user, the array will contain the NotAuthenticatedUser role and a role corresponding to the API key (for example RestUser if the request is made with the REST API Key).
req.context.httpHeaders - a JS object with the HTTP headers and the corresponding values from the original request.
req.context.eventContext - returns the name of the "asset" for which the event has been triggered. This is useful for "generic" (catch all) event handlers which process events for multiple tables/files/messaging channels, etc. For instance, for the data event handlers, the method returns the name of the table targeted in the API call. For more information, see the Event Handler Asset section of the documentation.

 

The req object also provides access to the original API call arguments. Property names will vary between different API event handlers. When you generate an event handler using Backendless console, the JSDoc comments in the generated code will include a detailed list. For example, the code below is generated for the beforeSubscribe event for the Messaging service, notice the JSDoc comments describing req.subscriptionId, req.channel and req.options:

beforesubscribe-js-code.zoom80

 

Stop Event Processing

In some scenarios it might be required to stop further processing of an API event in a "before" event handler. For example, suppose a "beforeRegister" event handler performs user validation and must prevent default Backendless logic from executing. Backendless supports this scenario through a special return type. See the example below:

/**
* @param {Object} req The request object contains information about the request
* @param {Object} req.context The execution context contains an information about application, current user and event
* @param {Object} req.user 
*
* @returns {Object|Promise.<Object>|void} By returning a value you can stop further event propagation and return
*          a specific result to the caller
*/
Backendless.ServerCode.User.beforeRegister(function(req) {
  // your user validation logic goes here
  
  // return a special JS object with the "short" property set to true
  return { short: true }
});

By returning a JS object with the "short" property set to true, the code instructs Backendless to stop further processing of the event.

Custom Events

In addition to the built-in events triggered by the API calls, Backendless supports custom, developer-defined events. A custom event is implemented by an event handler which can be defined using Backendless Console. Custom events can be triggered through a specialized API call from a client library or by other custom business logic. A custom event may accept event arguments and return a value which is delivered back to the client-side that has triggered the event.

Consider the following custom event handler:

/**
* @param {Object} req The request object contains information about the request
* @param {Object} req.context The execution context contains an information about application, current user and event
* @param {Object} req.args 
*
* @returns {Object|Promise.<Object>|void} The event caller will receive this value
*/
Backendless.ServerCode.customEvent('foo', function(req) {
  return { weather:"sunny" }
});

The code above is a custom event handler for the "foo" event. The event name is declared using the Backendless.ServerCode.customEvent function. Event arguments are delivered to the code via req.args argument.

 

Custom Event Return Value

The return value of a custom event handler is sent to the client application where the event was dispatched. For example, the following code returns a JavaScript object. Each client type adapts the return value to a data structure in the native environment. For instance, Android/Java client would receive the response as java.util.Map.

/**
* @param {Object} req The request object contains information about the request
* @param {Object} req.context The execution context contains an information about application, current user and event
* @param {Object} req.args 
*
* @returns {Object|Promise.<Object>|void} The event caller will receive this value
*/
Backendless.ServerCode.customEvent('foo', function(req) {
    console.log( "event arguments: " + req.args );
    return {status:"Event Processed"} ;
});

 

Create Custom Event Handler in Console

To create a custom event handler using Backendless console:

1. Login to console and select an application. Click the Business Logic icon.
2. Click EVENT HANDLERS, then click the New Event Handler link:
new-event-handler-js.zoom80
3. Make the selection as shown in the screenshot below and enter CoolEvent for the Event field:
new-custom-event
4. Click SAVE. Backendless generates custom event handler code which can be edited and deployed from the console.
custom-event-handler-ready-to-go.zoom70
5. At this point you can either write the code and deploy the event handler directly in console, or download the generated code and continue working with it from the developer's computer.

 

API for Dispatching Custom Events

All Backendless client SDKs include the functionality for dispatching a custom event. The API allows to send event arguments to the server-side and receive the result from the event handler.

 

Android/Java:

HashMap args = new HashMap();
args.put( "weather", "sunny" );

Backendless.Events.dispatch( "foo", args, new AsyncCallback <Map>()
{
  @Override
  public void handleResponse( Map result )
  {
    System.out.println( "received result " + result );
  }

  @Override
  public void handleFault( BackendlessFault backendlessFault )
  {
    System.out.println( "got error " + backendlessFault.toString() );
  }
});


iOS:

All methods are available via the backendless.events accessor. Method signatures:

-(void)dispatch:(NSString *)name args:(NSDictionary *)eventArgs response:(void(^)(NSDictionary *data))responseBlock error:(void(^)(Fault *fault))errorBlock;


JavaScript:

Method signature:

Backendless.Events.dispatch( eventName, eventArgs, Async );

Example:

var eventArg = {weather:"sunny"};
 
var successHandler = function( response ) {
};
 
var errorHandler = function( response ) {
};
 
Backendless.Events.dispatch( "foo", eventArgs )
 .then( successHandler )
 .catch( errorHandler );


.NET:

Method signatures:

// synchronous method
public IDictionary Backendless.Events.Dispatch( String eventName, IDictionary eventArgs );
 
// asynchronous method
public void Backendless.Events.Dispatch( String eventName, IDictionary eventArgs, AsyncCallback<IDictionary> callback )

Example:

Dictionary<String, String> eventArgs = new Dictionary<string, string>();
eventArgs.Add( "weather", "sunny" );
 
AsyncCallback<IDictionary> callback = new AsyncCallback<IDictionary>(
       result =>
       {
         System.Console.WriteLine( "received result - " + result );
       },
       fault =>
       {
         System.Console.WriteLine( "Error - " + fault );
       } );
 
Backendless.Events.Dispatch( "foo", eventArgs, callback );


REST:

Method:

 POST

URL:

https://api.backendless.com/<APP ID>/<REST API Key>/servercode/events/<event name>

where:

<APP ID> - the ID of your application generated upon its creation. You can find this header in the Manage > App Settings section of the Backendless Console.
<REST API Key> - the key of your application generated upon its creation. You can find this header in the Manage > App Settings section of the Backendless Console.
<event name> - name of the event to dispatch

 

Request headers:

Content-Type - the static value, should be set to application/json. This header is mandatory.

 

Request body:

a JSON object with arguments for the event handler

 

Example:

curl \
-H Content-Type:application/json \
-X POST  \
-d '{"weather":"sunny"}' \
-v \
https://api.backendless.com/APP-ID/REST-API-KEY/servercode/events/foo

Timers

About Timers

A timer in the Backendless custom business logic system is a block of code with some execution schedule. A timer itself does not need to perform any scheduling work, Backendless automatically calculates the time for the next execution step of the timer, allocates computing resources and runs the code. Consider the following timer implementation:

/**
* CoolTimer timer.
* It is executed according to the schedule
*/
Backendless.ServerCode.addTimer({

  name: 'CoolTimer',

  startDate: 1491929303000,

  frequency: {
    schedule: 'custom',

    repeat: {'every':120}
  },

  /**
  * @param {Object} req
  * @param {String} req.context Application Version Id
  */
  execute(req){
    Backendless.Data.of( "TimerLog" ).save( { timerContext: JSON.stringify(req) })
  }
});

A timer must follow the following guidelines:

A timer must define the name, startDate and frequency properties.
Code must use the Backendless.ServerCode.addTimer function to register a timer.
The frequency property defines the timer execution schedule.
The execute( req ) function must contain the business logic of the timer.

 

Timer Schedule

Timer's frequency is determined by a JSON object assigned to the frequency property. For example, the following declaration sets the timer's frequency as 'weekly' which will be repeated every week on Mondays.

  frequency: {
    schedule: 'weekly',

    repeat: {'every':1,'on':[2]}
  }

The 'frequency' JSON object has the following properties:

Property name

Required

Data Type

Description

schedule

Yes

String

Defines how frequently the timer should run. Possible values are:

'once' - the timer will run only one time.
'daily' - the timer will run on daily basis, repeated every XX number of days set by the repeat property.
'weekly' - the timer will run on weekly basis, on the specified days, repeated every XX number of weeks.
'monthly' - the timer will run on monthly basis, on the specified months, dates, or days of the week.

repeat

No

JSON Object

Defines how frequently the timer execution should be repeated. The structure of the object varies depending on the value set in the schedule property. See the table below for details.

 

The repeat property is a JSON object. The structure of the object varies depending on the value of the schedule property:

schedule property value

repeat object syntax

Description

'once'

Does not apply.

Repeat object must not be present.

'custom'

{
 'every':NUMBER
}

NUMBER is the number of seconds between each execution of the timer.  For example:

{'every':100} - indicates that the timer should execute every 100 seconds.

{'every':5} - requests the timer to run every 5 seconds.
 
The highest possible value is 86400 seconds. The lower limit of the allowed values depends on the billing plan:
Free plan - 60 seconds
Backendless Plus - 5 seconds
Cloud Enterprise - 1 second

'daily'

{
 'every':NUMBER
}

NUMBER is the number of days between each execution of the timer. For example:

{'every':1} - indicates that the timer should execute every day.

{'every':5} - requests the timer to run every 5 days.

'weekly'

{
 'every':NUMBER,
 'on':[1,2,3,4,5,6,7]
}

NUMBER is the number of weeks between each execution of the timer. Numbers in the 'on' array indicate the days of the week when the timer should run - all are optional, but at least one must be present. 1 indicates Sunday, 2 - Monday and so on. For example:

{'every':2, 'on':[2,5]} - requests the timer to run every 2 weeks (every other week) on Monday (2) and Thursday (5).

'monthly'

{
 'every':[MONTHS ARRAY],
 'on':
 {
    'days':[DAYS ARRAY],

    'weekdays':

    {

      'on':[WEEK ASSIGNMENT],

      'weekdays':[1,2,3,4,5,6,7]

    }
 }
}

MONTHS ARRAY is a numeric array of numbers representing months when the timer should run. 1 stands for January and 12 is for December. For example: 'every':[1,3,11] indicates that the monthly timer should run every January, March and November. Once the months when the timer runs are established, the 'on' property is used to set the detailed schedule. There are two mutually exclusive options: 'days' and 'weekdays'. If one is set, the other one must be null.

 

DAYS ARRAY is a numeric array of days on which the timer should run. 1 represents the first day of the month, 31 stands for the 31st. For example: 'days':[1,5,10] indicates that the timer must run on the 1st, 5th and 10th days of the selected months.

 

WEEK ASSIGNMENT is an array of week indicators with possible values: 'first', 'second', 'third', 'fourth' and 'last'. These values are used in combination with the 'weekdays' property which is an array of numbers. The numbers in the 'weekdays' array indicate the days of the week when the timer should run - all are optional, but at least one must be present. 1 indicates Sunday, 2 - Monday and so on. For example:

'weekdays':

     {

       'on':['first', 'last'],

       'weekdays':[1,3,7]

    }

requests the monthly timer to run on the first and last Sunday, Tuesday and Sunday of the months from the MONTHS ARRAY.

CodeRunner

Custom server code development, debugging and runtime execution in Backendless is made possible with a special utility/container called Backendless CodeRunner™which must be installed as an npm moduleThe CodeRunner SDK includes several command line tools enabling the process of debugging custom server-side code and subsequently deploying it to Backendless. It is important to note that CodeRunner makes it possible to debug custom code entirely on the developer's computer while the custom code is "plugged into" the API workflow in the cloud. This means any custom code whether it is an API service, API event handler or a timer, can be debugged using the traditional approaches (line-by-line code step through, variable inspection, console.log logging) by invoking that code through the centralized Backendless Cloud service. See the Development and Debugging section for details.

 

The process of developing custom business logic with Backendless is slightly different between API services and event handlers/timers. The only difference is the latter may include the step of code-generation which simplifies the workflow.

API Event Handlers and Timers


Backendless supports two approaches for developing custom business logic:

1. Expressive, code-driven approach. With this approach a developer bypasses code generation and builds the custom code for event handlers and timers by using the published APIs.
2. Declarative, console-driven approach. This approach relies on a code generator built into Backendless Console. Using the code generator developer selects the API events and configures schedules for timers for which he intends to add custom logic. The system automatically generates the code which can be downloaded for further development.
 
The diagram below illustrates the process:
backendless-development-process
where:
1. Developer generates code for event handlers and configures timer schedules using Backendless Console. The console includes a code generator which creates the place holder JS code for the API events and timers. The code can be downloaded as a project archive which includes project files for IntelliJ IDEAIDE. This step is optional, but it significantly simplifies the development process and saves development time.
2. Using an IDE developer writes custom business logic. The compiled code is passed to CodeRunner™, which is a command line utility used for local debugging of the custom business logic code. Developer can optionally attach to the CodeRunner™ process to set breakpoints in the code for the line-level debugging.
3. CodeRunner™ registers with the Backendless Cloud Service. It instructs Backendless to route the API and timer events to the developer computer for the debugged code.
4. Client application sends an API request.  Backendless determines if there is an event handler registered for the received API request and routes the event information to CodeRunner™.
5. CodeRunner™ receives the request and executes the code. Developer can inspect and debug invocations locally before the custom code is deployed to the production environment.

API Services


The workflow of developing API services is illustrated in the diagram below:
service-dev-process
where:

1. Developer downloads and installs the Backendless CodeRunner SDK. Using an IDE developer writes the service code. The compiled code is passed to CodeRunner™, which is a command line utility used for local debugging. Developer can optionally attach to the CodeRunner™ process to set breakpoints in the code for step-by-step debugging.
2. CodeRunner™ registers with the Backendless Cloud Service. It instructs Backendless to route the service invocation requests to the developer computer for the debugged code.
3. Client application sends a service API request.  Backendless determines if there is a service registered for the received API request and routes it to CodeRunner™.
4. CodeRunner™ receives the request and executes the code. Developer can inspect and debug invocations locally before the custom code is deployed to the production environment.

Project Setup

Setting up your development environment is very straight-forward with Backendless. There are two approaches for this task:

Generated Project Template

1. Login to Backendless Console and select your app.
2. Click the Download Project Template button at the top of the screen:
download-project-template-button.zoom60
 
3. In the displayed popup double-click JAVASCRIPT and click API SERVICE.
download-api-service-template.zoom80
 
4. Click the DOWNLOAD button to download a project template for your app.
5. Unzip the downloaded file into a directory. Let's call that directory "Project root".
6. Open a command prompt/Terminal window and change to the "Project root" directory.
7. Run the following command to install required dependencies:

npm install

8. Now the project is setup and is ready for development. Notice that the downloaded template already includes a sample service.

Manual Setup

Use the instructions below to configure a new project for developing custom server-side code in JavaScript with Backendless CodeRunner™:

1. Create a new directory which will contain your project.
2. Create a file named package.json with the following contents (make sure to substitute APPNAME with the name of your client project, the final value cannot have any spaces):

{
 "name": "APPNAME-servercode",
 "version": "1.0.0",
 "scripts": {
   "debug": "coderunner debug",
   "deploy": "coderunner deploy"
 },
 "devDependencies": {
   "backendless-coderunner": "^4.3.0"
 }
}

3. Create a file named coderunner.json with the following contents:

{
 "backendless": {
   "apiServer": "https://api.backendless.com",
   "msgBroker": {
     "host": "cl.backendless.com",
     "port": 6379
   }
 },
 "app": {
   "id": "APP-ID",
   "apiKey": "CODE_RUNNER_API_KEY",
   "exclude": [
       "package.json",
       "coderunner.json",
       "README.md",
       "servercode.iml",
       "servercode.ipr",
       "servercode.iws"
   ]
 }
}

4. Make sure to replace the following "substitution variables" with specific values:
 
APP-ID        - application ID for your Backendless backend.  You can obtain this value in Backendless console on the Manage > App Settings screen.
CODE_RUNNER_API_KEY - Code Runner API key from the Manage > App Settings screen in Backendless console.
 
5. Install Backendless CodeRunner dependency by running the following command from the root of your project directory:

npm install

Deployment Models

A “deployment model” combines API event handlers, timers and API services into a single group. The purpose of that grouping is to allow "fragmented" deployment of the business logic into the application. For example, you could separate the API services code from the event handlers and timers in separate projects and deploy them independently of each other.

 

It is important to keep the following in mind when working with business logic:

Deployment models are associated with languages. It means a model with the same name may exist with Java business logic and JS business logic, but it is not the same model – they will contain different business logic.
 
A model cannot contain more than one API event handler attached to the same "asset", which is a table/file/messaging channel. This means, for instance, if you have a "afterUpdate" event handler processing events for the "Person" table and the event handler is in model “X”, the same model “X” cannot have another "afterUpdate" handler for the table "Person".
 
Contrary to what’s stated above, you can have the same kind of an API event handler in different models.

 

When downloading code from the Backendless Console, you must identify the language and then the model for which to download the code.

 

When deploying code using CodeRunner (JS or Java), the deployment model name for the code must be specified either in the CodeRunner's configuration file or from the command line.

 

Deploying code into a model deletes all previously deployed code in the same model.

 

When the code is debugged locally (while being plugged into the Cloud servers), it takes precedence over the same code which may have been previously deployed. In other words, the Debug mode has higher precedence than the Production mode.

Models in Console

When creating an event handler or timer in the Backendless Console you will see a new drop-down list where you can select an existing model. To create a new one, simply type in the desired model and click the menu option to create it. The screenshot below is for creating a new event handler. You will see the identical "Model" drop-down in the "New Timer" popup:

event-handler-model

 

The Event Handlers screen in console displays the model name for every handler (see the MODEL column):

event-handlers-with-models.zoom70

 

When you download the generated code for event handlers and timers, you need to choose the model for which to download the code. The downloaded code contains only the event handlers and the timers for the selected model:

download-js-models.zoom70

 

Similarly, if you were to edit the code directly in the Backendless Console on the CODING screen, you need to choose the language and the model. The displayed tree of directories and files will contain only the code for the selected model:

js-model-coding.zoom80

Models with CodeRunner

CodeRunner lets you control what model the code is deployed to. When you download the Cloud Code from the Backendless Console, the generated project is already configured for the deployment model where the code is declared. For example, suppose an event handler is created in the "validator" model, then the downloaded project will reference the model in the coderunner.json file:

"app": {
 "id": ...,
 "apiKey": ...,
 "model": "validator",
 "exclude": [
     ....
 ]
}

. The referenced model is used when the code is either debugged locally or deployed to production.

 

When the code is being deployed to the Backendless servers, any other previously code in the same mode will be removed and replaced by the current deployment. CodeRunner informs about it with the following message:

IMPORTANT!
The business logic code will be deployed to model "MODELNAME".
Any business logic which is already deployed on the server in that model
will be removed and replaced with the code from your current project.
If this is an undesired behavior, stop now and set a different deployment model
either by using the --model argument or changing the model name in coderunner.json.
Would you like to continue? (Y/N)

Use the --model argument to change the name of the deployment model without modifying the configuration file:

npm run deploy -- --model YOUR-MODELNAME

 

Event Handler Chains

Deployment models enable support for assigning multiple event handlers to an API event. This approach promotes better design for cloud code with a clean division of responsibilities between the event handlers. It is important to note that any data received as arguments for an API call is passed from one event handler to another. If an event handler placed at the beginning of the chain makes a change to an argument (or the return value), then all other event handlers down the chain will get the modified value. The diagram below illustrates event handler chains:

api-flow-multiple-handlers

Backendless Console includes a visual tool enabling the control of the placement order. Suppose an application contains event handlers as shown in the image below:

event-handlers-with-models.zoom70

 

Notice there are three beforeCreate event handler with the generic (*) context, which means the event handlers will be executed when an object is created in any of the application tables. Two of the handlers are written in JavaScript and the third one is in Java. When Backendless detects you have more than one event handler applicable to a particular context, it displays the "handler ordering" icon:

event-handler-ordering-icon.zoom64

 

Clicking the icon opens up a popup where you can control the execution order for all applicable event handlers. Notice that the invocation chain can combine event handlers written in different languages:

Debugging

Backendless custom business logic can be debugged locally on the developer's computer while it is plugged into the Backendless Cloud service. This means your business logic code can be invoked through the API accessible via Backendless Cloud while the custom code runs on your computer. To develop and debug custom business logic developers must use CodeRunner™ SDK.

 

If you develop API services, make sure to follow the instructions from the Project Setup section. If you develop API event handlers and/or timers and used the Business Logic code generation built into Backendless console, CodeRunner™ project configuration files are already included into the generated ZIP file.

 

To install and run CodeRunner™, open a command prompt window and change the current directory to the root directory of your project (the directory containing the coderunner.json and package.json files)

Run the following command to download and install CodeRunner™:

npm install

 

Run the following command to start CodeRunner™ in the local debug mode:

npm run debug

 

Once coderunner starts, it produces the following output:

your-comp-name:bin username$ npm run debug
 
> PROJECTNAME@1.0.0 debug /path-to-your-project
> coderunner debug
 
2016-03-23T13:12:51.136Z - CodeRunner(tm) Backendless Debugging Utility                                                                    
2016-03-23T13:12:51.139Z - Copyright(C) 2017 Backendless Corp. All rights reserved.
2016-03-23T13:12:51.139Z - Version 1.2.1
2016-03-23T13:12:51.367Z - Starting Debug Code Runner...
2016-03-23T13:12:51.393Z - Building Model..
2016-03-23T13:12:51.397Z - Model Build completed: event handlers: 2, timers: 0, custom types: 1, services: 0, errors: 0
2016-03-23T13:12:51.399Z - Registering Code Runner on https://api.backendless.com
2016-03-23T13:12:51.777Z - Runner successfully registered.
2016-03-23T13:12:51.777Z - Registering Model on https://api.backendless.com
2016-03-23T13:12:51.950Z - Model successfully registered
2016-03-23T13:12:51.950Z - Waiting for Server Code tasks..

 

Attaching IDE Debugger

CodeRunner can also be configured to allow debugging at the source code level in a JavaScript IDE/debugger. To configure your project for local debugging in IntelliJ IDEA/WebStorm:

1. Click Edit Configurations... as shown below:
edit-configuration
2. On the top left of the Run/Debug Configurations dialog, click the + sign to add a new configuration.
3. Choose Node.js (If you don't see it in the list you have to install Node.js plugin first):
adding-node-js
4. Name the new configuration "CodeRunner".
5. IntelliJ IDEA/WebStorm should detect your node interpreter (most likely in /usr/local/bin/node).
6. Under JavaScript file enter node_modules/backendless-coderunner/bin/coderunner. The complete configuration dialog should look as shown below:
finished-config
7. Click Apply, then OK.

 

To start the new debug configuration of CodeRunner, make sure it is selected in the configurations and click the debug icon:

run-debug-coderunner-js

Troubleshooting

How do I debug after I have deployed business logic to the Backendless Cloud?

When the code is deployed, all console.log calls, in addition to the CodeRunner service messages, are redirected to the Backendless.Logging API calls and will be saved in Backendless log files. This results in a file with all the logged messages created in the root/logging folder of the file space allocated to the application. For more details on Backendless logging see the Backendless.Logging documentation.

 

Consider the following example of a dummy Heartbeat timer :

  //...
  execute: function() {
    console.log("I'm alive!");

    Backendless.Logging.getLogger('heartbeat').debug("I'm alive!");
  }

 

After this timer runs in production, you will find the following log messages in the logs:

timerlog

ServerCode works well in the DEBUG mode, but does not when deployed to the Cloud

Make sure the code is getting invoked when a triggering event is raised. It could be an API call, a timer event, a custom event or a service invocation. To verify this, open the Backendless Console, go to the Files section and check the files in the logging directory. You should see log messages in the SERVER_CODE logging category. If you do not see any messages for that logging category and you are sure that the corresponding business logic triggering event is raised, please contact the Backendless support forum to resolve the issue.

 

The log messages may contain errors raised during business logic execution and this information may help you in diagnosing and fixing the problem.

 

If you see that the Business Logic execution was interrupted (for example, you see some of the console.log messages, but don't see others), that means that you have some undeclared asynchronous IO operations, which were interrupted by the CodeRunner. This happens when asynchronous operations started by your code are not properly returned to CodeRunner. Business logic method must return a Promise to CodeRunner which should be resolved only when all asynchronous jobs are finished.

 

This is explained in details in the Sync vs Async chapter.

The size of my code is bigger than allowed in the current payment tier

You can decrease the application deployment zip size by adding exclusion filters to your app.files config parameter which is located in the {PROJECT_DIR}/coderunner.json file. This parameter contains an array of inclusion/exclusion patterns forming the content of the server code deployment. The default value of the parameter is a pattern which means 'include all files under {PROJECT_DIR}/app folder:

"app": {
 "files": [
    "app/**"
  ]
}

CodeRunner will automatically include into the deployment all the non-dev dependencies from the node_modules folder listed in the project.json file. Notice that backendless-coderunner is a dev dependency. When deploying your code to the client, all dev dependencies are excluded.

 

You can run the deployment with a few additional parameters which will give you more control of the deployment process:

npm run deploy -- --verbose --keep-zip

 

When using the --verbose parameter, you will see all applied deployment patterns which are declared in app.files configuration parameter.

 

When using the --keep-zip parameter, once the deployment is complete (or even after you attempt to run it), you will find the deploy.zip file in the {PROJECT_DIR} folder. You will be able to check its size and the contents.

 

The cumulative size of the deployed code (which includes all the dependencies) is limited to 2 megabytes in the free tier of Backendless Cloud. The limit can be expanded by purchasing a Function Pack from the Backendless Marketplace. To reduce the deployment size, consider minimizing the installation of the dependencies to only the ones your the code needs/uses. Additionally, you may apply additional filtering to identify what should be skipped from the depended module's folder. For instance, if {some_module} has the examples folder you may omit its inclusion by adding the following exclusion pattern in app.files - !node_modules/some_module/examples

 

Some of this information can be found in the README file of the generated and downloaded from the console servercode project. There are several examples of app.files config parameter tweaking.

Deployment

Backendless CodeRunner™ includes a command line utility enabling deployment of custom server code to Backendless. Once the code is deployed, it is immediately "activated", which means:

 

API Services - published service code is packaged as API Services. REST endpoints and native SDKs are generated.
API Event handlers - attached to the API invocation chain. The code will be triggered when the corresponding APIs are invoked by the clients.
Timers - timers are scheduled and begin their execution according to the declared schedules.

 

To deploy the code:Run the following command from the root directory of the project:

npm run deploy

 

 

You can verify the production deployment in Backendless Console. API services appear on the Business Logic > API SERVICES screen under the DEPLOYED SERVICES heading:

deployed-services-heading

For event handlers and timers, open the Business Logic section and navigate to either EVENT HANDLERS or TIMERS. Event handlers and timers deployed to production appear with the green color code:

event-handlers-in-prod.zoom80

 

 

The cumulative size of the deployed code (which includes all dependencies) is limited for the Backendless Online deployments to 2 megabytes. It can be expanded by purchasing a Function Pack from the Backendless Marketplace. This limitation does not exist in the Managed Backendless and Backendless Pro versions of the service.

Sync vs Async Code

The node.js environment does not allow synchronous blocking IO (networking) operations. As a result, any custom code which uses either Backendless API or communicates with external hosts, must use the asynchronous version of APIs. However, there is a special consideration for custom server code which uses asynchronous operations. Specially, any code for which the following two scenarios are true, must return a JavaScript Promise:

 

1. performs an asynchronous operation and needs its result to be used as the return value of the event handler/API service;
 
or
 
2. performs an asynchronous operation which continues to run after the custom code completes its execution. In this case returning a Promise from custom code is an instruction to CodeRunner to wait for the asynchronous operation to finish.

 

When custom server code returns a Promise, CodeRunner will wait for its completion and will use its resolved/rejected value as the result of event handler or API service.

 

Execution Flow


The diagram below illustrates the decision flow the CodeRunner goes through when receives a return value from the custom server code (applies to event handlers, timers and API services):

code-runner-promise-analysis

Example


Consider the following example illustrating the approach. Suppose there is a blog management app. There is a task to track the number of comments on a blog post. This can be done by adding an afterCreate event handler to the Comment table, retrieve a related Post object, increment it's comments field and save it. That means the code has to perform two asynchronous operations in a row ("get post" and then "save post"). Also, it means the code must return a Promise from the event handler letting CodeRunner™ know that the event handler has completed its task.

 

You will find four different implementations with identical logic for this scenario, which should help in understanding how Promises must be used in custom server code in Backendless.

 

All implementations are configured as asynchronous event handlers (see the "true" argument in the afterCreate API method call - the last last in all examples). This approach provides several benefits in this particular case:

asynchronous event handlers run in background without blocking main API invocation chain (comment insertion in our case);
the Promise's "resolve" value is not important because it is never sent to the server (for the reason described in the first bullet).

 

Returning Promise with asynchronous Backendless API wrapped into it:

backendless-async-approach.js

Backendless.ServerCode.Persistence.afterCreate('Comment', function (req) {
    var PostsStore = Backendless.Data.of('Post');

    return new Promise((resolve, reject) => {
        var onError = (err) => {
            reject('Unable to update post comments counter. Got an error : ' + err.message)
        }

        PostsStore.findById(req.item.post, new Backendless.Async(
            post => {
                post.comments++;

                PostsStore.save(post, new Backendless.Async(resolve, onError))
            },
            onError
        ))
    });
}, true)


Enabling promises in Backendless API, then returning result of the Backendless Data Service "save" method - which will be a promise, since the promises are enabled:

backendless-promises-approach.js

Backendless.enablePromises();
Backendless.ServerCode.Persistence.afterCreate('Comment', function (req) {
    var PostsStore = Backendless.Data.of('Post');

    return PostsStore.findById(req.item.post)
        .then(post => {
            post.comments++;

            return PostsStore.save(post);
        })
        .catch(err => {
            return Promise.reject('Unable to update post comments counter. Got an error : ' + err.message)
        });
}, true)


Event handler delegates to persistence item object, which modifies its own property and saves itself:

persistence-item-approach.js

var Post = require('./post');

Backendless.ServerCode.Persistence.afterCreate('Comment', function (req) {
    return Post.findById(req.item.post)
        .then(post => post.incrementCommentsCounter())
        .catch(err => {
            Promise.reject('Unable to update post comments counter. Got an error : ' + err.message);
        });
}, true)

post.js

'use strict'; //required if you want to use es6 classes

class Post extends Backendless.ServerCode.PersistenceItem {
  incrementCommentsCounter() {
    this.comments++;
    
    return this.save().
  }
};

module.exports = Backendless.ServerCode.addType(Post);

External Hosts

Requests to external hosts must be sent asynchronously - the node environment does not allow blocking network IO calls. The following example demonstrates the approach:

Backendless.ServerCode.Persistence.beforeCreate('*', function(req) {
  const http = require('http');
 
  return new Promise((resolve, reject) => {
    http.get('http://some.external.host.com/', (res) => {
      let body = '';
 
      res.on('data', (chunk) => body += chunk);
      res.on('end', () => {
        req.item.apiCallResult = body;
 
        resolve();
      });
 
      res.resume();
    }).on('error', reject);
  });
});

 

Node Modules

Custom business logic in JavaScript can take advantage of 3rd party node modules. To add a module:

 

1. Install a module you want to use with --save option. It is important to use the --save option so that the module is installed at the project level (rather than globally). When a module is installed at the project level, it is added to project.json into the non dev 'dependencies' list. As a result, when the code is deployed to Backendless, the module will be included into the deployment scope.
 
For example, the command below installs the "lodash" module:

npm install lodash --save

2. Once installed, use the module in the code as usual. For example, the lodash module installed in the step above can be used to set the name of the creation item to camelCase using the lodash library:

var _ = require('lodash');

Backendless.ServerCode.Persistence.beforeCreate('*', function(req) {
  req.item.name = _.camelCase(req.item.name);
});

3. Be aware of the module size. The free tier of Backendless limits the deployment size to 5 mb. it can be expanded to 20mb with a function pack available in Backendless Marketplace. To reduce the deployment size, consider minimizing the installation of the dependencies to only what the code needs. For example, since the code above uses only the camelCase function, it is possible to install only that module as shown below:

npm install lodash.camelcase --save

Then use the module as:

var camelCase = require('lodash.camelcase');

Backendless.ServerCode.Persistence.beforeCreate('*', function(req) {
  req.item.name = camelCase(req.item.name);
});

Logging

Custom server-side code can log information using the API provided by Backendless SDK for JavaScript.

/* global Backendless */

Backendless.ServerCode.Persistence.beforeCreate('Order', function(req) {
    Backendless.Logging.setLogReportingPolicy( 1, 1 );
    var logger = Backendless.Logging.getLogger( "MyLogger");
    logger.info( "informational log message" ); 
    logger.warn( "warning message" ); 
    logger.debug( "debug message" ); 
    logger.error( "error message" ); 
    req.item.customername = req.item.customername.toUpperCase();
});

It is important to note that using logging in custom server-side code requires a special configuration of the logging policy using the following API. This is required to avoid creation of a log buffer and an additional thread responsible for submission of the log messages to the Backendless servers:

Backendless.Logging.setLogReportingPolicy( 1, 1 );

 

The sample code above logs four different messages, each for separate logging level. The log levels are organized into a hierarchy:

Log level

Includes messages from log levels

debug

debug, info, warn, error

info

info, warn, error

warn

warn, error

error

error

The log file is saved in the /logging directory of the Backendless File Service storage.

The following logging APIs are available:

Retrieving Logger object:

Backendless.Logging.getLogger( loggerName );

Log a message with the debug level:

loggerObject.debug( message );

Log a message with the info level:

loggerObject.info( message );

Log a message with the warn level:

loggerObject.warn( message );

Log a message with the error level:

loggerObject.error( message );