. . .

Best practices

This page contains some common use cases and proven practices when using ApiOmat.

Versioning

Most changes in the API are caused by adding more classes and attributes to a module. ApiOmat itself can handle these changes, even if the data is stored in MongoDB. Also, running apps with old SDKs will continue to work, because unknown data is ignored during requests. This implies that even pure REST requests will still work with missing or unknown attributes; these will be ignored by the ApiOmat REST API.

Changes in class or attribute names cannot be managed automatically, because data conversions or deletions in the database cannot be made without manual intervention. Existing apps therefore won't work as expected after this kind of breaking changes.

If there are really breaking changes to implement, best practice is to leave the old module as is and create a second one. Since ApiOmat 3.3 it's possible to create a new version of your module. The new module version should copy all classes and attributes of the old one, so it can access the dataobjects that were already persisted.

Caching

ApiOmat can connect to legacy systems using a variety of APIs. Although these non-mobile optimized systems can be made accessible for mobile devices very easily using ApiOmat, usually some actions need a caching mechanism to ensure proper performance on the users' side.

A typical use case would be fetching a large list of detailed results from a legacy system. Imagine the legacy system would only support calls which return a list of reduced details of entities:

List<Customer> getAllCustomersList()

The returned customer list will only contain the customers username, but all other attributes (details) of the customer such as the firstName, lastName, etc will be null. To get these details, a second request is needed which requires the name of a single customer:

Customer getCustomerDetails(String name)

Retrieving a list of thousands of customers would usually last several minutes depending on the legacy system. To speed up this use case, fetching the customer details could be cached in ApiOmat's MongoDB using only a few straightforward lines of code:

public List<Customer> doGetAll( String query, Request r ) {
List<Customer> returnList = new ArrayList<>( );
/* get customers from legacy system */
List<Customer> customers = searchCustomersInLegacy( query );
/* look for details in cache first and fetch from legacy system if not found */
customers.forEach( c -> {
IModel<?> cachedCustomer = this.model.findByForeignId( c.getForeignId( ), "Ordinary", "CustomerCache", r );
if ( cachedCustomer == null ) {
Customer detailCustomer = searchCustomerDetailInLegacy( c.getForeignId( ) );
returnList.add( detailCustomer );
/* save new in cache */
cachedCustomer = convertToCached( detailCustomer );
((CustomerCache)cachedCustomer).save( );
}
else {
returnList.add( convertToOriginal( cachedCustomer ) );
/* maybe check creation date and drop model from cache afterwards... */
}
} );
return returnList;
}

Everything that is needed is an additional non-transient class which will be used for storing the cached data. The example above used two convert methods to transform an object between both representations.

Automatic SDK download

Downloading frontend SDKs to your development enviornment can be invoked via a REST request in the same fashion as all other actions made in ApiOmat. To learn more, take a look at this article.

Internationalization / i18N

An internationalization of texts can be realized by using maps. The language code is the key and the text is associated value:

{"language_code1":"lang1_text","language_code2":"lang2_text",....}

The following examples demonstrate this.

Android
final String language_code="de";
final Conference conference = new Conference();
 
Map<String, String> i18n_name = new HashMap<String,String>();
i18n_name.put("de", "Wie benutze ich properties");
i18n_name.put("en", "How to use properties");
conference.setI18n_name(i18n_name);
 
Map<String, String> i18ns_guests = new HashMap<String,String>();
i18ns_guests.put("de", "Gastsprecher: John Doe");
i18ns_guests.put("en", "Guest speaker: John Doe");
conference.setI18n_guests(i18ns_guests);
 
conference.saveAsync(new AOMEmptyCallback() {
@Override
public void isDone(ApiomatRequestException exception) {
//if exception != null an error occurred
if(exception == null) {
Log.i ("Conference",conference.getI18n_name().get(language_code));
Log.i ("Conference",conference.getI18n_guests().get(language_code));
}
}
});
JavaScript
var LANGUAGE="de";
function showConference (conference)
{
console.log(conference.getI18n_name()[LANGUAGE]);
console.log(conference.getI18n_guests()[LANGUAGE]);
}
 
function initConference ()
{
var conference=new Apiomat.Conference();
var i18n_name = {
"de" : "Wie benutze ich properties",
"en" : "Talk about properties"
}
conference.setI18n_name(i18n_name);
var i18ns_guests = {
"de" : "Gastsprecher: John Doe",
"en" : "Guest speaker: John Doe"
}
conference.setI18n_guests(i18ns_guests);
var saveCB = {
onOk: function () {
showConference(conference);
},
onError: function (error) {
console.log("error "+error);
}
}
conference.save(saveCB);
}