A user on StackOverflow asked how to load only the data that belongs to the currently logged-in user. This is indeed an interesting and very common use-case. Backendless handles it beautifully and this feature certainly deserves a place among our Recipes.
What you need to know before we get to the coding sample part is:
Loading data objects that belong to a user does not require any specialized APIs. In fact, exactly the same Backendless API you’d use to load objects from the server should be used. The trick is in setting up the security policy to restrict users from accessing data objects and letting Backendless fall back on what we call the Owner Policy.
Consider the example below. The code logs in a user and adds a few data objects:
private static void loginUserAsync() { Backendless.UserService.login("spidey@backendless.com", "mypassword", new AsyncCallback<BackendlessUser>() { @Override public void handleResponse(BackendlessUser backendlessUser) { Log.i(TAG, "User has been logged in"); } @Override public void handleFault(BackendlessFault fault) { Log.e(TAG, fault.getMessage()); } }); } private static void addObjectsAsync() { Order order1 = new Order(); order1.orderName = "spider web"; order1.orderNumber = 1; Order order2 = new Order(); order2.orderName = "costume"; order2.orderNumber = 2; List<Order> orders = Arrays.asList(order1, order2); Backendless.Data.of(Order.class).create(orders, new AsyncCallback<List<String>>() { @Override public void handleResponse(List<String> ids) { for (String id : ids) { Log.i(TAG, "Order object has been saved with ID - " + id); } } @Override public void handleFault(BackendlessFault fault) { Log.e(TAG, fault.getMessage()); } }); }
private fun loginUserAsync() { Backendless.UserService.login("spidey@backendless.com", "mypassword", object : AsyncCallback<BackendlessUser> { override fun handleResponse(backendlessUser: BackendlessUser) { Log.i(TAG, "User has been logged in") } override fun handleFault(fault: BackendlessFault) { Log.e(TAG, fault.message) } }) } private fun addObjectsAsync() { val order1 = Order("spider web", 1) val order2 = Order("costume", 2) val orders = listOf(order1, order2) Backendless.Data.of(Order::class.java).create(orders, object : AsyncCallback<List<String>> { override fun handleResponse(ids: List<String>) { for (id in ids) { Log.i(TAG, "Order object has been saved with ID - $id") } } override fun handleFault(fault: BackendlessFault) { Log.e(TAG, fault.message) } }) }
Backendless.shared.userService.login(identity: "spidey@backendless.com", password: "mypassword", responseHandler: { loggedInUser in let order1 = Order() order1.orderName = "spider web" order1.orderNumber = 1 Backendless.shared.data.of(Order.self).save(entity: order1, responseHandler: { savedOrder1 in if let savedOrder1 = savedOrder1 as? Order { print("Order 1 has been saved: \(savedOrder1.orderName ?? "") - \(savedOrder1.orderNumber ?? 0)") } }, errorHandler: { fault in print("Error: \(fault.message ?? "")") }) let order2 = Order() order2.orderName = "costume" order2.orderNumber = 2 Backendless.shared.data.of(Order.self).save(entity: order2, responseHandler: { savedOrder2 in if let savedOrder2 = savedOrder2 as? Order { print("Order 2 has been saved: \(savedOrder2.orderName ?? "") - \(savedOrder2.orderNumber ?? 0)") } }, 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 addObjects = () => { const order1 = { orderName: 'spider web', orderNumber: 1 } const order2 = { orderName: 'costume', orderNumber: 2 } const storage = Backendless.Data.of('Order') return Promise.all([ storage.save(order1), storage.save(order2), ]) } const loginUser = () => { return Backendless.UserService.login('spidey@backendless.com', 'mypassword') } const onSuccess = () => { console.log('Order objects has been saved') } 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(loginUser) .then(user => console.log('User has been logged in')) .then(addObjects) .then(onSuccess) .catch(onError)
static void _loginUserAsync() => Backendless.UserService.login("spidey@backendless.com", "mypassword").then( (user) => print("User has been logged in")); void _addObjectsAsync() { Map order1 = { "orderName": "spider web", "orderNumber": 1, }; Map order2 = { "orderName": "costume", "orderNumber": 2, }; Backendless.Data.of("Order").create([order1, order2]).then((ids) { ids.forEach((id) => print("Order object has been saved with ID - $id")); }); }
The Order class that the code above uses can be found below.
The class uses public fields, but could also be turned into a Java Bean with getter and setter methods – Backendless supports either one of the approaches.
public class Order { public String orderName; public int orderNumber; }
public class Order { public String orderName; public int orderNumber; }
@interface Order : NSObject @property (strong, nonatomic) NSString *orderName; @property (strong, nonatomic) NSNumber *orderNumber; @end
@objcMembers class Order: NSObject { var orderName: String? var orderNumber: NSNumber? }
If you didn’t have the Order table in your app when you run the code, you will not have any other Order objects in the table. As a result, to see the effect of loading the objects that belong to the user, it would help to add an additional object belonging to someone else (or no one).
Take a look at the Order table in Backendless Console – you will see it contains the ownerId column. That column is automatically added to every table in Backendless. If it contains a value, it will be the objectId of the user who created (saved) the object. Add a new row to the Order table using the Console and make sure the ownerId column remains empty. Your Order table should look like in the screenshot below (the “fish and chips” order was added manually):
The code to load objects from the Order table is below. Make sure the user is logged in before running the code:
// make sure the user is logged in. Use the code sample shown above Backendless.Data.of(Order.class).find(new AsyncCallback<List>() { @Override public void handleResponse(List orders) { for (Order order : orders) { Log.i(TAG, "Order name - " + order.orderName); Log.i(TAG, "Order number - " + order.orderNumber); Log.i(TAG, "============================"); } } @Override public void handleFault(BackendlessFault fault) { Log.e(TAG, fault.getMessage()); } });
// make sure the user is logged in. Use the code sample shown above [[Backendless.shared.data of:[Order class]] findWithResponseHandler:^(NSArray *orders) { for (Order *order in orders) { NSLog(@"Order name - %@", order.orderName); NSLog(@"Order number - %@", order.orderNumber); NSLog(@"============================"); } } errorHandler:^(Fault *fault) { NSLog(@"Error: %@", fault.message); }];
// make sure the user is logged in. Use the code sample shown above Backendless.Data.of(Order::class.java).find(object : AsyncCallback<List<Order>> { override fun handleResponse(orders: List<Order>) { for (order in orders) { Log.i(TAG, "Order name - ${order.orderName}") Log.i(TAG, "Order number - ${order.orderNumber}") Log.i(TAG, "============================") } } override fun handleFault(fault: BackendlessFault) { Log.e(TAG, fault.message) } })
// make sure the user is logged in. Use the code sample shown above Backendless.shared.data.of(Order.self).find(responseHandler: { orders in if let orders = orders as? [Order] { for order in orders { print("Order name - \(order.orderName ?? "")") print("Order number - \(order.orderNumber ?? 0)") print("============================") } } }, 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 loginUser = () => { return Backendless.UserService.login('spidey@backendless.com', 'mypassword') } const getObjects = () => { return Backendless.Data.of('Order').find() } const onSuccess = orders => { orders.forEach(order => { console.log('Order name - ' + order.orderName) console.log('Order number - ' + order.orderNumber) console.log('============================') }) } 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(loginUser) .then(user => console.log('User has been logged in')) .then(getObjects) .then(onSuccess) .catch(onError)
// make sure the user is logged in. Use the code sample shown above Backendless.Data.of("Order").find().then((orders) { orders.forEach((order) { print("""Order name - ${order['orderName']} Order number - ${order['orderNumber']} ============================"""); }); });
If you run the code before making any changes in the security policy, it will produce the following output:
Order name - fish and chips Order number - 3 ============================ Order name - costume Order number - 2 ============================ Order name - spider web Order number - 1 ============================
As you can see, this is not the desired output – the code loads all objects from the table. To make the change so the client app gets only the objects that belong to the user, follow the steps below: