Example of a Parse.com JavaScript application with offline support

This post is more than 2 years old.

This morning I got a seemingly innocent question from a reader:

Came across your blog post on Parse + PhoneGap and wanted to get your opinion on the following use-case for that combo...

I've been exploring the possibilities of an app that essentially has a web form (similar to the contact form you've got right here, actually) that would store the resulting data via Parse. The reason being...it would be important that the app would allow a form to submit, even if there wasn't an active Internet connection available.

So, just wanted your thoughts on whether I am looking in the right direction to accomplish this. Don't have much experience in the way of iOS apps, but have to start somewhere, right?

I replied to him with a basic outline:
  • When you submit the form, hit Parse if you are online, hit WebSQL if not.
  • When the application starts, see if you have data in WebSQL, and if you are online, push it to parse.

That seemed simple enough, but I figured I might as well build a real demo just to prove it can be done. This is what I came up with. It's got some issues (don't we all?) but it covers the basics. As always though I'm open to suggestions for how this could be done better.

I began by creating the layout for an application. Since the reader just mentioned a form, I built the entire application around one form. I decided to build a simple UFO Report Form. It has a field for the number of UFOs, your name, and the description. I didn't make use of any UI framework but instead directed my incredible design skills at the task.

Here's the HTML behind the form, just in case your curious:

Fancy, eh? Ok, now it's time to get into the code. I'm going to tackle this piece by piece, and it may get a bit confusing, but I'll post the entire file in one chunk at the end for your perusal.

Whether or not we are online, we need to set up the database. This is done via the WebSQL API. While this API is deprecated, it is fully supported in PhoneGap and works great in Chrome, the main browser I use for testing.

I'm not going to detail how this works as I've covered it before (Example of PhoneGap's Database Support), but even if this is brand new to you I think you can get the idea.

After the database is set up, our application needs to upload any existing data to Parse. We're going to skip that now though and look at the basic form handling aspects of the code.

I wrote a function to wrap my check for online/offline support. Why? I wrote this demo without actually building it as a PhoneGap application. It should work fine when converting into a mobile application, and at that point my wrapper function can be modified to use PhoneGap's API, but for my initial testing I just wanted to use the navigator.onLine property. Having a wrapper also let me easily add in a hack (see the commented out line) to test being offline.

If we are online, I need to initialize Parse support. I won't repeat what is already covered in the Parse JavaScript Guide. Instead, this is just an example of how I initialize Parse.com with my API keys and define an object type I'm calling SightingObject (as in UFO sighting).

Now let's look at the form handler. Remember, this needs to either save to Parse or to the database.

This code block is a bit large, so let's break it down. The first thing I do is grab the values from the form. As mentioned in the comments, it would probably make sense to do some basic validation. Screw validation - this is a demo. Next I do some basic UI stuff to let the user know that exciting things are happening in the background (although in theory, not as exciting as the UFO in front of them). Then we have the online/offline block. I've taken the Parse logic out into another function that I'll show in a moment. The other part of the conditional simply writes it out to the database. In both cases we run a function, resetForm, that handles resetting my UI.

Here is saveToParse. Notice how darn easy this is. Just in case it isn't obvious - this is all the code I need to store my data, permanently, in the cloud. It would only be easier if the Parse.com engineers fed me grapes and lime jello shots while I wrote the code.

Before we get into the synchronization aspect, here is resetForm. Again, it just handles updating the UI and letting the user know something happened with their important data.

I did some quick testing and confirmed it was working. I used Parse.com's online data browser first:

I then tested offline storage. Chrome makes it easy to check since it has a database viewer built in:

That's almost it. The final piece of the puzzle is handling uploading the database data. This turned out to be simple too. If we are online, we can run a SQL against the table. If anything exists, we upload it and remove it.

That's basically it. The biggest issue with this code is that it doesn't handle a change to your online/offline status, specifically, if you start the application offline, save some sightings, and then become online, it won't upload the old rows. That wouldn't be too hard to fix, but I was trying to keep it simple. At minimum, the next time you run the application it will upload those old records. For folks who want to see the entire code base, simply view the gist here: https://gist.github.com/3723074

I've also included a zip attached to this blog entry. (Note that the animated gif is courtesy of jQuery Mobile.)

Download attached file.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by jean-louis posted on 9/17/2012 at 2:52 AM

would you show us how to "simply" retrieve the list of ufo's names descript in an html list ?
thanks !

Comment 2 by Raymond Camden posted on 9/17/2012 at 5:45 AM

Sure - actually - it is going to be more difficult to the UI aspect. This "app" has one form. In order to add a list, I'd need to add some form of navigation to it. The actual act of asking Parse for the stuff is much simpler than the UI.

That being said - I wanted to work on this app a bit more anyway. This gives me an opportunity too. :)

