Twitter: raymondcamden


Address: Lafayette, LA, USA

Parse.com - dynamic data storage for mobile

01-03-2012 119,874 views Mobile, jQuery 28 Comments

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:

view plain print about
1function make_base_auth(user, password) {
2     var hash = window.btoa(tok);
3     return "Basic " + hash;
4    }

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:

view plain print about
1var note = {
2        title:"Test title",
3        body:"Test body"
4};
5
6$.ajax({
7    contentType:"application/json",
8    dataType:"json",
9    url:ROOT+"classes/note",
10    username:appId,
11    password:masterKey,
12    processData:false,
13    headers:{
14        "Authorization": make_base_auth(appId,masterKey)
15    },
16    type:"POST",
17    data:JSON.stringify(note),
18    error:function(e) { alert('error: '+e);}
19}).done(function(e,status) {
20    alert('Done');
21});

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?

view plain print about
1$.ajax({
2    contentType:"application/json",
3    dataType:"json",
4    url:ROOT+"classes/note",
5    username:appId,
6    password:masterKey,
7    processData:false,
8    headers:{
9        "Authorization": make_base_auth(appId,masterKey)
10    },
11    type:"GET",
12    error:function(e) { alert('error: '+e);}
13}).done(function(e,status) {
14    var s = "";
15    for(var i=0; i<e.results.length; i++) {
16        var note = e.results[i];
17        s+= "<p><b>id:</b>"+note.objectId+"<br/>";
18        s+= "<b>created:</b>"+note.createdAt+"<br/>";
19        s+= "<b>title:</b>"+note.title+"<br/>";
20        s+= "</p>";
21    }
22    $("#result").html(s);
23});

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:

view plain print about
1<!DOCTYPE HTML>
2<html>
3<head>
4
5<meta name="viewport" content="width=320; user-scalable=no" />
6<meta http-equiv="Content-type" content="text/html; charset=utf-8">
7<title>PhoneGap</title>
8<script type="text/javascript" charset="utf-8" src="js/phonegap-1.3.0.js"></script>
9<script type="text/javascript" charset="utf-8" src="js/jquery-1.7.min.js"></script>
10<script type="text/javascript" charset="utf-8" src="js/jquery.parse.js"></script>
11<script type="text/javascript" charset="utf-8" src="js/main.js"></script>
12<style>
13input[type=button] {
14    width: 100%;
15    padding: 10px;
16}
17</style>
18</head>
19
20<body onload="init()">
21
22<input type="button" id="addBtn" value="Add Item"><br/>
23<input type="button" id="listBtn" value="List Items"><br/>
24<div id="result"></div>
25
26</body>
27</html>

And then whipped up this JavaScript:

view plain print about
1var appId = "f4yClrICW58n0IbryPbFFBC99cZvHUaVI0muEH7d";
2var clientKey = "ameusdLfx4Kl4nV316lzMX8Wb0lli4NEpb5GgDXN";
3var masterKey = "PiRAKmRtMIaexl4uBlvMDyo6gEUvCpLHpYqOIuJo";
4var ROOT = "https://api.parse.com/1/";
5
6$.ajaxSetup({
7    contentType:"application/json",
8    dataType:"json",
9    username:appId,
10    password:masterKey,
11    processData:false,
12    headers:{
13        "Authorization": make_base_auth(appId,masterKey)
14    },
15    error:function(e) { alert('error: '+e);}
16});
17
18function init() {
19    document.addEventListener("deviceready", deviceready, true);
20}
21
22function make_base_auth(user, password) {
23     var tok = user + ':' + password;
24//     var hash = Base64.encode(tok);
25     var hash = window.btoa(tok);
26     return "Basic " + hash;
27    }
28
29function deviceready() {
30    
31    $("#addBtn").on("click", function() {
32        //demo adding an item
33        var note = {
34                title:"Test title",
35                body:"Test body"
36        };
37        
38        $.ajax({
39            url:ROOT+"classes/note",
40            type:"POST",
41            data:JSON.stringify(note)
42        }).done(function(e,status) {
43            alert('Done');
44        });
45    });
46
47    $("#listBtn").on("click", function() {
48        //demo getting items
49        $.ajax({
50            url:ROOT+"classes/note",
51            type:"GET"
52        }).done(function(e,status) {
53            var s = "";
54            for(var i=0; i<e.results.length; i++) {
55                var note = e.results[i];
56                s+= "<p><b>id:</b>"+note.objectId+"<br/>";
57                s+= "<b>created:</b>"+note.createdAt+"<br/>";
58                s+= "<b>title:</b>"+note.title+"<br/>";
59                s+= "</p>";
60            }
61            $("#result").html(s);
62        });
63    });
64
65}

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.

