Adventures in Gotham City


The choice of client side MV* framework ain’t an easy one. And fairly often a new one comes along. Among the usual suspects we find frameworks like Backbone, Knockout and Spine. In this post we’ll setup a playground for another client framework. Batman. This using Visual Studio and ASP.NET Web API.

Batman is a client MVC framework by Shopify, written in coffeescript. In other frameworks you need to bring your own parts for templating, module loading and routes. Batman comes with its own representation of all these parts included. But that’s far from all!  You could read more on how Batman differs on the shopify blog.

ASP.NET Web API was introduced with the ASP.NET MVC 4 beta, building on the blocks of the former WCF Web API. It helps you building HTTP API’s on the .NET stack.


We’re going to use one of the samples from Batman, using coffeescript in Visual Studio. Then we’re going to swap the samples use of localstorage to a simple Web API. The goal is to set this up and leave with a playground for further investigation.


The starting point is an MVC 4 application. First we need to add Batman. This ain’t on nuget(NuGet), so we need to download it from github. The sample we’re going to use is also in the repository.

We’ll use the files batman.js. and batman.jquery.js. Batman has no dependency on jQuery, but if you’re planning to use jQuery this version is a bit smaller than batman.solo.js. Next is support for coffeescript, we’re going to write coffeescript in VS an let a HttpHandler compile on the fly, this by installing CoffeeSharp via nuget. To get slightly better experience when writing coffeescript in VS there is also a highlighting extension, CoffeeLight, to be found in the extensions manager.


Now we’re ready to write the coffeescript sample code. Or copy and paste code to start with.

We copy the example BatMap from the Batman repository to a file in our scripts folder. We also copy the markup and script references and updates the index view of our project.

The view then looks like this (note how the templates looks a bit like knockout);

@section Javascript {


is batman

The looks like:

    # Create our application and namespace.
    class BatMap extends Batman.App
      @global yes

      # setup our root route. When the app starts up, it will automatically call AppController::index
      @root 'app#index'

      @on 'ready', ->
        console.log "BatMap ready"

    class BatMap.GoogleMapsView extends Batman.View
      html: '<div style="width: 200px; height: 200px;"></div>'
      address: ''
      zoom: 12

      render: ->
        @node = $(@get('node'))
        @address ='address')
        @geocoder = new google.maps.Geocoder();

        @geocoder.geocode {'address': @address}, (results, status) =>

          if status is google.maps.GeocoderStatus.OK
            myOptions =
              zoom: @zoom
              center: results[0].geometry.location
              mapTypeId: google.maps.MapTypeId.ROADMAP
              disableDefaultUI: true

            map = new google.maps.Map(@node.children("div")[0], myOptions)
            new google.maps.Marker position: results[0].geometry.location, map: map


    class BatMap.Character extends Batman.Model
      @global yes

      @persist Batman.LocalStorage
      @encode 'name', 'isBatman', 'address'

      name: ''
      isBatman: false
      address: ''

    class BatMap.AppController extends Batman.Controller
      emptyChar: null

      index: ->
        @set 'emptyChar', new Character

        # add some example characters to show off.
        Character.load (error, characters) ->
          # you always want to make sure you handle errors (more elegantly than this) when writing connection code
          throw error if error
          unless characters and characters.length
            callback = (error) -> throw error if error
            new Character(name: 'Bruce Wayne', isBatman: true, address: "11 Wall Street New York, NY").save(callback)
            new Character(name: 'Dick Grayson', address: "Alcatraz").save(callback)
            new Character(name: 'Alfred Pennyworth', address: "Sydney, NSW").save(callback)
        @render false

      create: => (error, record) =>
          throw error if error
          @set 'emptyChar', new Character

    # Start the app. This will start up the dispatcher and a number of other mechanisms.

Now this should be ready to run. You could validate that the script compiles correct by visiting /Scripts/

When you run this you should be presented by a simple UI, where you see three maps and an input for a new character.