Comment 3 by Raymond Camden posted on 9/17/2012 at 6:06 AM

Here is a quick example where I just wrote it as a separate HTML file:


It makes use of the Query API - https://parse.com/docs/js_g...

Comment 4 by jean-louis posted on 9/17/2012 at 8:45 PM

Great! Exactly what I was looking for !
thanks, jl

Comment 5 by mkhuda posted on 12/24/2012 at 10:56 AM

Thanks raymond ! its great example !
Actually, i want to parse data from Parse.com and then store it in a local database with onclick/submit button ! How to do it?

and Thank you for the tutorial ! :)

Comment 6 by Raymond Camden posted on 12/24/2012 at 9:19 PM

Well, my entries on Parse tell you how to get the data. So all you need to know is how to store it. In PhoneGap, that would be with WebSQL, their database support, and I've done a number of entries on that. Shoot, this blog entry shows you how to store into the db when you are offline - it is the exact same code to just plain store it.

Comment 7 by mkhuda posted on 12/25/2012 at 12:25 AM

this is my code,


after I pressed the query button, I get the query does not work well.

Comment 8 by Raymond Camden posted on 12/25/2012 at 12:50 AM

Your Parse.init line should only be run once, so you want to move that out.

You say the query does not work well. What exactly does that mean? I need a firm error to help.

Comment 9 by Raymond Camden posted on 12/25/2012 at 12:51 AM

Another issue. You are trying to insert a report.body (title) property but report doesn't exist. result[i] is your current object. I believe you also want to use result[i].get("title") to get the property. Check the Parse.com API docs to be clear.

Comment 10 by mkhuda posted on 12/25/2012 at 1:53 AM

oke, this is my complete code !


I have 4 rows in note Class. and i have successfully storing data from Parse to local SQL with 4 rows, But only one row data is store to local SQL.

Comment 11 by Raymond Camden posted on 12/25/2012 at 8:52 AM

Do you get any errors though? Have you tried using console.log to see where things fail?

Comment 12 by mkhuda posted on 12/25/2012 at 9:07 AM

i dont get an error, im using google chrome console..
and the javascript code is running well.

Comment 13 by Raymond Camden posted on 12/25/2012 at 9:56 AM

I'd add a bunch of console.log messages. If you want, you can send me your code (with the real app ids, I won't share) and I'll run it here... Wednesday.

Comment 14 by mkhuda posted on 12/25/2012 at 10:55 AM

this is my project files:


Thank you in advance for your help and your time

Comment 15 by Raymond Camden posted on 12/26/2012 at 11:01 PM

I just tested and it is entering data just fine. How are you testing? For me, I go into Chrome Dev Tools, Resources, Web SQL, I clicked on the db name, then click on the table name (maybe double click) and I see rows of data from the inserts.

Comment 16 by mkhuda posted on 12/27/2012 at 3:59 AM

I have 4 rows in Parse.com DB.
this is my Parse rows:
but why only the last row inserted into my local SQL ?
[body: mk] [title: mkhuda] counted 4 times.

Comment 17 by Raymond Camden posted on 12/27/2012 at 10:36 PM

Ohh, yeah, this is an issue with the transactions. Here is a fixed version. Note what I commented out and how I changed it.


Comment 18 by mkhuda posted on 12/28/2012 at 1:15 AM

Wow, amazing !
Thank you very much !

Comment 19 by Anthony posted on 1/30/2013 at 8:24 PM

Hi Ray, I haven't used the Parse JS api but the IOS api has a "saveEventually" method that tries to send the object immediately to parse and if a network connection is not available the object is stored locally in an on-disk cache until it can be delivered to Parse.

