Parse.com – dynamic data storage for mobile

Updated September 3, 2012: As just an FYI, since I wrote this post Parse launched a JavaScript API. I’d recommend using that instead of the REST-based API if you are using JavaScript. You can still use the REST-based API, but using their JS API let’s you skip some of the authorization issues I had below.

During the Christmas break, I had the chance to look at, and play with, Parse. While you can check the site for the marketing description, the basic idea behind the service is a way to store data for mobile applications without the need for a server. I played with their API and was incredibly impressed by what I saw. Here’s why you should consider them:

  • First off – you can skip setting up a server. Now, I know that may sound crazy. I use ColdFusion everywhere. Most of us have a web server we can use. But having an ad hoc back end system for your mobile apps is a powerful incentive.
  • Their storage system allows you to store any data. Basically, you can take your JavaScript object, JSON serialize it, and store. While not explicitly called out on their site (or maybe it is, I didn’t check every page), they are obviously using some form of NoSQL on the back end.
  • They automatically handle basic metadata for your objects. So no need to worry about a created or updated field. It’s a small thing, but darn useful.

Parse has full SDKs for Android and iOS, but even better, they’ve got a URL based API. You can use this API to create, update, delete, and search your data.

Just to reiterate to make sure this is clear – your mobile applications can make use of a dynamic database without any setup on your web server. Just sign up, get your keys, and get cranking.

Even better, they provide some real good benefits on top of what I already said.

First – any objects you create will have metadata (date+ID) added to them automatically. Yeah you could do that yourself, but it’s a handy addition.

Second – they have special support for users. Basically, they recognize that user objects are probably something almost every application has, so they go out of their way to handle them better and make it part of your model.

Third – they also support geolocation searches. I’m not sure how often I’d use this, but if you tag data with a location, they make it trivial to later search for objects within a certain range. I’ve done this manually a few times in SQL and I can tell you – it’s not fun. (Even when cutting and pasting a solution.)

Four – they have a nice web based data browser. This allows you to do some basic CRUD type operations and is real useful while testing:

You can read more about their REST API here. I decided to give their API a try with PhoneGap and jQuery. While Parse is a paid service, they do offer a free version in their pricing plans.

Hitting their API with jQuery proved to be a bit difficult. You need to include a username and password (which corresponds with two of your keys). That parts easy enough if you use $.ajax. What proved more difficult was the Authorization header. This header needs to be the combination of the username/password strings and has to be Base64 encoded.

I found a JavaScript library for this here: http://www.webtoolkit.info/javascript-base64.html.

This worked, but I also discovered that some modern browsers include it natively via window.btoa. I combined this with a blog post by Bruce Kroez to come up with this method:


function make_base_auth(user, password) {
var hash = window.btoa(tok);
return "Basic " + hash;
}

I believe, stress, believe, window.btoa may not work in iOS. If so, switch to the JavaScript library version listed above. So given that, a basic hit to Parse via jQuery could look like so:


var note = {
title:"Test title",
body:"Test body"
};

$.ajax({
contentType:"application/json",
dataType:"json",
url:ROOT+"classes/note",
username:appId,
password:masterKey,
processData:false,
headers:{
"Authorization": make_base_auth(appId,masterKey)
},
type:"POST",
data:JSON.stringify(note),
error:function(e) { alert('error: '+e);}
}).done(function(e,status) {
alert('Done');
});

In this case, I’m adding an object by doing a POST. Notice how I can just make a note object, serialize it, and send it. I love that. You get a JSON object back containing the ID of the new object. How about listing objects?


$.ajax({
contentType:"application/json",
dataType:"json",
url:ROOT+"classes/note",
username:appId,
password:masterKey,
processData:false,
headers:{
"Authorization": make_base_auth(appId,masterKey)
},
type:"GET",
error:function(e) { alert('error: '+e);}
}).done(function(e,status) {
var s = "";
for(var i=0; i<e.results.length; i++) {
var note = e.results[i];
s+= "<p><b>id:</b>"+note.objectId+"<br/>";
s+= "<b>created:</b>"+note.createdAt+"<br/>";
s+= "<b>title:</b>"+note.title+"<br/>";
s+= "</p>";
}
$("#result").html(s);
});

Note that the significant change here was from POST to GET. It’s as simple as that. I can then loop over my results and display them how I choose. Note that they support both pagination and getting the total number of results too.

So, how about an application? I wrote a super simple application that let’s me add an object by clicking a button and then generate a list. It isn’t dynamic at all, but demonstrates the API. jQuery supports an ajaxSetup function which lets me default a lot of the stuff you saw above. First, I built up a super simple HTML page:

<!DOCTYPE HTML>
<html>
<head>

<meta name="viewport" content="width=320; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>PhoneGap</title>
<script type="text/javascript" charset="utf-8" src="js/phonegap-1.3.0.js"></script>
<script type="text/javascript" charset="utf-8" src="js/jquery-1.7.min.js"></script>
<script type="text/javascript" charset="utf-8" src="js/jquery.parse.js"></script>
<script type="text/javascript" charset="utf-8" src="js/main.js"></script>
<style>
input[type=button] {
width: 100%;
padding: 10px;
}
</style>
</head>

<body onload="init()">

<input type="button" id="addBtn" value="Add Item"><br/>
<input type="button" id="listBtn" value="List Items"><br/>
<div id="result"></div>

</body>
</html>

And then whipped up this JavaScript:


var appId = "f4yClrICW58n0IbryPbFFBC99cZvHUaVI0muEH7d";
var clientKey = "ameusdLfx4Kl4nV316lzMX8Wb0lli4NEpb5GgDXN";
var masterKey = "PiRAKmRtMIaexl4uBlvMDyo6gEUvCpLHpYqOIuJo";
var ROOT = "https://api.parse.com/1/";

$.ajaxSetup({
contentType:"application/json",
dataType:"json",
username:appId,
password:masterKey,
processData:false,
headers:{
"Authorization": make_base_auth(appId,masterKey)
},
error:function(e) { alert('error: '+e);}
});

function init() {
document.addEventListener("deviceready", deviceready, true);
}

function make_base_auth(user, password) {
var tok = user + ':' + password;
// var hash = Base64.encode(tok);
var hash = window.btoa(tok);
return "Basic " + hash;
}

function deviceready() {

$("#addBtn").on("click", function() {
//demo adding an item
var note = {
title:"Test title",
body:"Test body"
};

$.ajax({
url:ROOT+"classes/note",
type:"POST",
data:JSON.stringify(note)
}).done(function(e,status) {
alert('Done');
});
});

$("#listBtn").on("click", function() {
//demo getting items
$.ajax({
url:ROOT+"classes/note",
type:"GET"
}).done(function(e,status) {
var s = "";
for(var i=0; i<e.results.length; i++) {
var note = e.results[i];
s+= "<p><b>id:</b>"+note.objectId+"<br/>";
s+= "<b>created:</b>"+note.createdAt+"<br/>";
s+= "<b>title:</b>"+note.title+"<br/>";
s+= "</p>";
}
$("#result").html(s);
});
});

}

Here’s a quick screen shot:

As I said, I think this is an incredibly impressive service. The only negative I can see so far is that it seems a bit slow, but, I only tested with the Android Emulator, and that tends to make everything look a bit slow. I’ve yet to test it on a real device over WiFi yet.

Also – note that their REST API will also work great with your server applications. Now, I know I just said you don’t need a server, but if you want to offload data storage and traffic to Parse, you can use another server to handle things like reporting or management. I tried calling Parse via ColdFusion and it worked well.