If we take a look at the sample code, we see that Batman has the MVC concept as well as a notion of an app. On app level we configure routes. Later it is possible to configure model loading at this level. The model has a configuration for persistence. Last but not least we have the that starts our app. In the controller we see that the application will load characters, when no character is found, it would create three and save.

The sample uses localstorage for the characters, so running the application will store the three initial characters.



We’re going to change this demo to use another provider for model persistence. Batman comes with another provider RestStorage. To use this we need to change our model class.

class BatMap.Character extends Batman.Model
    @global yes
    @primaryKey: 'id'
    @persist Batman.RestStorage
    @encode 'name', 'isBatman', 'address'
    @url = '/api/characters'
    name: ''
    isBatman: false
    address: ''

We add a primaryKey configuration to point to an id property. Then we switch the localStorage provider to RestStorage. Last we need to point to an HTTP endpoint. The RestStorage will the use HTTP GET, POST, PUT, DELETE with that endpoint.


We’re now going to make a simple HTTP endpoint with some dummy logic using ASP.NET Web API. If we choose Web API project when we created the project we already have simple structure, otherwise its easy to add. We need a controller, not the traditional MVC controller but one that derives from the new ApiController, lets call it CharactersController. Web API then has some different conventions how to map a HTTP verb to an action. The simplest one is just naming the action as the matching verbs.

Before we go further, if you didn’t pick Web API project at start you need to configure routes (in global.asax).

    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }

Note that this is the default API route with the starting segment API.

Our controller then needs some actions. We’re going to make some simple dummy data. Then use the dynamic JSON wrapper classes that comes with Web API to return just the data that Batman requires of the backend, without any real persistence.

public class CharactersController : ApiController
    public JsonArray Get()
        return new JsonArray(GetCharacter());

    public JsonValue Get(int id)
        return GetCharacter();

    public JsonValue Post(JsonValue value)
        dynamic character = value.AsDynamic().character; = new System.Random().Next(1, 1000);
        return value.AsDynamic().character;

    public JsonValue Put(JsonValue value)
       return value.AsDynamic().character;

    public void Delete(int id)


    private JsonValue GetCharacter()
        dynamic character = new JsonObject(); = "Joe";
        character.address = "Fith Avenue"; = 66;
        return character;

There are some really neat ways to work with JSON with Web API. Using the wrappers extension to go from wrapper to dynamic lets us work with the object in an easy fashion. Note that we also return and bind to these objects. To use model binding with JsonValue is something that we could do with a custom model binder in MVC using the JsonValue package.

This is enough to let Batman retrieve an initial collection of characters via the first action, returning an array with one character. We also fake persistence when we add a character via the UI in the post action. We need to provide the id that Batman uses to determine if the object is created.  


We swapped provider and configured the primaryKey to get this to work. Batman offers more configuration on these parts.

In your client controller the sample uses Character.Load. In the localstorage scenario no characters was initially found so three demo characters where created. In our RestStorage scenario our first get action returns an array of characters via the Character.Load method.

Now we could check out one more Batman feature. The loader. We could break up the MVC part in its own files and configure the app to load these.

class BatMap extends Batman.App
    @global yes
    @controller 'app'
    @model 'character'
    @view 'googleMapsView'

The default convention is “/controllers/” , “/models/” and “/views/”.


With the demo setup in place we can continue to play with Batman in an HTTP API scenario. The choice of client MVC framework has not become easier now, but we can see how Batman is inspired by the other players and adds to the story. The full package with routes and loader definitely has its pros and cons depending on scenario. If you didn’t read the shopify blog post on how Batman differs from other frameworks, it may seem like knock off with the added routes and loader. It will be interesting to see if it gains popularity. So be on the look out for the bat-signal!

This post was not focused on the web API, but we used a much cleaner way to match the HTTP actions than with MVC, and we gained some neat JSON usage. Web API will be your best HTTP buddy in the 4.5 story.


This Post Has 2 Comments

  1. gt3

    Nice intro! Thanks for exposing Batman.js to .Net devs.

Leave a Reply