Quick Start Guide

Top  Previous  Next

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 API Engine. You will be able to experience REST-based method invocations and download generated client SDKs.

Requirements

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

1.Download and install Node.js version 4 or above and NPM.
2.Create new project, where you can put your Backendless Server Code logic. See the Project Setup section of the guide for details.
3.Create a Backendless account, register for your free account at https://develop.backendless.com/#registration
4.Select JS in the Business Logic section of Backendless Console.
Click Business Logic.
Click Users under Service Event Handlers.
Select JS for the Language dropdown.
js-selection.zoom50

Service Design

The service developed in this quite is a trivialized shopping cart. The APIs generated by API Engine from the code will allow adding items to the shopping cart, retrieving and deleting the contents of the cart. It is important to remember that 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, cache, etc). In the design of the code, the state is the actual shopping cart object and it is stored in Backendless Cache. Every method call retrieves the cart from cache, updates the cart object as necessary and then places it back into cache. The code identifies every shopping cart by its logical name, thus the same service will be able to handle multiple cart objects.

Service Development

Service implementation will consist of four classes. Two of them, ShoppingItem and Order, are used to represent data objects stored in Backendless Data Service. Additionally, there is ShoppingCart class which is used by the service implementation. The service implementation is in the ShoppingCartService class. You will start with the ShoppingItem class:

 

1.Create the /app/models directory. It 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 jsdoc annotation is very important. It is parsed and analysed by CodeRunner™ and is used for proper service REST 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;
    });
  }
}

Backendless.enablePromises();

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.enablePromises();
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 jsdoc annotations are used by CodeRunner™ to create service API declaration, including Swagger API document.

 

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

To start a debugging session, run the following command 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 > Hosted 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.

Service Deployment (to Backendless)

To deploy the service to the Backendless servers, run the following command from the project's root directory:

npm run deploy

The command deploys the service to the server.

 

Working with Service

The instruction below are applicable to both modes of service execution (debug and deployment).

1.Once the service is in the debug mode or uploaded to Backendless, it is processed and the service REST APIs along with the native SDKs are generated. Backendless Console will then display available operations in API Inspector. For example, the following screenshot shows the service for this guide in the debug mode:
js-service-in-debug.zoom50
 
2.Click the pop-out icon for API Inspector to open it in an pop-up window:
js-service-inspector.zoom70

 

3.The API Inspector lists all operations available in the service. To see details of an operation, click its name. For example, click the POST /Item method. The details of a method include a listing of its parameters. Backendless automatically inserts the following parameters for every method: application-id, secret-key and user-token. The first two are mandatory, the values for them can be obtained from the Manage > App Settings screen of the Backendless Console. The user-token parameter is optional. If it is present, it must contain the user token value obtained from the result of the login API call. All three parameters must be formatted as HTTP headers.
 
In addition to the parameters injected by Backendless, the user interface lists any formal method arguments and their corresponding schema. For example, the POST /Item method has an argument which combines the shopping cart name and the item being added to the cart. To remind you, the JavaScript addItem method declaration and implementation are:
  /**
   * @param {String} cartName
   * @param {ShoppingItem} item
   * @returns {Promise.<void>}
   */
  addItem(cartName, item) {
    return this.addItems(cartName, [item]);
  }

When Backendless generates REST API, it combines formal method arguments into a single JSON object which must be sent in the body of the request. For the addItem method, the REST operation argument is:
{
  "cartName": "string",
  "item": {
    "objectId": "string",
    "product": "string",
    "price": 0,
    "quantity": 0
  }
}

 

4.To invoke the addItem method from API Inspector, you will need to provide the argument value. The starting point may be clicking the model schema area so its contents are copied to the argument value area:
click-schema.zoom50

 

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

 

6.Click Try it out to make an invocation of the method:
try-it-now.zoom50

 

7.Notice the output displays a complete curl request which executes the same REST API request. There is also Request URL, Response Code and Response HTTP headers.

 

8.To see the contents of the shopping cart, click the GET /Items method:
get-items-method.zoom50

 

9.Make sure to enter a value for the cartName parameter. The value must be entered in double quotes as shown below:
shopping-cart-name

 

10.Click Try it out! to invoke the GET /Items method.
get-items-result.zoom50
 
The result is a JSON array of ShoppingItem objects (so far there is only one in the collection). This maps closely to the formal response type of the getItems() method in JavaScript:
  /**
   * @param {String} cartName
   * @returns {Promise.<Array.<ShoppingItem>>}
   */
  getItems(cartName) {
    return ShoppingCart.get(cartName, true).then(cart => cart.getItems());
  }

Please let us know how we can improve the documentation by leaving a comment. All technical questions should be posted to the Backendless Support forum. We do not respond to the technical questions on the documentation pages.: