Blog

How to Load Objects From an MBaaS Storage Using Data Retrieval API

by on April 1, 2020

Loading data objects from the Backendless persistent storage is a fundamental operation a large majority of the online/mobile applications require. Backendless Data Retrieval API is simple, yet very powerful.

As you will learn in the course of this series, the API provides the following capabilities:

All the features listed above are based on the core data loading API. Consider the example below, but make sure your Backendless backend contains some data. The example in this post (as all the other related posts highlighting the data retrieval features) will use the Restaurant To-Go App schema. You can populate the backend with data using the import feature:

  1. Download the file from the URL below:
    https://backendless.com/documentation/samples/restaraunt-app-tables-with-data.zip
  2. Follow the instructions on how to import schema/data into a Backendless application. You will need to import the file from the step above.

The sample code below will be fetching data from the following table:

Restaurant Table With Data

Additionally, the example below will be using the code generated by Backendless.  To get the code make sure to follow the instructions describing how to generate client-side code based on the backend’s database schema.

    private static void fetchingFirstPageAsync() {
       final long startTime = System.currentTimeMillis();
    
       final AsyncCallback<List<Restaurant>> callback = new AsyncCallback<List<Restaurant>>() {
           @Override
           public void handleResponse(List<Restaurant> restaurants) {
               Log.i(TAG, "Loaded " + restaurants.size() + " restaurant objects");
    
               for (Restaurant restaurant : restaurants) {
                   Log.i(TAG, "Restaurant name = " + restaurant.getName());
               }
    
               Log.i(TAG, "Total time (ms) - " + (System.currentTimeMillis() - startTime));
           }
    
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.e(TAG, fault.getMessage());
           }
       };
    
       Backendless.Data.of(Restaurant.class).getObjectCount(new AsyncCallback<Integer>() {
           @Override
           public void handleResponse(Integer count) {
               Log.i(TAG, "Total restaurants in the Backendless storage - " + count);
               Backendless.Data.of(Restaurant.class).find(callback);
           }
    
           @Override
           public void handleFault(BackendlessFault fault) {
               Log.e(TAG, fault.getMessage());
           }
       });
    }

    private fun fetchingFirstPageAsync() {
       val startTime = System.currentTimeMillis()
    
       val callback = object : AsyncCallback<List<Restaurant>> {
           override fun handleResponse(restaurants: List<Restaurant>) {
               Log.i(TAG, "Loaded ${restaurants.size} restaurant objects")
    
               for (restaurant in restaurants) {
                   Log.i(TAG, "Restaurant name = ${restaurant.name}")
               }
    
               Log.i(TAG, "Total time (ms) - ${System.currentTimeMillis() - startTime}")
           }
    
           override fun handleFault(fault: BackendlessFault) {
               Log.e(TAG, fault.message)
           }
       }
    
       Backendless.Data.of(Restaurant::class.java).getObjectCount(object : AsyncCallback<Int> {
           override fun handleResponse(count: Int?) {
               Log.i(TAG, "Total restaurants in the Backendless storage - $count")
               Backendless.Data.of(Restaurant::class.java).find(callback)
           }
    
           override fun handleFault(fault: BackendlessFault) {
               Log.e(TAG, fault.message)
           }
       })
    }


    Restaurant class:

    @interface Restaurant : NSObject
    
    @property (strong, nonatomic) NSString *name;
    
    @end
    

    API example:

    NSLog(@"============ Fetching first page ============");
    
    NSDate *startTime = [NSDate date];
    DataStoreFactory *dataStore = [Backendless.shared.data of:[Restaurant class]];
    
    [dataStore findWithResponseHandler:^(NSArray *restaurants) {
        NSLog(@"Loaded %lu restaurant objects", (unsigned long)restaurants.count);
    
        [dataStore getObjectCountWithResponseHandler:^(NSInteger totalRestaurants) {
            NSLog(@"Total restaurants in the Backendless storage - %li",(long)totalRestaurants);
            for (Restaurant *restaurant in restaurants) {
                NSLog(@"Restaurant name = %@", restaurant.name);
            }            
            NSLog(@"Total time (ms) - %g", 1000*[[NSDate date] timeIntervalSinceDate:startTime]);
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
    
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];


    Restaurant class:

    @objcMembers class Restaurant: NSObject {
        var name: String?
    }

    API example:

    print("============ Fetching first page ============")
            
    let startTime = Date()
    let dataStore = Backendless.shared.data.of(Restaurant.self)
            
    dataStore.find(responseHandler: { restaurants in
        print("Loaded \(restaurants.count) restaurant objects")
                
        dataStore.getObjectCount(responseHandler: { totalRestaurants in
            print("Total restaurants in the Backendless storage - \(totalRestaurants)")
                    
            if let restaurants = restaurants as? [Restaurant] {
                for restaurant in restaurants {
                    print("Restaurant name = \(restaurant.name ?? "")")
                }
                print("Total time (ms) - \(Int(Date().timeIntervalSince(startTime) * 1000))")
            }
        }, errorHandler: { fault in
            print("Error: \(fault.message ?? "")")
        })
                
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })

    const Backendless = require('backendless')
    /*
     Or use `import Backendless from 'backendless'` for client side.
     If you don't use npm or yarn to install modules, you can add the following line
     <script src="//api.backendless.com/sdk/js/latest/backendless.min.js"></script>
     to your index.html file and use the global Backendless variable.
    */
    
    Backendless.initApp('YOUR_APP_ID', 'YOUR_JS_API_KEY')
    
    const PAGE_SIZE = 100 // max allowed value
    
    const fetchAllObjects = async () => {
      let offset = 0
      let totalCount = 0
      let lastPageSize = 0
      const itemsCollection = []
    
      const startTime = Date.now()
    
      do {
        const pageQuery = Backendless.DataQueryBuilder.create()
    
        pageQuery.setPageSize(PAGE_SIZE)
        pageQuery.setOffset(offset)
    
        const items = await Backendless.Data.of('Restaurant').find(pageQuery)
    
        lastPageSize = items.length
    
        itemsCollection.push(...items)
    
        offset += PAGE_SIZE
        totalCount += lastPageSize
      } while (lastPageSize >= PAGE_SIZE)
    
      console.log('Total time (ms) - ' + (Date.now() - startTime))
    
      return itemsCollection
    }
    
    const onSuccess = restaurants => {
      console.log(`Loaded ${ restaurants.length } restaurants objects`)
    
      restaurants.forEach(restaurant => {
        console.log(`Restaurant name = ${ restaurant.name }`)
      })
    }
    
    const onError = error => {
      console.error('Server reported an error: ', error.message)
      console.error('error code: ', error.code)
      console.error('http status: ', error.status)
    }
    
    Promise.resolve()
      .then(fetchAllObjects)
      .then(onSuccess)
      .catch(onError)
    

    static void _fetchingFirstPageAsync() {
       int startTime = DateTime.now().millisecondsSinceEpoch;
    
       Backendless.Data.of("Restaurant").getObjectCount().then((count) {
         print("Total restaurants in the Backendless storage - $count");
        
         Backendless.Data.of("Restaurant").find().then((restaurants) {
           print("Loaded ${restaurants.length} restaurant objects");
           restaurants.forEach((restaurant) => print("Restaurant name = ${restaurant['name']}"));
           print("Total time (ms) - ${DateTime.now().millisecondsSinceEpoch - startTime}");
         });
       });
     }

    The example demonstrates the usage of the data loading API in its most basic form. The highlighted lines show the actual API. It returns a collection of objects. Notice that the collection is strongly-typed – you do not need to perform any additional unmarshalling of data. Also, the same data object that is saved on the backend is automatically morphed into the client-side type. In other words, the same restaurant object can arrive as a Java POJO to Android/Java application or an Objective-C or Swift object to an iOS app.

    The code produces the following output:

    ============ Fetching first page using the SYNC API ============
    Loaded 4 restaurant objects
    Total restaurants in the Backendless storage - 4
    Restaurant name = Cantina Laredo
    Restaurant name = Buca Di Bepo
    Restaurant name = McDonald's
    Restaurant name = Endless Sweets
    Total time (ms) - 275
    ============ Fetching first page using the ASYNC API ============
    Loaded 4 restaurant objects
    Total restaurants in the Backendless storage - 4
    Restaurant name = Cantina Laredo
    Restaurant name = Buca Di Bepo
    Restaurant name = McDonald's
    Restaurant name = Endless Sweets
    Total time (ms) - 122

    As you can see, the example above prints out the names of the restaurants. The code implies that the results are paged (see the getCurrentPage call).

    Enjoy!

    Leave a Reply