Creating a Mobile-Optimized REST API Using Oracle Mobile Cloud Service – Part 1 API Design

Introduction

To build functional and performant mobile apps, the back-end data services need to be optimized for mobile consumption. RESTful web services using JSON as payload format are widely considered as the best architectural choice for integration between mobile apps and back-end systems. At the same time, many existing enterprise back-end systems provide a SOAP-based web service application programming interface (API). In this article series we will discuss how Oracle Mobile Cloud Service (MCS) can be used to transform these enterprise system interfaces into a mobile-optimized REST-JSON API. This architecture layer is sometimes referred to as Mobile Backend as a Service (MBaaS). A-Team has been working on a number of projects with MCS to build this architecture layer. We will explain step-by-step how to build an MBaaS, and we will  share tips, lessons learned and best practices we discovered along the way. No prior knowledge of MCS is assumed. In this first part we will discuss how to design the REST API.

Other parts in this series include:

This series will be followed by a number of articles describing how to consume our mobile-optimized API in mobile clients build with various technologies like Oracle MAF, Oracle JET, native Android or native iOS.

Main Article

Design Considerations

Let’s start with the first challenge: how do you design an API that is truly optimized for mobile apps? A common pitfall is to start with the back-end web services, and take that back-end payload as a starting point. While that may limit the complexity of transformations you have to do in MCS it leads to an API which is everything but optimized for mobile. This brings us to our first recommendation:

The REST API design should be driven by the mobile developer.

He (or she) is the only one who can combine all the requirements, information and knowledge required for a good design:

  • he designs and builds the various screens, knows the supported form factors and knows exactly which data should be retrieved for which screen.
  • he knows the requirements for working in offline mode, and knows how this can be supported and implemented using his mobile development tool set.
  • he is responsible for data caching strategies to optimize performance in both online and offline scenarios
  • he decides which read and write actions can be performed in a background thread not impacting the user-perceived performance.

To illustrate how the above aspects impact the design of the API, we will introduce the sample “human resources” app that we will use throughout this article series. Lets start with the three screen mockups our API should support:

mockups

A first design for the read (GET) resources can look like this

  • /departments: returns list of departments containing department number and name. A “quickSearch” query parameter might be added to support filtering if this cannot be implemented or is undesirable to perform on the device because of the size of the dataset.
  • /departments/{departmentId}: returns all department attributes for the department matching the {departmentId} path parameter and a sub list of all employees working in this department consisting of id, firstname and lastName attributes.
  • /employees/{employeeId}: returns all employee attributes for the employee matching the {employeeId} path parameter.

As you can see this design is driven by the screens. It allows for “on-demand” data loading, using lean resources that only send the absolutely neccessary set of data across the wire, minimizing payload size and maximizing performance. This design is clearly optimized for online usage of the application.  If the mobile developer has to support an offline usage scenario, he would need to do the following to prepare the app for offline usage, storing all data locally on the device:

  • Call the /departments resource
  • Loop over all the departments returned, and for each department call the  /departments/{departmentId} resource.
  • Loop over all employees returned, and for each employee call the /employees/{employeeId} resource

Needless to say that this is not a very efficient way of data loading for offline usage. It can easily result in hundreds of REST calls causing a delay of many minutes to prepare the app for offline usage. So, to support offline usage, it would be handy to add a query param “expandDetails” to the /departments resource which when set to “true” would return all department and employee data in one roundtrip.

Of course there are limits to the amount of data you can offload to your phone or tablet. You sure don’t want to store the complete enterprise back-end database on your phone!  So, in our sample, depending on the number of departments in the back-end database, we might need additional query parameters to allow the mobile user to select a specific subset of departments for offline usage.

At this point you might think, no worries, I only have to support an online usage scenario. Well, not too fast… A-team has learnt from our experiences in mobile engagements that more aggressive upfront data caching strategies might still be needed for various performance-related reasons:

  • The app might be used in parts of the world where network connectivity and network bandwidth is unreliable so users prefer to have a longer waiting time at app startup to prevent network hick ups while using the app.
  • The performance of the back-end API calls might turn-out to be too slow for “on-demand” data loading.
  • The REST-JSON transformations in MCS are typically very fast. However, the required JSON payload might require assembling data from various back-end data sources, degrading performance when looping over result sets is needed to get additional lookup data.

