The Dojo Offline API

[Note: This blog post is out of date. For up to date information on Dojo Offline please see the official web page.]

The last few weeks we’ve been putting together our API for the Dojo Offline Toolkit (DOT). How will a programmer use this toolkit in their work? How will it be integrated into their applications?

Last week we reported on addressing usability for offline access, with offline mockups of popular web apps. Usability is just as important for programmers as it is for end-users, it just takes a different form: the API, or Application Programmer Interface. Getting the API right is just as important as the UI; programmers need the love too.

Look Mah, No Proxy!

Before we dive down into the API, I want to share a nice surprise: the Dojo Offline Toolkit API has been designed to not necessarily need a web proxy. For example, if your browser has native support for offline access, then we don’t need to download the small web proxy — the browser will simply cache these offline resources. The plan for Firefox 3 is to natively support such an API — in this scenario, Dojo Offline could simply use the browser’s offline cache rather than requiring you to download the web proxy.

Here’s an even nicer thing: as soon as we finish the JavaScript API you see in this blog post, you can start using Dojo Offline even without the full, local web proxy being done. How is this possible? Well, if you use the trick discovered by Julien Couvreur and set your web server headers to correctly send HTTP/1.1 caching info (like my Moxie application does), then the browser cache will store your files for offline use. This is appropriate for prototyping and getting beginning applications out there; the web proxy is an optimization meant to improve the reliability of offline cached files. Without the web proxy browser cache misses are possible, which would translate into UI resources not being available when offline, but this might be good enough for prototyping and for some applications (in fact, Scrybe works this way if you look carefully at their screencast).

And here’s the really nice thing: when the full web proxy becomes available, your application will require very little change (in fact, one change, as you will see below) to gain the improved UI reliability the web proxy offers.

Let’s quickly see how DOT achieves this generality. DOT refers to the ability to truly store web files offline as a durable cache, rather than a web proxy; a durable cache is a more generic term, since it could be backed in any number of ways: by the web browser with native support or by the DOT local proxy. By default, DOT requires a durable cache; this can be changed with offline.requireDurableCache = false. During startup, the default UI queries if a durable cache is required; if so, we see if one is available; if it is not, we prompt the user to install one. When a durable cache becomes available (such as the DOT local proxy), the toolkit can simply prompt the user to download it.

KISS – Keep It Simple

Let’s start with the simplest example of programming against Dojo Offline. The philosophy behind the Dojo Offline API is to have sensible defaults for everything (thanks DHH!), while providing framework configuration hooks that can be overridden if you want to take things over and go past the defaults.

Our example application will be a kind of web-based Outlook, with emails and tasks. Users can read and create emails offline, and also update their task list when away from the network. Everything below will be pseudo-code to illustrate how Dojo Offline can be used. Please keep in mind that this is just the planned API and is not available yet — this is all design work in preparation for coding this week; also note that the API is sure to change both during development as well as after the first version is released. Expect it to evolve in mid-air as we slam into reality.

Let’s look at the full pseudo-source code for our example application, which we will break down and explain in pieces. Don’t worry — all of this will be explained in detail below.

Our HTML file will look something like the following:

Parts of my application's UI here
Bookmark and Share

