Design approaches to web applications revisited
Traditional Way
Traditional web applications consist of several “pages”. Each page requires external resources such as CSS, Javascript and image files to be fetched. Every time we load a web page those resources are also loaded with it. So whenever you navigate from one page to another, all these resources are loaded again and most of these resources are static. They are always the same, yet, they are reloaded every time you navigate in the web site.
Implementations and specifications are evolved to remedy these design problems. Each resource was retrieved by a separate connection in HTTP 1.0, it was only since HTTP 1.1, we started to re-use existing connections to retrieve additional resources. Even then, to be a good network citizen, browsers only open limited number of connections to an origin, so additional resources are queued and fetched in serial fashion anyway.
Additionally, we started to use HTTP caching directive headers aggressively to avoid re-fetching of the resources.
Furthermore, we enabled gzip compression to compress textual data in the server before sending it to the browsers which support it.
But no matter what you do, even if you avoid re-fetching resources completely, the browsers reloads them from its cache. This means they are processed again and cause visual imperfection, as you can notice the page is being re-rendered.
Furthermore when we reload the page, it is a challenge to keep the state of all the visual components. This is basically due to HTTP is being an inherently stateless design.
This is, obviously, a severely broken way. Web’s needs have changed and this design is clearly far from ideal for today’s scenarios.
A more modern approach
For the past few years, I’ve adopted another design approach which today’s web giants such as Facebook, Twitter and finally Google too have adopted to some degree.
Even though it has existed before that, a new technique called Ajax emerged around 2005 which is basically just a feature that you can use to send web requests in the background without reloading the web page and get the result back. So, with this technique, you could retrieve new information, such as live stock data, in the background and update some part of the page without reloading it completely.
This was actually a ground braking approach. It remedies most of the flaws of the traditional way of web application development. Yet, it took quite some time for people adopt and really use it. For instance, Google Ajaxified Youtube’s comment system just a few years ago. Even though, Facebook used Ajax for many of its tasks for a very long time, Facebook recently Ajaxified most of its interface, such as when you click on a photo, it just pops up a dialog right now instead of sending you to another page — which was the case about a year ago.
I do believe that ideal web application is just a single static web page which can be served with very high speed, redundant technologies such as CDN servers which have no dynamic page creation ability. After the web application is loaded, it’ll communicate with the back-end server via Ajax requests in the background. I’ve used this technique successfully and the result was amazing, it was the fastest user experience I’ve ever seen in a web page. I’ve engineered whole thing to be as fast as possible.
Try it for yourselves, I was playing with a toy project which is now in hiatus status, which is hosted at http://beta.gettingback.at/. Go ahead and test the speed for yourselves. Once, it is cached, It takes 600 ms for the whole page to be loaded and rendered completely, from a server which is 2600 km away. Clicking on any link on the page just pops up another dialog to show you the information you are requesting. If you design your application smarter, you can fetch-ahead some of the data user is most likely to click so the experience can get even more faster. The responsiveness and perceived speed is outstanding.
A single static web application
Benefits
- Lower requirements. You can serve the UI file with the dummiest web servers available even with the lowest technology CDN servers.
- Minimal bandwidth usage. As all the communication is done over Ajax and all transferred information is strictly data and no repetitive, redundant information (such as the presentation logic — i.e. html). This cuts cuts bandwidth costs.
- Minimal server load, distributed computing. As presentation logic is completely on the client, server does not need to fiddle with templates and all sort of stuff which has a computational expense. Hence, in a sense, you are distributing computing expenses to the clients.
- Optimizable. Single static web page proved to be easier to optimize — more on that later.
Problems
- Lack of frameworks. Since this approach is not widely adopted there are lack of frameworks that dictate best practices.
- Back and forward buttons. As this approach keeps the user in one single page back and forward buttons’ job is rendered useless. Luckily enough, this was the first problem noticed with Ajax and had been worked on for a long time and solutions exist.
What’s the best way to communicate with the API ?
So, how should we design our API ? We most probably want our API to be accessible by the world as 3rd party developers will use it. This means cross-domain communication. If our API is at http://api.foo.com and another page such as http://static.foo.com tries to access it, it will be a cross domain communication.
What are our options for cross domain communication ?
I’ve studied the subject in my post Cross-domain data-push methods compared two years ago. Back then, using a transparent Flash as cross-domain communication proxy seem like the way to go to me. Mainly because:
- Flash can maintain a single connection and many request-response on it
- Flash has raw TCP connection so you can freely implement a bandwidth efficient protocol.
- Flash was a very ubiquitous technology which was available in %99 of the browsers — making it the most cross-browser method.
Though it proved to have some performance challenges.
Other dominant competing methods was JSONP and XHR.
JSONP is inherently cross-domain compatibly while XHR had cross-domain communication support with the introduction of CORS and IE introduced CORS support in IE8. Back at the time I wrote above comparison article IE6 and IE7 market share was %19,6 according to statistics hence rendering the XHR CORS method not available in at least %19,6 of the market. Now, things has changed and IE6 and IE7 market share dropped to %1.5 as of October 2012.
This makes XHR with CORS compatible with the vast majority of the market, most likely above %95. So, XHR is a serious choice again.
JSONP virtually has 100% browser compatibility.
Furthermore, JSONP and XHR are mobile device friendly unlike Flash. This leaves us two serious competitors for modern times, JSONP and XHR.
| JSONP | XHR CORS | |
| Browser compatibility | %100 | %95 (roughly according to statistics) |
| Error handling (failed requests) | Poor | Built-in |
| Ability to upload data without 2kb limit | NO (As it only supports GET method) | YES |
I believe it is justified to chose XHR technology for cross domain communication today.
YouTube API already has XHR support with this new access control specification. Though, Facebook still uses JSONP internally for most of its communication, probably because it is working just fine.
Caveats of XHR CORS
From now on, I’ll refer to XHR assuming it has CORS implementation.
Now, there are at least two types of XHR requests defined as I’ve already wrote about here.
- Simple requests: These are the requests with HTTP methods GET, POST or HEAD and have no custom HTTP headers in it.
- Not so simple requests: Any request who does not fit into above criteria is not a simple request and requires a preflight request (explained below). So, this renders strictly RESTful APIs which rely on PUT and DELETE methods less robust.
What is a preflight request ?
If browser decides it requires a preflight request, it first sends an HTTP request with method OPTIONS to ask if the server allows you to do cross domain requests to her then the actual request is executed. So a preflight request means doubling your number of requests for a resource. If you keep asking for the same resource (URL part before that #) preflight cache will be used and you’ll spare subsequent OPTIONS requests. But if you ask for different resources (foo.php?q=a and foo.php?q=b) then each of these requests will require preflight requests.
So what can be do about preflight requests ?
- We can use a simple requests which does not require preflight requests at all — that is only GET, POST, HEAD methods with no custom headers.
If that is not an option and we inevitably need preflight requests — for instance because we need to add custom HTTP headers to our request:
- We can use a single entry point to our api (api.php) and pass all the parameters via POST to keep the URL fixed hence there will only be one preflight request. Basically, we’ll just have http://api.foo.com/api.php and send all the parameters to that very same resource with POST parameters hence require only one single preflight request.
Sometimes, the JS libraries we use send custom headers by default. For instance ExtJS, Dojo and Prototype JS libraries were sending custom HTTP headers without consulting the developer, rendering a simple request complex one and hence requiring a preflight request. Maybe some of those libraries have some ways to stop this behavior, I haven’t check.
For instance, with ExtJS you can stop it using custom headers with the following property.
|
1 2 |
Ext.Ajax.useDefaultXhrHeader = false; Ext.Ajax.request({url: ............... }); |
Note that, this API is not documented for normal ExtJS as it is marked as a private API but it is documented in ExtJS Touch API.
Conclusion for server-side designs
You have two options
1) Single entry point API handler
I believe the most robust way is to have a single entry point and encourage using POST method only. All interaction will be done on your entry point so even if preflight is required, it will be done only once.
HTTP POST your parameters, such as {controller: user, method: search, query: foo} to http://foo.com/api.php. Note, that you are always interacting with the same URL. POST method is chosen over GET because it can reliably send more data than GET method.
This pattern contradicts with most of the server-side you’ll find today as they adopt MVC pattern with pretty URLs as their design choice.
2) Pretty URLs with strictly simple requests
The frameworks out there which adopted MVC pattern and preferred pretty URLs create many resources. This might have an impact on the performance as they might require separate preflight request for each resource such as http://api.foo.com/post/5 and http://api.foo.com/post/8 are both different resources and require separate preflight requests — unless they are strictly simple requests.
So, if we want to use multiple resource APIs, we should make sure our clients are sending simple requests. Otherwise we’ll perform poorly as a preflight will be required for each resource.
Final thoughts for server-side design
In many scenarios the only difference between the implementation of two options listed above is an HTTP rewriting module in the web server. As you can rewrite http://api.foo.com/user/add into http://api.foo.com/?c=user&m=add anyway. Their implementations are pretty much the same but the way they perform in different scenarios are different — as one of them would require preflight and the other won’t.
As for the CORS specification. I couldn’t figure out, why the CORS specifications require separate preflight requests per resource. I believe a domain/path wide access-control policy would be more robust.
If particular resources need special restrictions they could still do it, as each HTTP request has an origin header in it, per resource restrictions might be applied too.
I don’t know why it hasn’t been designed this way.
Client-side design
Everything is fine, we pretty much decided how should our API ideally be designed. Now, what ?
With the new web application design, server-side is just for data retrieval, so we need a powerful ways of designing our user experience in the client side.
There are many Javascript libraries out there, such as venerable jQuery but rarely they provide high quality UI components. Windows provides many controls, similarly, other frameworks such as Qt also provides tons of widgets that we can assemble together to build an application but Javascript ecosystem is just catching up.
If you don’t need many UI components such as Grids, Panels and layout managers you should definetely go with jQuery, it is a very innovative and intuitive library that would dramatically make your work simpler.
But if you need a more corporate-like professional applications, you want a consisten look among all your UI components and a big collection of components to chose from. In that respect, ExtJS comes into play. They provide a very high quality JS library. Yet, they naturally, have a much more steep learning curve.
How to glue client-side and server side
Most of the time you have to glue them together manually. This is one department we have to work on. I’m speculating ideas about to generate a JS API on the fly by server via Refletion APIs of the server-side languages. In fact, I’m playing with a server side framework idea does generate an API specification automatically, as you add more services to it. Here’s a sample output.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
{ "Project":{ "test":{ "parameters":{ "name":{ "optional":false, "type":{ "name":"String" } }, "surname":{ "optional":false, "type":{ "name":"String" } } } } }, "Session":{ "status":{ "parameters":[ ] }, "create":{ "parameters":[ ] } }, "User":{ "getStatus":{ "parameters":[ ] } } } |
In the mean time, ExtJS acted quicker and they propose a solution to this problem with Ext.Direct. I’ll study its design and probably write about it soon.