28 Comments

  • Commented on 01-03-2012 at 1:58 PM
    Now this is exactly what I've been looking for for my mobile apps :)
    Thanks Ray!
  • Commented on 01-03-2012 at 2:01 PM
    I should add - if anyone wants my PhoneGap app, just let me know. I figured it was too trivial to actually share. I see I included my keys in the code above, so, um, that's bad, but it's a demo app, and the company knows about my blog post, so I'm not going to fret about it.
  • Commented on 01-04-2012 at 1:56 PM
    I'd like to see the PhoneGap app if you don't mind! We get a ton of requests from PhoneGap and Appcelerator users for Parse integration and maybe you'd let us use your app as an example?

    Great blog post Ray -- really impressive.

    Best,
    Tikhon
  • Commented on 01-04-2012 at 1:56 PM
    Oh can you remove my email address from that last comment? :-) Thanks!
  • Commented on 01-04-2012 at 2:00 PM
    You can download a zip of the Eclipse project here:

    http://www.raymondcamden.com/enclosures/parse.zip

    The APK is in the bin folder. In theory, you can add this to your Eclipse install and run it.

    p.s. Will remove your email addy from the comment in a sec.
  • Commented on 01-04-2012 at 2:01 PM
    If I get time, I'll try to finish my app and turn it into a real mini Notes type app.
  • Andrew Gscheidle #
    Commented on 01-11-2012 at 1:53 PM
    Ray,
    I get 404 when trying to download the parse.zip. Is it no longer there? If you can make available, I'd greatly appreciate it!
  • Commented on 01-11-2012 at 2:01 PM
    oops, change that to parsedemo.zip.
  • Andrew Gscheidle #
    Commented on 01-11-2012 at 4:07 PM
    Ray,
    When I try to run a similar app, it gives me problems with trying to call the REST api at Parse because I'm trying to run on a local web server to debug. Did you run into anything similar?

    Error:
    XMLHttpRequest cannot load https://api.parse.com/1/classes/Church/[objectid]. Origin http://andys-imac is not allowed by Access-Control-Allow-Origin.

    *Note, I'm using a real objectId in my code, just don't want to post on here.

    Thanks,
    Andrew
  • Commented on 01-11-2012 at 4:09 PM
    That's normal - it's your browser not letting you do a network request to another domain. Some APIs support JSON/P, which is a 'cheat' way around it, but I do not think Parse.com does. You could hit ColdFusion on your local server and have it proxy the request (or PHP, Ruby, etc).

    In my testing, I never ran it as a file on my own server. (Well, I did for my ColdFusion test, but that wasn't Ajax.)
  • Andrew Gscheidle #
    Commented on 01-11-2012 at 5:20 PM
    Ray,
    Sigh... this is frustrating for sure. When you created your phone gap app, you were running it on a web server, not locally? I suppose I could use dropbox or something as a cheat for a remote host. Hmmm;
  • Commented on 01-11-2012 at 5:37 PM
    Ah, you have a fundamental misunderstanding of PhoneGap here. PhoneGap packages your HTML/JS/CSS up into a native application. It runs your HTML - much like you can run HTML by just double clicking on an HTML file on your desktop. However, PhoneGap HTML apps are NOT restricted in the same ways traditional HTML apps are. So for example, I can do an Ajax request to any domain. It just plain works.

    Does that distinction make sense?

    Is it time for me to give a PhoneGap preso over Google+ Hangouts? :)
  • Andrew Gscheidle #
    Commented on 01-12-2012 at 11:31 AM
    Ray,
    I tried running this on Apache on my local box (using the built in Apache instance on mac) and that didn't work. Why would CF running locally work any better (just curious). The problem is, when I run this in iOS/Android emulator, the Parse calls fail for apparently the same reason. I'm interested to know more about how you developed your app without running into this. Thanks!
  • Commented on 01-12-2012 at 11:33 AM
    I think we are still misunderstanding each other.

    The CF code wasn't using JavaScript/Ajax to speak to Parse. It just used HTTP.

    You can't run my main code above on your web server. It just won't work because of the remote XHR call.

    Does that make sense?

    Now- when you run this in Android, it should work fine. I've never had issues doing a HTTP call to remove servers. If you could send me your APK, I could take a look.
  • Andrew Gscheidle #
    Commented on 01-12-2012 at 11:40 AM
    My fault for not being clear... I've not run your code, nor tried to. I was referring to my creation of code similar to that shared in the above article.

    The article above mentions using AJAX to call the Parse API methods.Your parse demo is entirely html/js, right? At least, the part you made available to download was. So I was wondering how you got an html/jQuery mobile app using Ajax calls to Parse to work in a browser/emulator, etc. Does that make sense?

    Perhaps it only works on the physical device. That would really suck and impede one from seriously using Parse.com services in an app. Maybe I'm missing something - it's been known to happen :)
  • Commented on 01-12-2012 at 11:41 AM
    I didn't use the browser at all. I used either real hardware, or the Android emulator.
  • Stelios #
    Commented on 02-09-2012 at 4:40 AM
    Hi Ray,
    Great code! Thanks for your contribution!
    I am trying to hit the logging in functionality of Parse using $.ajax (i have already created 2 users via curl) but with no success. Here is sth i tried.

    $.ajax({
       url:ROOT+"login",
       type:"GET",
    data:"username=stelios",
    data:"password=123456"
    }).done(function(e,status) {
       alert('Logged in');
    });

    Could u be of any help?
  • Commented on 02-09-2012 at 10:34 AM
    The data argument, afaik, should be one object, not multiple keys as you had it there. I'd change it to:

    data:{"username":"stelios","password":"123456"}
  • Commented on 08-23-2012 at 3:59 PM
    Hey Ray,

    I am very interested in using a similar setup as you for my mobile app. I am somewhat new to this and might have an unclear understand of how you app works but say I had a string that stored a comma delimited list of ids. Is there a way I could parse this server-side to create a string array without needing the phone to handle it?

    I'm interested in your thoughts, thanks,
    Jon
  • Commented on 08-23-2012 at 4:02 PM
    I'm confused. You say you don't want to use the Phone to handle parsing a string. But if you are building a phone app, and parsing strings is simple, why would you send it to the server? Having JavaScript split a string is easy - just use the split() method of strings.
  • Commented on 08-23-2012 at 4:13 PM
    makes sense, I think I had a misunderstanding of how to optimize my mobile app to take try and meet tighter processing and network traffic constraints
  • Commented on 08-23-2012 at 4:32 PM
    It's a new world. :)

    By the way - for folks who subscribed to the comment feed back in January, Parse.com has added a crap load of features since then.
  • Commented on 08-30-2012 at 4:14 PM
    I am currently trying to hit the API and I had it working however for some reason it is now failing on me, wonder if there is something you can see that would keep it from working? The current approach works on a desktop browser but not on the phone.


    <script type="text/javascript">
       Parse.initialize("06Rj5ynzjq4XXXXXXXXXXXXXKGvDLqNKB", "jQtc5aNnpXXXXXXXXXXXXXEl9aKFw931ui");
       var appId = "06Rj5ynzjXXXXXXXXXXXXXXXXXXLO6mShKGvDLqNKB";
       var masterKey = "jkxkdxJLXXXXXXXXXXXXXXXXXXXXZ8eUSO";
       var restKey = "BV3YQ4XXXXXXXXXXXXXXXwcBfDnU";
       var ROOT = "https://api.parse.com/1/";;

       $.ajax({
           contentType:"application/json",
           dataType:"json",
           url:ROOT+"users",
           username:appId,
           password:masterKey,       
           processData:false,
           headers: {
              "X-Parse-Application-Id": appId,
           "X-Parse-REST-API-Key": restKey
           },
           type:"GET",
           error:function(e) { alert('error: '+e);}
          }).done(function(e,status) {
             var user = e.results[0];
              var s = user.objectId;
           $("#result").html(s);
          });
    document.addEventListener('deviceready', function() {
        navigator.splashscreen.hide();
    });
    </script>
  • Commented on 08-30-2012 at 4:41 PM
    Seems ok to me - although I'd probably put it in my onDeviceReady handler. I typically do not do anything till then. You may want to try console debugging as well.
  • Commented on 09-03-2012 at 9:00 AM
    As just a general FYI to everyone here - note that Parse.com's new JS api makes this stuff even easier. I'm hoping to do an updated blog post sometime this month.
  • mkhuda #
    Commented on 12-22-2012 at 10:56 AM
    Hey, its worked for me, mr Raymond !
    Many thanks for you ! :)
  • mkhuda #
    Commented on 12-22-2012 at 11:54 PM
    And how to store these object to local app storage ? and then view it offline ? thanks
  • Commented on 12-23-2012 at 9:49 AM
    See my next post on Parse:

    http://www.raymondcamden.com/index.cfm/2012/9/14/E...

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty