Twitter: raymondcamden


Address: Lafayette, LA, USA

Integrating Parse.com on the Server

02-22-2013 6,230 views Mobile, Development, ColdFusion 7 Comments

Earlier today a reader asked me to discuss using Parse.com on the server. I've blogged about Parse quite a bit, but always in the context of a web application (typically a mobile application). Parse.com has a full REST API that provides 100% of the functionality I've talked about before to any server-side application. In this blog post I'll demonstrate a few examples in ColdFusion, but certainly any application platform would suffice.

First off, let's discuss why you would want to integrate your server with your Parse.com application data. The docs provide a few reasons, two of which I think are big - uploading a significant amount of data and exporting Parse.com data. Specifically that last one could be vital for creating backups. (Parse could fail. It happens.) On top of that I can also think of other reasons.

To me, the biggest would be reporting. Parse.com's dashboard has a dataviewer, a rather nice one, but managers will want something a bit more formal. Also, you can't get an aggregate view of your data at a high level. The REST API would make all of this possible.

Let's look at a simple example of retrieving data. Parse has made their API as simple as possible. All URLs begin with a base value:

https://api.parse.com/1

You then simply append to the URL based on the type of data you are getting. So for example, if I wanted to get "TipObject" data (see the related links below for a look back at my "CowTipLine" application) I'd use this URL:

https://api.parse.com/1/classes/TipObject

You also have access to metadata like installations and users:

https://api.parse.com/1/users

To retrieve data, you pass along two headers: X-Parse-Application-Id and X-Parse-REST-API-Key. As a reminder, you can find your application ID and REST API keys in the dashboard for your application.

Here is a quick demonstration of retrieving TipObjects.

And the result:

Retrieving one object is as simple as appending an object ID value to the URL:

https://api.parse.com/1/classes/TipObject/w2KtU29hNR

Most likely though you aren't going to want to fetch everything. The REST API provides a very rich query functionality that lets you...

  • Do property matches and ranges (i.e. x=something or x gt something)
  • Retrieve nest data (this is optional and gives you better control over the size of data being retrieved)
  • Return just a count (note that, by itself, a count request won't limit the results, you need to ask for a count and tell Parse to return no objects)
  • Handle pagination

Consider the simple example below. It filters my TipObjects by the property howdangerous where the value is 1.

As you can see, the query value is passed as a URL variable named where. The value of this is a JSON-encoded string. If I were doing something very complex I'd create the filter as pure data and then convert to JSON.

I mentioned earlier that I'd consider reporting as the best use case for this feature. Let's consider a simple example. My application lets you report on the relative safety of a region on a 1 to 3 value. Using the Parse.com REST API I can ask for a "count" value on each of those values. Parse.com has a "batch" process where you can run multiple operations at once, but unfortunately it doesn't support GET operations. (I filed a request for this here: Support for Batch and Get) But since this is an aggregate report we can simply add a bit of caching so we are not constantly hitting the API. In this example I've added a cache for 0.1 of a day (which we all know is... um... something).

I built a simple chart to render this in manager-friendly bright colors.

Finally, how about an email report? You can imagine a scheduled task that, once a month, emails management a basic report covering the number of new users and data entries over the past month. Date filters are slightly more complex. Here is an example with a bit of white space in it to make it easier to read:

where = {
  "createdAt":
    {"$gte":{"__type":"Date","iso":"2013-02-01T00:00:00.000Z"}}
}

Note how I have to specify a type for the property. Here is a full example that emails the report for the month (and certainly you could change this to a daily, weekly, etc type report).

7 Comments

  • xavier #
    Commented on 02-22-2013 at 10:47 AM
    Thans Ray for this post. How about data movement in the other direction? My app requires that data from an existing SQL database is first uploaded on to Parse before the Mobile users can consume it. I tried to do this using the Javascript examples from your other posts and was not successful.
  • Commented on 02-22-2013 at 10:51 AM
    When I say they have full support in the REST API, I really mean it. :) Full CRUD (create, read, update, delete). Check the docs - but it should be pretty simple, right? If you want me to craft a CF example of adding an object, just let me know.
  • xavier #
    Commented on 02-22-2013 at 2:14 PM
    Parse gives an example of a batch POST:
    curl -X POST \
    -H "X-Parse-Application-Id: eQ9oV6e3Suy9TQbmhsulOT5pB3Gd3lRfYfnk05gu" \
    -H "X-Parse-REST-API-Key: zBDlX1eXV8WGxoBbPhm42djWEOzjgrB65fn43gJT" \
    -H "Content-Type: application/json" \
    -d '{
    "requests": [
    {
    "method": "POST",
    "path": "/1/classes/GameScore",
    "body": {
    "score": 1337,
    "playerName": "Sean Plott"
    }
    },
    {
    "method": "POST",
    "path": "/1/classes/GameScore",
    "body": {
    "score": 1338,
    "playerName": "ZeroCool"
    }
    }
    ]
    }' \
    https://api.parse.com/1/batch

    I tried to do this using coldFusion CFHTTP tag and got "unathorized" error. I also tested to make sure that i can read an existing record, just to confirm that my Parse IDs are good. My CF code looks like this:

    <cfset dataString = "{ requests: [">
    <cfloop query="myQ">
       <cfif myQ.currentRow NEQ 1>
          <cfset dataString &= ', '>
       </cfif>
       <cfset dataString &= '{ method: "POST", "path": "/1/classes/myParseObject", "body": {'>
       <cfset dataString &= '"userid":' & myQ.userId & ', "firstName":' & myQ.firstName & ', "lastName":' & myQ.lastName & '}}'>
    </cfloop>
    <cfset dataString &= ' ] }'>

    <cfset parseAppId = "myParseAppId_here">
    <cfset parseRestId = "myParseRestId_here">
    <cfset dataUrl = "https://api.parse.com/1/classes/myParseObject"...;
    <cfhttp method="Post" url="#dataUrl#">
       <cfhttpparam type="Formfield" name="X-Parse-Application-Id" value="#parseAppId#">
       <cfhttpparam type="Formfield" name="X-Parse-REST-API-Key" value="#parseRestId#">
       <cfhttpparam type="Formfield" name="Content-Type" value="application/json">
       <cfhttpparam type="Formfield" name="data" value="#dataString#">
    </cfhttp>
    <cfdump var="#deserializeJSON(cfhttp.filecontent)#">

    I suspect the line where i am passing the data is where the problem is.

    Thanks again.
  • Commented on 02-22-2013 at 2:27 PM
    I didn't test batch, but your content-type has the wrong type - it should be type=header. See this working example:

    https://gist.github.com/cfjedimaster/5016327

    Note, CF insisted on converting my "9" to 9 which broke with parse because it wanted it to be a string (even though it is a number ;)
  • Commented on 02-22-2013 at 2:27 PM
    Also, you don't pass the data as a formfield, but as a "body" type.
  • xavier #
    Commented on 02-22-2013 at 3:41 PM
    Getting an invalid json error. Tried your example also and got the same results:

    struct
    code    107
    error    invalid json: "{\"comments\":\"From CF\",\"howdangerous\":\"3\",\"numcows\":\"99\"}"
  • Commented on 02-22-2013 at 3:44 PM
    Not sure what to say - it just worked for me. Obviously my data model is not the same as yours so you can't use the exact same data. If you want to send me your app/rest api info and a snapshot of your data model (from the Parse dashboard), I can try it myself.

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