Let’s clarify this last point with an example. Assume there is a back-end HR interface that returns all employee data except for the job title (only job code is returned). Another “lookup” interface returns the job details including the job title. In MCS, a loop over all employees is then needed, and for each employee a call to the jobs “lookup” interface is needed to add the job title to the payload. If the lookup call takes just one second, the performance loss can already be significant with tens of employees returned.  In such a situation you have two options: use the object storage facility in MCS to cache the job lookup data to prevent expensive calls for each employee, or modify the JSON payload to pass only the job id and do the job lookup on the mobile device. This last option would require an additional /jobs resource in your design that returns all job titles for you to cache on the device. In this article series, we will implement both options for educational purposes, we will use MCS caching, but we will also provide an additional resource to return job id’s and their titles as this is needed since our screen design shows a drop-down list to assign a job to an employee.

In summary: various user groups might have different data caching needs, and initial data caching strategies might need to be revisited for performance reasons.

We can distill an important lesson from the above:

Your REST API design should be flexible in terms of the data caching options it can support.

Creating the API Design in MCS

Developers building traditional enterprise system interfaces often follow a design-by-contract approach. In the XML-based web services world, this means the use of XML Schema’s (XSD’s) and Web Service Definition Language (WSDL) to formally specify the interfaces. However, mobile developers live in a different world, they typically just think in JSON payloads. Fortunately, there are emerging standards in the REST-JSON world like RAML (Restful API Modeling Language) and SWAGGER. Oracle MCS uses RAML for specifying the API design. The nice thing about RAML is that it both supports formal REST specifications using the JSON Schema standard, but also allows you to define request and response payloads using sample data. Sample payloads can be changed easily, reflecting the agile and flexible nature mobile developers are used to when working with JavaScript-based (mobile) frameworks like Cordova, Angular, Ionic, Ember etc. Moreover, by using sample data while specifying the API design in MCS you can instantly provide the mobile developer with a mock data server so they can start building the app even before your API design has been implemented in MCS.

So, as we started off by recommending to have the mobile developer drive the design, we should facilitate him with a documentation format he is comfortable with: sample request/response payloads, together with the resource path, path and query parameters, the HTTP method and a short description. We will create this design in MCS by executing the following steps:

1. Create a Mobile Backend

A mobile backend is a container of one or more API’s used by one or more mobile applications. API’s in MCS are always accessed from the outside through a mobile backend. To create the mobile backend, login to Oracle Mobile Cloud Service and click on the Development tab.

DevHome

On the development home page, click on the Mobile Backend icon. Click on button New Mobile Backend and enter values as shown below.

NewMobileBackend

Click the Create button,

2. Specify the Human Resources API

We will now specify the REST resources and their HTTP methods that make up the human resources API. Click on the Open button of the newly created HR1 mobile backend, go to the APIs menu option and click on the New API button. Enter the values of your new API as shown below.

NewAPI

Click the Create button, this opens up the General tab of your new Human Resources API.

ApiGeneral

On this tab, set the Default Media Type to application/json. We can now start specifying the various REST resources that together form our API design. Click on the Endpoints tab, then click on the New Resource button, and create a new resource named /departments.

NewResourceDepartments

We need to define the HTTP methods that we want to support on this resource. To do this, we click the Methods link at the right and then we click the Add Method button and choose GET.

MewMethodGET

Since we want to support both online and offline scenarios, we click the Add Parameter button and we add a boolean query parameter named expandDetails as discussed above. When set to true, this will return all department data including nested employee data for each department. When set to false or not specified, we only return a list consisting of department id and name.

QueryParam

To specify a sample response payload, we click on the Responses tab.The standard response status code 200 is already selected as well as the suggested return media type which is based on the default media type that we specified on the General tab. We now enter a sample response payload as shown below.

DepartmentsResponseSample

To support creation of a new department and update of a department, we also add a POST and PUT method to this same resource. The sample payload for both the request and response payload for these two methods looks like this:

{
    "id": 80,
    "name": "Sales",
    "managerId": 145,
    "locationId": 2500
}

When designing REST resources it is common practice for PUT/POST methods to return the same object(s) as sent in the request, possibly updated with server-derived values like the ID.

Note that this sample payload indicates you can only insert or update one department at the time. When implementing the API you can easily choose to support both a single department insert/update or an array of departments. For a quick overview of the methods implemented on a resource, you can use the description field.

