Your API design is a silent killer
When building applications for mobile devices the first consideration is usually the screen size. This is very different from desktops but an equally critical distinction is how these mobile devices connect to the internet. Mobile internet connections tend to be unstable, have massive fluctuations in their latency and are frequently offline. We should be enabling our applications to work as best as possible despite these challenges.
Proper offline support isn’t as simple as turning on the app cache or storing JSON requests to disk. It’s critical to design a proper architecture before a single line of code is written. For that there’s one key rule to be enforced above all else: statelessness!
Stateless models and a stateless API design are imperative for proper offline support. To illustrate this, we’ll build a fictitious app to gamify doing homework. Like any tried and true gamification app, it needs badges. As a sample we’ll focus on just one badge, the “100-page Term Paper Merit Badge”. In order to earn this badge a series of 7 events need to be completed: Gather Research, Write Introduction, Write Body, Write Closing Arguments, Footnotes, Editing/Rewrites, and Achive a Passing Grade.
The sample psuedo-code of the API implementation is written in a Python micro-web server framework called Bottle. The transport written in standard HTTP and data models are plain JSON. We’ll just be focusing on the create and update methods.
API Version 1
Interface
@post(/courses/<course_name>/students/<student_name>/badges) def Badges_Create():
@put(/courses/<course_name>/students/<student_name>/badges/<badge_id>) def Badges_Update(course_name, student_name, badge_id):
Transport/Data
URL: /courses/apis-101/students/bobby/badges
Method: POST
Content-Type: application/json; charset=utf-8
Accept: application/com.rga.gamify.api+json;ver=1
Body: { id:1, name:"100-Page Term Page", eventsTotal:7, eventsCompleted:0 }
URL: /courses/apis-101/students/bobby/badges/1
Method: PUT
Content-Type: application/json; charset=utf-8
Accept: application/com.rga.gamify.api+json;ver=1
Body: { eventsCompleted: 3 }
Do you spot the flaws? The “eventsCompleted” value is stateful. It can’t be 2 things at once. You can’t change the order of the events. You can’t handle conccurent operations. You change enable 2 different users to change the data at the same time. Conflict resolution is impossible as well.
We had this issue come up on a recent project using 3rd party back-end. In the end to enable offline support we were forced to create a serial queue of the events, ask the server for the state, send the updated state, validate the state with the server and then move on to the next event in queue. If that sounds horrendously tedious, it is. With a few simple improvements to the API we can allow for batching all the event changes and get us off on the right foot to handle conflict resolution.
There are a lot of ways we could tackle improving this. For now let’s take a simpler approach since this is just a prototype application. It will allow for the flexibility of a stateless model and if/when we need enhanced functionality is need, we can easily break off into creating multiple models and hierarchies.
API Version 2
Interface
@post(/courses/<course_name>/students/<student_name>/badges)
def Badges_Create():
@put(/courses/<course_name>/students/<student_name>/badges/<badge_id>)
def Badges_Update(course_name, student_name, badge_id):
Transport/Data
URL: /courses/apis-101/students/bobby/badges
Method: POST Content-Type: application/json;
charset=utf-8
Accept: application/com.rga.gamify.api+json;ver=1
Body: { id:1, name:"20-Page Term Page", events:["research","into","body","closing","footnotes","revise","passing score"], eventsCompleted:[] }
URL: /courses/apis-101/students/bobby/badges/1
Method: PUT
Content-Type: application/json; charset=utf-8
Accept: application/com.rga.gamify.api+json;ver=1
Body: { eventsAdd: ["body", "closing"], eventsRemove: ["research"] }
Now we’ve moved from a summation of events to talking about individual actions to change on the array. The application can track changes made offline as unique actions and then send those individual actions once the mobile device is online. Batching changes together is also possible in order to save on data transfer and server processing time. Lastly we have enough detail that we can resolve conflicts if/when they occur.
Offline and semi-connected devices are becoming the norm. Keeping our data models and API designs state-less is the first step in enabling end-users the flexibility to work when ever and where ever they are.

