API-based Web Application with Backbone, Require.js and Slim framework

A single-page application (SPA) was something I’ve been exploring for the last few months. I always liked the idea of moving certain responsibilities to the client’s side. After all, why would you like to waste server’s RAM/CPU to buil a HTML page (and pay for a transfer to deliver it) when a web browser is perfectly capable of doing that on its own? You will not only save money but also provide a better user experience. In addition to the performance, moving the presentation layer to the web browser gives a clearer division between back-end and front-end code.



Recently I came across an extremely useful boilerplate for Backbone.js created by Thomas Davis. The boilerplate brings together one of the most popular (and useful) front-end libraries:

It’s a great start because if you are new to those technologies it’s really hard to guess what would be the best way to put everything together. I used it as a foundation to create an example App which talks to a PHP back-end.

The back-end could be a plain PHP but instead I went for the Slim micro framework. It’s fast, very easy to learn and will enforce a better code structure. After all, this is one of the best use cases for a micro framework.

Before I will go into details get a copy of the example from GitHub and install it on your web server.

$ git clone https://github.com/lukaszkujawa/api-based-application.git
$ cd api-based-application/
$ php composer.phar install
$ sudo vim /etc/apache2/sites-available/your-vhost

        ServerName 192.168.186.133
        ServerAdmin admin@localhost

        DocumentRoot /home/lukasz/projects/api-based-application/public

        
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all

                RewriteEngine On
                RewriteBase /
                RewriteRule ^index.php$ - [L]
                RewriteCond %{REQUEST_FILENAME} !-f
                RewriteCond %{REQUEST_FILENAME} !-d
                RewriteRule . /index.php [L]
        

$ sudo a2ensite your-vhost
$ sudo /etc/init.d/apache2 reload

When you open the page in your web browser you should see something like this:

A very minimalistic website with 3 links in the main content area. You can add some more if you follow the “create” link in the gray nav bar.

While you’re switching between pages to submit a new URL, notice that your web browser won’t reload. All front-end code is stored in JavaScript. When the web browser needs to talk to the web server it does it in the background with AJAX. It makes the application very responsive and limits number of requests.

To understand how it works lets have a look into “public/index.php“. The script defines 3 endpoints:

The first one returns a JSON object with all available links. In the real live it would look for them in a database but for the sake of simplicity I’m using a session. The second one handles POST request and stores links in the session. The last one is the simplest. It renders “templates/index.php” for all requests to the root “/”.

The template is an almost empty HTML5 file.





  <!--

  
  

  Backbone Boilerplate
  
  

  http://js/libs/require/require.js


  
  

The only interesting line here is:

http://js/libs/require/require.js

This is where the front-end gets initialised. It loads the Require.js library and refers it to js/main.js.

If you’re not familiar with Require.js it’s a module loader. Small but powerful library for breaking JavaScript code into multiple files/modules. If you want to learn only one new front-end framework this is where I would start. It has a good documentation and will help you organise your JavaScripts.

The next file we are going to look at is obviously “public/js/main.js”.

require([
  'views/app',
  'router',
  'vm'
], function(AppView, Router, Vm){
  var appView = Vm.create({}, 'AppView', AppView);
  appView.render();
  Router.initialize({appView: appView});  // The router now has a copy of all main appview
});

It loads 3 modules: views/app.js, router.js and vm.js.

Router.js is the heart of the application. It defines available pages and assigns them with the appropriate views.

var AppRouter = Backbone.Router.extend({
    routes: {
      // Pages
      'create': 'create',

      // Default - catch all
      '*actions': 'defaultAction'
    },

    instance: null

  });

The AppRouter extends Backbone.js router which is documented on the official website.

router.on('route:create', function () {
        require(['views/create'], function (CreateView) {
          console.log( "router::create" );

          var createView = Vm.create(appView, 'CreateView', CreateView);
          createView.render();
        });
    });

router.on('route:defaultAction', function (actions) {
      require(['views/index'], function (IndexView) {
        console.log( "router::defaultAction" );

        var indexView = Vm.create(appView, 'IndexView', IndexView);
        // indexView.render();
      });
    });

In this example we have only one custom route – “create“. It’s handled by “public/js/views/create.js“. Any other request will be resolved to the default action handled by “public/js/views/index.js“.

The index view does two things: initialise and render. When the object is initialised it’s calling LinksModel (public/js/models/links.js) model to fetch all links from the PHP back-end.

model: new LinksModel(),

initialize: function() {
        console.log( "controllers/IndexController::initialize()" );

        var context = this;

        this.model.fetch({
                success: function () {
                        context.render();
                }
            });

    },

Once a JSON object is successfully pulled from the web server the “public/templates/index/index.html” template is parsed and rendered with Lo-Dash template function.

render: function () {
        console.log( "controllers/IndexController::render()" );
        console.log( indexTemplate );
        console.log( this.model.toJSON() );

        this.$el.html ( _.template( indexTemplate, this.model.toJSON() ) );
}

The LinksModel I mentioned above extends Backbone.js model. Backbone models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control. It’s a very useful and important entity so try to learn more about it.

define([
  'lodash',
  'backbone'
], function(_, Backbone) {

        var linksModel = Backbone.Model.extend({

           defaults: {
              links: []
           },

            url: "link/latest",

            initialize: function(){
                console.log("Links model init...");
            }

        });

        return linksModel;

});

In our example the model defines a data type called “links” which is an array and a URL “link/latest” to populate the defaults from.

As you can see it’s all quite simple. The only problem is that you have to embrace multiple JavaScript frameworks at once but the boilerplate makes it much easier. What I really like about this setup is the structure. It’s similar to how things are done in the back-end and it should be fine even with big applications.

I know that some developers feel anxious about writing one application in two languages simultaneously. I can sympathise with them because I prefer simple solution. The problem is that we like it or not, the web applications will have more and more JavaScript code. As the old saying goes – “if you can’t fight them, join them”. With the latest front-end frameworks you can improve user experience, cut hosting costs and have a well structured code. Single-page applications will probably become the industry standard in the next few years so you have all the reasons to give it a go.