Comment 20 by Raymond Camden posted on 1/30/2013 at 8:39 PM

Just checked and it doesn't exist for JS. I bet you could add support for something like this. Just add the obs to a queue and ping the network every 5 minutes. Or listen for an online event.

Comment 21 by xavier posted on 2/22/2013 at 10:00 AM

Thanks for the great tutorial. I am planning my mobile app in such a way that data from our main database will be pushed to Parse and the mobile app will only interact with the data in Parse and will not hit our main database. End of the day, there will be a "download" from Parse to the main database also to keep things synched.

I guess i will have to use Parse.com's Rest API for the data movement back and forth from our database to Parse. I would like to use Coldfusion queries for this. Parse has examples with Curl and Python. So, just reaching out to you for your thoughts on this. Thanks in advance for your time

Comment 22 by Raymond Camden posted on 2/22/2013 at 4:48 PM

I could have sworn I had shown an example of this before, but I couldn't find it. Will make a simple demo today.

Comment 23 by xavier posted on 2/22/2013 at 9:09 PM

Thanks for your time and attention. Looking forward to it.

Comment 24 by Raymond Camden posted on 2/22/2013 at 9:12 PM

Xavier, please check out this: http://www.raymondcamden.co...

Comment 25 by Mkhuda posted on 4/26/2013 at 10:58 AM

I remembered this article,
and I thank you for helping complete the final project in my college.

Thanks, Mr. Raymond for your teaching ! :)

Comment 26 by Raymond Camden posted on 4/26/2013 at 2:28 PM

You are most welcome.

Comment 27 by muhaimin cs posted on 7/22/2013 at 12:52 PM

i just test your sample from download file. It seems your sample has created 2 same object of Web SQL. Is it bcos i refresh the page? One more thing how do i let the page know the connection to internet is ready. Thanks

Comment 28 by Raymond Camden posted on 7/22/2013 at 3:39 PM

If you are offline and save two things then yes you will get two objects. Outside of that I may not be reading you right.

As for letting the page know a connection is ready, you can use the online/offline event handlers. My app would be improved if it noted that.

Comment 29 by muhaimin cs posted on 7/23/2013 at 8:01 AM

how about if user close the safari, will the saved data still available in Web SQL?

Comment 30 by muhaimin cs posted on 7/23/2013 at 9:52 AM

sorry to bother you again. how about associate the entries with a photo snap on tablet?

Comment 31 by Raymond Camden posted on 7/23/2013 at 3:45 PM

WebSQL persists, yes.
Your second comment doesn't make sense to me.

Comment 32 by muhaimin cs posted on 7/25/2013 at 5:24 AM

thanks raymond

Comment 33 by Georges posted on 9/29/2013 at 6:58 PM

hey awesome post . i just have one question though.
if i want to load images from object how do i do it ?
i added this code as it is described in parse.com but didn't work .
s+= '<b>ID:</b> '+ result.imagePlaceholder.url() + '<br/>';

any thoughts ? Thanks

Comment 34 by Raymond Camden posted on 10/1/2013 at 12:16 AM

Hmm - have you tried what I did here - http://www.raymondcamden.co...

Comment 35 by joseph david posted on 11/12/2013 at 4:18 PM

Hi Raymond,
Thank you for your code. I try to send Sighting to your parse, have sightName : Joker there?

I just want try to connect database. I can not connect parse n codeigniter with javascript. Could you help me? Thanks

Comment 36 by Raymond Camden posted on 11/12/2013 at 4:27 PM

You should be able to check the response via your browser dev tools. If you don't see a successful response, then it didn't work.

As for CodeIgniter, I don't use it.

Comment 37 by marciokoko posted on 7/28/2015 at 2:46 PM

Hi. Im trying to save some data to parse.com without the sync to local store. So I basically used your code and commented out the last bit referencing the db.transaction(function()). The form submits but nothing gets saved. Im wondering if Im using the right AP keys, ApplicationID & Client Keys? I did create a SightingObject class and the 3 respective columns.

Comment 38 (In reply to #37) by Raymond Camden posted on 7/30/2015 at 1:36 AM

Sorry - I haven't used this code in years.