We now create a nested resource under /departments to retrieve the details of a single department and to remove a department. (Our user interface design doesn’t seem to support deletion but for completeness we will add it anyway), Nested resources are created by clicking the plus icon at the left of the parent resource. We then extend the /departments path with a path parameter named id which should be enclosed in curly brackets.

DepartmentDetailsResource

The GET method should return a sample payload like this:

{
    "id": 80,
    "name": "Sales",
    "managerId": 145,
    "locationId": 2500,
    "employees": [
        {
            "id": 145,
            "name": "John Russel"
        },
        {
            "id": 146,
            "name": "Peter Fletcher"
        }
    ]
}

The DELETE method does not need sample payloads as the department id and HTTP verb provide enough information to execute the delete action.

In a similar way, we can create the /employees resource with GET, PUT and POST methods, and the /employees/{id} resource with GET and DELETE methods. Finally, we need the /jobs resource with a GET method to return a list of jobs. When we are done, and we click on the Compact Mode On button, the endpoints tab should look like this.

CompactMode

3. Publishing the Human Resources API Design

Now that we have completed the design we want to share this with the various stakeholders. One way to do this is to give them (read-only) access to MCS so they can inspect the API design themselves. However, for non-technical people that might be a step to far, so you probably want to publish the API in an easily human-readable format.Well, it turns out that MCS has already done that for you! While entering the various resources and methods, all information entered was directly stored in a RAML document, which through a strict indentation scheme is structured enough for computers to understand but at the same time easily read by humans. To view the RAML document you can click on the most right “toggle” icon which allows you to switch between RAML source mode and design mode.

RAML

The icon with the down arrow can be used to download the RAML document so you can publish/distribute it as desired. Note that if other people have comments and would like to make some changes to the design, they can send you a modified version of the RAML document, which you then can copy and paste in the RAML source mode. You can also create a brand new API design in MCS by directly importing an existing RAML document.

Providing a Mock Data Service for MCS API Design

Time-to-market is critical to stay competitive in this fast-moving mobile world. To not loose valuable development time, it is common practice to provide a server with mock-up data once the initial API design is fleshed out. This allows the mobile developer to already start building out the mobile user interface, while the MCS developer is still busy implementing the real API. Again, the good news is that by using Oracle MCS you got this mock data service for free. By default, calling an MCS REST resource that has not been implemented yet, will return the sample payload that we defined while specifying the API design.

We can use a powerful REST client like Postman to test this out. First, we need to find out the full REST resource path. We can find this by going to the General tab for our Human Resources API.

ResourcePath

The resource path shown here can be appended with the specific REST resources we defined in our API, for example /departments or /departments/10. To be able to access the MCS API, without user credentials, we also need to allow anonymous users access. We can do this on the Security tab by selection the ON option.

AninymousAccess

We also need to specify two HTTP request headers, one specifying the mobile backend ID, and one specifying the anonymous user key. The values for these header parameters can be found on the mobile back end overview page by expanding the Keys section.

MobileBackendKeys

With this information we can set up our request header parameters as follows:

  • Oracle-Mobile-Backend-Id = fd27fb4f-90e1-4d4d-9e3f-ca4befdebf2c
  • Authorization = Basic UFJJTUVfREVDRVBUSUNPTl9NT0JJTEVfQU5PTllNT1VTX0FQUElEOml6LmQxdTlCaWFrd2Nz

When we enter this information in Postman and hit the Send button our sample payload is returned correctly.

Postman

We can pass on this information to the mobile developer so he can start accessing our MCS API using mock data. As you noticed, this is a static mock data service, the payload returned cannot be changed based on the value of path or query parameter or the request payload. However, in the next part of this article series we will see how we can easily implement a more “dynamic” mock API taking into account the value of the expandDetails query parameter.

Implementing the Design

In part 2 of this article series we will start implementing this design using Oracle MCS. We will integrate with a back-end interface that is provided in the form of an ADF BC SDO SOAP web service. In subsequent parts, all aspects of the MCS implementation will be discussed, including Node.js asynchronous Javascript programming, MCS caching options, sequencing multiple backend calls, error handling, logging and security. No prior knowledge of MCS will be assumed. Stay tuned!

Add Your Comment