How we save bandwidth when using ApiOmat
In this article I would like to discuss how ApiOmat solves the problem of transferring unnecessary data between clients and servers. This is important to keep in mind with app development because of limited data flatrates and "white" areas with no network connection.
To minimize transferred data our REST interface and our mobile client SDKs support both the HTTP header If-Modified-Since and If-None-Match. How this works will be described below.
If-Modified-Since
On a GET request the client sends an additional If-Modified-Since header with a timestamp as the value. The server can react with the 2 following possibilities:
-
The server responds with response-code 200, a Modified-Since Header and the data in the body
-
The response-code from server is 304 and the body is empty
The second case will happen if the requested data on the server was not modified since the given time stamp. At ApiOmat this works for GET requests on single objects. The following cURL example will explain it (for simplification we use -I option to send a HEAD request and get only HTTP headers back):
$ curl -I https:
//apiomat
.org
/yambas/rest/apps/TaskManager/models/TaskManagerMain/Task/51482cbae4b0891c605f4099
\
-u __user_creds__ -H
"Accept: application/json"
-H
"X-apiomat-apikey: __key__"
HTTP
/1
.1 200 OK
Date: Tue, 19 Mar 2013 09:21:07 GMT
Last-Modified: 19 Mar 13 10:18:33:629 CET
Content-Type: application
/json
Vary: Accept-Encoding
Content-Length: 0
Server: Jetty(7.6.9.v20130131)
#Send same request again with If-Modified-Since Header and value from Modified-Since Response
$ curl -I https:
//apiomat
.org
/yambas/rest/apps/TaskManager/models/TaskManagerMain/Task/51482cbae4b0891c605f4099
\
-u __user_creds__ -H
"Accept: application/json"
-H
"X-apiomat-apikey: __key__"
\
-H
'If-Modified-Since: 19 Mar 13 10:18:33:629 CET'
HTTP
/1
.1 304 Not Modified
Date: Tue, 19 Mar 2013 09:23:04 GMT
Vary: Accept-Encoding
Server: Jetty(7.6.9.v20130131)
#HTTP Response-Code is 304
Possible values for If-Modified-Since Header
The HTTP standard specifies that timestamp should be sent according to RFC 1123 standard. This format, however, only handles down to seconds. This is not enough for requests against ApiOmat REST interface, so we accept only the following format which includes also milliseconds:
dd MMM yy HH:mm:ss:SS CET
An example follows:
19 Mar 13 10:18:33:629 CET
Etag/If-None-Match Header
How you can check if a list of objects has changed? If you send an If-None-Match header with your GET request on a list, then the server answer with one of the two following possibilities:
-
Returns the data, response-code 200 and an ETag header
-
Returns 304 as response-code and NO data if data hash is equal (and so not modified) to sent If-None-Match header value
#Get list of task models
$ curl -
v
https:
//apiomat
.org
/yambas/rest/apps/TaskManager/models/TaskManagerMain/Task/
\
-u __user_creds__ -H
"Accept: application/json"
-H "X-apiomat-apikey: __key__
HTTP
/1
.1 200 OK
Date: Tue, 19 Mar 2013 09:32:29 GMT
ETag:
"1-2114886209"
Content-Type: application
/json
Vary: Accept-Encoding
Transfer-Encoding: chunked
Server: Jetty(7.6.9.v20130131)
#send request again with If-None-Match header and ETag value from previous HTTP response
$ curl -
v
https:
//apiomat
.org
/yambas/rest/apps/TaskManager/models/TaskManagerMain/Task/
\
-u __user_creds__ -H
"Accept: application/json"
-H "X-apiomat-apikey: __key__ \
-H
"If-None-Match: 1-2114886209"
HTTP
/1
.1 304 Not Modified
Date: Tue, 19 Mar 2013 09:38:08 GMT
Vary: Accept-Encoding
Server: Jetty(7.6.9.v20130131)
...
#HTTP Response-Code ist 304
Support caching in client SDKs
If you use our iOS, Java or Android SDK then everything is already prepared to save bandwidth, because we sent the previous explained headers. A local cache keeps all returned data and returns the values if response-code from server is equal to 304.
If you want to disable the caching explicit, then you have to call the following method for iOS:
[DataStore setCachingStrategy:AOM_NO_CACHE];
For Android and Java the method call equals to:
Datastore.setCachingStrategy( Datastore.AOMCacheStrategy.NO_CACHE );
As above seen, we are commited to minimalizing the transfer of unnessecasy data between customers and servers, especially in light of limited data flatrates and "white" areas with no data connection.