16 Responses to “The Dojo Offline API

  1. Steve Yen says:

    Brad, nice API. I used to push on the edge of the Single-Page-Application model for offline access, until other priorities and projects popped up. I started a task tracking app called Next Action: http://trimpath.com/project/wiki/NextAction that I hope to one day upgrade to Dojo Offline Toolkit.

    Like you, I used the negative-number trick to handle records created while offline. It’s ok, but doesn’t fully solve the conflict/merge hair-pulling.

    One API thought: there could end up being lots of dual code paths, with much “if (offline.isOnline())-then-else” testing. How about something like (handwave)…

    emails = offline.getDataStore(“email”,
    { ajaxy-CRUD-request-info-and-handlers-when-online });

    function displayEmails() {
    var displayMe = emails.find().items;
    // do something with array of emails
    }

    That might help to reduce the online/offline if-then-else across the codebase?

  2. Adam Peller says:

    Brad, great stuff.

    I noticed your code lives in a top-level package. I might have missed it, but I gather you’re trying to keep your APIs (and perhaps your implementation?) separate from the Dojo Toolkit. Is that a stated design goal? Being able to use it against other toolkits makes a lot of sense. I wonder, though, about the namespace issues. Are you a top-level namespace because you plan to have this code in a top-level Foundation project? It’s a nit-pick, but is there a convention for scoping top-level names which end up in the global namespace?

  3. Brad Neuberg says:

    Steve: Good to hear that the negative number trick has been used before. In our case when the client has sent a new item over to the server, the server creates that item; in the response log, the server first directs the client to delete the old negative item, then create a new entry that has the new correct ID (you will notice that this ‘create’ request has a small, new field in the item: ‘origID’).

    About making more convenience methods like you suggest; these are good, but at this point I think I want to just get the API out there and see what sticks against the wall. We can imagine all sorts of cool ways to make this kind of coding more transparent, but these can come in later iterations.

    Adam: I originally adopted ‘offline.*’ because my paths were getting too long, such as “dojo.offline.file.put”. I might actually just go back to “dojo.offline” when I start coding tomorrow.

  4. Brad Neuberg says:

    Oh Steve, just realized you did NextAction; thats a great app! I used that for a long time as my task tracker.

    As an experiment, are you interested in working with me to see what it would look like to hook a server-side component + Dojo Offline into NextAction? As I roll the API out in the comming weeks, it would be useful to see how it interacts with real apps.

  5. Brad Neuberg says:

    Adam: We both went to the same college, though I was a few years after you. Do you still live in NYC?

  6. Steve Yen says:

    Sure, would love to collaborate!

    btw, I’ve been recently looking at the new REST API’s in ruby on rails 1.2 on a current project, so, a warning that that stuff might infect my thinking on how a server conversation might look. Laziness reigns.

    More feedback on sync…

    - With the sync message handshaking, I worry whether the server would need to remember a lot of unique offline state per client. (“I, the server, last sent Alice’s client this set of snapshot information. And, I also last sent to Bob this other snapshot.” etc) The server, now thinking it knows what Alice and Bob know, can now correctly generate sync reply messages — until something gets messed up. One idea to help us create optionally simpler and more braindead servers (with admittedly less efficient messaging): in the synch reply message from the server, perhaps have some way for the server to tell the client to instruct ‘wipe out everything you know because here’s a brand new complete set of data’.

    - On negative id numbers and joined information… Take, for example, a sales-force CRM app. A field sales rep is visiting new prospects and is offline. He enters in some new order quotes, and also creates some new customer records, too. Assume that the new price quote records are ‘linked’ or have some foreign-key like id ‘pointers’ to navigate to the new customer records. During the sync, you not only have to fix up the primary negative number ids of the records, but also those negative linked foreign-keys ids. One solution idea: make the negative numbers space unique across the entire offline system. So instead of id’s like: new_order_ids = [-1, -2] and new_customer_ids = [-1, -2]. Make it look like: new_order_ids = [-1, -2]; new_customer_ids = [-3, -4]. In Next Action, we use a rails-like naming scheme where any ‘field’ that ends with an ‘_id’ suffix gets examined during sync if it has a negative value. So, order[-1]['customer_id'] would get corrected during a sync.

    - Finally, an example where negative numbers aren’t enough: in the same sales-force CRM app, two field sales reps sync up to get their morning snapshot of inventory data (say quantity 10 widgets) and each hit the road. Separately, they take orders for the widgets for different customers, say quantity 8 and 6. When they sync later, someone loses. You’d definitely would want some UI to let the loser know. And, you can’t just delete the losing order. Easiest solution: punt — this problem is left as an exercise to each app developer, ha ha. :)

  7. [...] Dojo won me over with the slick interfaces, abundance of functionality, documentation for integrating with symfony, strong contributors and community, and potential of Dojo Offline Storage. Symfony brings … [...]

  8. Brad,

    After looking at your sample client sync request, I thought that it might be useful for you guys to keep in mind that PHP (for example, I’m not sure about other server side stuff) has a JSON decoder, but it expects keys in quotes. Read more (particularly in developer comments) here: http://us2.php.net/manual/en/function.json-decode.php .

    I’m really looking forward to using this!

    -Matthew Foley

  9. Brad Neuberg says:

    Matthew: Thanks for telling me that; I did not know that.

    I have found a way to significantly simplify the syncing model, resulting in much less server work and no actual custom sync protocol using JSON. More tomorrow (Monday).

  10. Simon says:

    Ugh:

    if(offline.isOnline() == false){
    if(offline.isOnline == true){ // looks like there is a typo in this line

    How about a more pragmatic:

    if(!offline.isOnline()){
    if(offline.isOnline()){

  11. [...] To start using Dojo Offline now in conjunction with the browser cache, you must have the following in your JavaScript code: dojo.off.requireOfflineCache = false. You must also turn on HTTP caching headers on your web server; how to turn on these HTTP headers and which ones to turn on are explained in the Dojo Storage FAQ. See the Moxie code links below for more examples of how to use the API. Note that the Dojo Offline JavaScript API has changed, especially for syncing, since our API blog post about a month ago and has become much simpler — see the Moxie source for details. The demo of Moxie shown in the screencast above can also be played with right in your browser. Please note that both Moxie and Dojo Offline are still alpha; you are literally seeing Dojo Offline being developed in front of your eyes, and glitches remain in both of them. Please debug and provide test cases for errors you find to help development. [...]

  12. Brad Neuberg says:

    Simon: Hi; the API has changed quite a bit since this blog post, becoming much simpler and straightforward. It is now dojo.off.isOnline, so you could have if(dojo.off.isOnline == true){}

  13. [...] There has been many ideas about a offline storage mechanism for web applications. Dojo implemented this in its Library. Firefox 3 promises this. Now we have a new arrival in this area – Google Gears. Unlike Dojo’s implementation, Gears require an extension for it to work. Google Gears is an open source browser extension that lets developers create web applications that can run offline. [...]

  14. [...] headers within HTTP to store objects within the browsers cache. Javascript libraries such as Dojo implemented support for offline web applications using the same principals, but applications were very limited in scope as there was no easy way to [...]

  15. [...] headers within HTTP to store objects within the browsers cache. Javascript libraries such as Dojo implemented support for offline web applications using the same principals, but applications were very limited in scope as there was no easy way to [...]

  16. wu says:

    I wondered why there isn’t a off line API for dojo??

Tell Us What You Think

*
*