BrowserSync proxy in top of a Play Framework server

Posted by on Aug 05, 2014 Comments Improve this article

TL;DR ‘coz I’m a h4k3r

Full source code of the demo is on GitHub with a nice and simple README on how to bootstrap the project.

What are you talking about dude?

Play Framework is an HTTP server written in Scala. It is super cool and you should probably give it a try. Let’s say you are using it, you really enjoy how productive it is, only having to refresh your browser to see all your code modifications, but you want to go further, you want live-reloading! There are several tools doing that.

Introducing BrowserSync. It provides 2 killers features. One is live-reloading: it can monitor resources, and when it detects some modifications, it will reload the browser page to load them. Even better, if possible, it will hot deploy them, meaning they will be loaded without reloading the page. That’s the case for CSS files which are loaded and the page repainted to display the new design without any refresh. The second one is to keep synchronized several browsers across devices. And we are not talking about resources only but about all user actions. If you scroll on one browser, it will scroll on all connected browsers, same for clicking on a button, and so on…

Let me say it again with a concrete example. You are on your computer, one screen with your source code, another one with Chrome opened and a last one with Firefox. You also have an iPad tablet and an Android smartphone connected to your computer. All of those have your application main page opened. Each time your edit your code, they will all reload and display the new result (hot deploy in case of CSS). When you are ready to test interactions, you just go to, let’s say, your Chrome browser and start scrolling and clicking. All your screens will start moving and doing the actions everywhere. Talk about increasing productivity!!

What about assets that require compilation? Such as SCSS, LESS, Stylus, CoffeeScript, etc… No biggy bro (or sis), we have you covered. In this demo, we will use Gulp to monitor those resources, compile them, and pass them to BrowserSync for live-reloading (obviously not losing the hot deploy if possible). You can freely use whatever build tool you want, be it Grunt, Broccoli, …

Magic proxy

BrowserSync could be used as the web server, that’s the easiest way to use it, and I’m actually doing it when my Play server is only here to provide a REST API. Consider doing that when you don’t need Scala templates or when you cannot use them (for example if you want to embed all your standalone HTML files inside a Cordova / PhoneGap application).

But here, we want to use Play as the web server, meaning it will be responsible to serve all our assets: HTML, CSS and JavaScript. For that, we will need to use the proxy feature of BrowserSync. Long story short, it will start another web server, on its own port, and will display the exact content from the Play server but adding a bit of JavaScript magic in it so it can enable all its features. It is just like opening your Play application, just on a different port. By default, you would use the port 9000 for Play, in this demo, we will set the proxy on port 9001 (it would have been around 3000 by default, but I didn’t want anyone to freak out so I put it as close as possible to Play default one).

We also want to live-reload some of our resources. I kept it simple for this demo, but you are free to add as many as your want. We will use the files property of BrowserSync configuration and set and array of files to monitor (it supports wildcards). Here, we are monitoring all CSS files from public/stylesheets, JavaScript files from public/javascripts and HTML files from app/views. There are two important points to notice here. First, we are referencing files based on their path on the source code, not on their actual url. For example, public/stylesheets/main.css will be served by Play as assets/stylesheets/main.css, but BrowserSync only needs to know the real path, after that, you can map it to whatever url you want. Second, we are monitoring Scala templates, and it’s fine because when BrowserSync will detect a modification, it will reload the page, and Play will re-compile the template before serving it, so it will display the new version. It works just fine with both run and ~run , the latest being faster.

Here is a super small configuration for BrowserSync to enable such a proxy. You can read the online documentation to extend it.

var browserSync = require('browser-sync');

browserSync({
  // By default, Play is listening on port 9000
  proxy: 'localhost:9000',
  // We will set BrowserSync on the port 9001
  port: 9001,
  // Reload all assets
  // Important: you need to specify the path on your source code
  // not the path on the url
  files: ['public/stylesheets/*.css', 'public/javascripts/*.js', 'app/views/*.html'],
  open: false
});

What about compiled assets?

Right… right… there are assets that you cannot serve directly, you need to preprocess them. The easiest way to do that is using a build tool, just pick one among the best ones (Gulp, Grunt, Broccoli, Brunch, …) and enjoy. There is a really simple separation of concern here: the build tool only manage assets that are not served directly (LESS, CoffeeScript, …) and compile them into assets that are actually handled by BrowserSync (CSS, JavaScript, images, …) which then live-reload them.

It’s so easy that it’s nearly frustrating. In the demo, the main.css file is generated from main.less. I have two Gulp task to handle that: one for compilation and one to monitor any modification.

var gulp        = require('gulp');
var less        = require('gulp-less');

// Registering a 'less' task that just compile our LESS files to CSS
gulp.task('less', function() {
  return gulp.src('./resources/less/main.less')
    .pipe(less())
    .pipe(gulp.dest('./public/stylesheets'));
});

// Let's watch our LESS files and compile them at each modification
gulp.task('watch', function () {
  gulp.watch(['./resources/less/*.less'], ['less']);
});

Wait, where are sbt-web and webjars?

Yeaaaaah… so lately, Play has introduced all those stuff on managing assets using SBT, some plugins and webjars. It works just fine but for me, it’s just as strange as if I would install Play from NPM. It’s cool if people want to try to merge two super different worlds (front-end and back-end), be it Node.js or scala.js, but I’m totally not ready for that. As a full stack developer, I want to enjoy each world with its own ecosystem, not trying to bend one into the other.

That’s why in all my projects, front-end assets are handled by front-end tooling (mostly Gulp and NPM lately) and my back-end resources by back-end tools (SBT, Ivy, Maven, …). Not only do I get the best of each, but I do not have some limitations. For example, the other day, I came across a bug on a JavaScript library I was using. No biggy: fork the repo, correct the bug, do a pull request. And while waiting for the author to merge it (which can take some time), I am free to directly use my fork inside my package.json so it’s not blocking at all. It tooks me less than an hour. I would be curious on how to do that using webjars? Anyway, sorry for not planning any proof of concept using webjars if that’s what you are using.

And that’s it! As I said at the beginning of the article, the demo is on GitHub, feel free to clone and play with it. Enjoy and thanks for reading.

AngularJS HTML5 routing on PlayFramework

Posted by on Feb 17, 2013 Comments Improve this article

The problem

AngularJS provides a nice $location service which allows you to deal with routing and url inside your application. It supports both a fallback mode using hashbang and a real HTML5 routing using the new History API. Since we love bleeding edge technologies, that’s obvious we want to use the History API if possible. Lucky us, it appears it’s quite easy to do so with a Play Framework application. Let’s see how to do that.

The full code of the demo is available on GitHub.

Simple configuration

Let’s keep things simple. Creating a new Play Framework application with play new. We will use a Scala application in this article, but it should as easy with a Java one. Then add AngularJS using CDN (see main.scala.html#L19). Then we will create an Angular module for our app and enable the HTML5 routing mode which is disable by default.

var app = angular.module("app", ["ngResource"])
  .config(["$routeProvider", function($routeProvider) {
      return $routeProvider.when("/", {
        templateUrl: "/views/index",
        controller: "AppCtrl"
      }).otherwise({
        redirectTo: "/"
      });
    }
  ])
  .config(["$locationProvider", function($locationProvider) {
      return $locationProvider.html5Mode(true).hashPrefix("!");
    }
  ]);

Nice. Now we will make some modification to the default views generated by Play. We want a single-page application, which means our root url must map to a main page which will contains all resource dependencies and includes the ng-view of AngularJS. I’ve done that by editing the main.scala.html file as you can see here and add a new Play route:

# File routes
GET     /                           controllers.Application.main(any = "none")

And a new Action in my Application controller:

def main(any: String) = Action {
  Ok(views.html.main())
}

Wait. Why is there a String param for the main method? And why do we pass a default any = "none" value? It seems a bit useless… Fair enough, I will explain it later on the article, don’t mind it for now. So, we have our main page. Let’s add some content for the landing page. As we have specified in the AngularJS routing, our root url, which is defined with $routeProvider.when("/", ...) is pointing to /views/index. So we need a Play route for that… but before, let’s talk about all those routes.

Routes, routes, routes…

As we advance in our coding, we can see that the term “route” is used in different contexts. It’s important to fully understand them all. Play routes, which are defined in the conf/routes file, are the core of your application. It’s those routes which will answer to the HTTP requests, serving either HTML, JSON, or whatever. But you, as the developer, will be the only one to know about them. Users of your application will only use and see AngularJS routes, which are defined in your main module using the $routeProvider.

The idea is that when a user land on your site, it should use AngularJS routing which will then use Play routes to load the template (= the content) of the page. So why not using the exact same url for both the AngularJS route and the templateUrl route? Well, in the case of the root, it’s obvious: the Play “/” route is already required for our main page. Ok. But in a more general case, could we do:

$routeProvider.when("/page", {
  templateUrl: "/page",
  controller: "PageCtrl"
})

Nope, you can’t. Well, that’s a small lie… you could do that, and it would work if your user always open your main page first. If he does that, then all AngularJS routing is loaded, and when the user will go to “/page” url, it will be catch by AngularJS which will ask for the “/page” on Play routes which will give him the content of the page. So what’s the problem? If you user directly land on the “/page” url, because he arrived for the first time on your application after having clicked on a link somewhere which was pointing at “/page”, then the Play route will be directly called since there is no AngularJS routing initialized at all… And that’s bad, right? The user would have the raw content of the page without any JavaScript or CSS from the main page. It’s a fail.

In order to prevent that, I have chosen to use some conventions. There is nothing official, you can use your own, the only thing to keep in mind is that AngularJS routes and Play routes should never be equals. My solution to do that is to prefix all Play routes serving HTML with a “/views/” prefix, all Play routes serving JSON with a “/api/” prefix and, as it is by default, all Play routes serving resources with a “/assets/” prefix. Meaning I can now use all routes I want inside AngularJS routing but never starts them with one of those prefix.

Of course, it will just modify the problem without solving it. Now, if a user land directly on “/page”, he will have a huge orange Play error telling him that the route doesn’t exist. As before, AngularJS routing isn’t loaded, so it’s Play routing which try to handle that and, since there is no longer any “/page” route in Play, it crash.

So what’s the point of having different routes? The next trick is to redirect all unknow routes to our main page. By doing so, if the user land on “/page”, Play will say “I don’t know that route, go to the main page”, so the user will indeed go to your main page, but then, all AngularJS routing will be loaded and super awesome AngularJS will say “Hey, I do know that route, and I need the /views/page template”, and then Play will respond “Oh yeah, I can serve you /views/page, here is the HTML to display”, and your user will see the correct “/page1” content, even if he lands directly on it.

Another question? Why didn’t we use this trick before and then have the same routes for both AngularJS and Play? Because it’s important that the trick apply only to unknow routes. We cannot redirect to the main page an existing Play route because it needs to serve its own content. So by using different routes, we can intercept AngularJS routes inside Play as “unknown routes” and redirect them to the main page so AngularJS can handle them. I hope I’m clear enough here, that might be the hardest part of the article. In order to achieve that, we just need to put a Play route at the end of the route file which intercept all routes and redirect them to the main page:

# File routes
GET     /*any                       controllers.Application.main(any)

And now you should understand why we had a any: String for our main page at the beginning.

First pages

Enough with all the theory, let’s do two concrete pages: creating “page1.scala.html” and “page2.scala.html” files with basic content (see source code, I’m using a template to stay DRY about the title and the menu but that’s not important), add then as Play routes (with a “/views/” as stated before) and also add them inside our controller:

@()

@template() {
  <h2>Page1</h2>
}
# File routes
GET     /views/page1                controllers.Application.page1
def page1 = Action {
  Ok(views.html.page1())
}

And then add the AngularJS routing:

.when("/page1", {
  templateUrl: "/views/page1"
})

That’s it. We can now create a link, using a a tag, to point to the url “/page1” and AngularJS will do the rest, loading the “/views/page1” template using Play route which will return the HTML code generated from our Scala template. Nice isn’t it? The url is correct: http://localhost:9000/page1, you can use the back button to return to the index page, no ugly hashbang, but all in Ajax with a single-page application.

Handling url parameters

So, we have our url working by using the History API thanks to the HTML5 mode of the AngularJS $routeProvider. But that’s only for raw url. What if we want to have dynamic parameters inside them? Just like REST, having url as “/colors/1” and “/colors/2” if we deal with colors (yeah, why not?). Do you think that’s easy? Well, you could, after all, both AngularJS and Play know how to handle parameters in their url. We could do something like:

.when("/colors/:id", {
  templateUrl: "/views/color/:id",
  controller: "ColorCtrl"
})
# File routes
GET     /views/color/:id             controllers.Application.color(id: String)

Nope, we can’t. Why? Because the “templateUrl” cannot handle url parameters. It’s just a template. And of course, using templateUrl: "/views/color" will not pass any “id” parameter to the Play route. Damn…

That’s not a problem in fact. Just keep to the AngularJS way of doing things: it’s not Play who should handle the data any longuer, it’s AngularJS who rules the world now, so you don’t need any params in your Scala code, just trust AngularJS. Ok, but we need a way to load the correct color depending on the id in the url right? Sure thing. It’s the role of AngularJS controller to that. That’s why there is a “ColorCtrl” along with our template, and guess what, the AngularJS controller know about the url params. Here is the code of the controller:

app.controller("ColorCtrl", [
  "$scope",
  "$routeParams",
  function($scope, $routeParams) {
    // Thanks to scope inheritance,
    // we can access the "db" from the AppCtrl scope
    $scope.color = $scope.db[$routeParams.id];
    if (!$scope.color) {
      $scope.msg = "There is no color for id "+$routeParams.id;
    } else {
      $scope.msg = undefined;
    }
  }
])

And just for fun, I’ve defined the fake database in the “AppCtrl”:

app.controller("AppCtrl", ["$scope", function($scope) {
  $scope.db = {
    1: {
      name: "black",
      hex: "000000"
    },
    2: {
      name: "white",
      hex: "FFFFFF"
    }
  };
}]);

So as we can see, using the $routeParams service, we can retrieve the url params and then load the correct color in the $scope. At the end, it’s just a matter of displaying that color in our view using AngularJS data-binding:

@()

@template() {
  <div data-ng-show="msg">
    <h2></h2>
  </div>

  <div data-ng-hide="msg">
    <h2>Color : # </h2>
  </div>
}

And that’s it! you can now display some data depending of params inside your url (and show an error message in case the data doesn’t exist). Cool isn’t it?

Want more?

Oh God, you are still reading? You should already be able to do anything you need. But if you want, we can dive in a more complex example, using AngularJS $resource service and serve JSON from Play like a REST API would do. Sounds good for you? Ok, let’s do that. First, we will create a “Users” controller and it will have two methods: “all()” and “find(id: String)” which will return an Array of Json and a Json object representing our list of users and one particular user based on its id. I will not use a real database, but something like MongoDB would fit really good in there. Here is the code:

package controllers

import play.api._
import play.api.mvc._
import play.api.libs.json._

object Users extends Controller {
  val db = Json.arr(
    Json.obj( "id" -> "1", "name" -> "John" ),
    Json.obj( "id" -> "2", "name" -> "Suzanne" )
  )

  def all() = Action {
    Ok(db)
  }

  def find(id: String) = Action {
    Ok(db.value.filter(v => (v \ "id").as[JsString].value == id).headOption.getOrElse(new JsUndefined("")))
  }
}

If you don’t fully understand it, you can check the PlayFramework documentation about handling Json (ScalaJsonRequests). I am using the new Json API from Play 2.1 (play.api.libs.json.package). Next we need our “UserCtrl” which will use the `$resource</code> service to retrieve the data:

app.controller("UserCtrl", [
  "$scope",
  "$routeParams",
  "$resource",
  "apiUrl",
  function($scope, $routeParams, $resource, apiUrl) {
    var Users = $resource(apiUrl + "/users/:id", {id:"@id"});

    if($routeParams.id) {
      $scope.user = Users.get({id: $routeParams.id}, function() {
        if (!$scope.user.id) {
          $scope.msg = "There is no user for id "+$routeParams.id;
        } else {
          $scope.msg = undefined;
        }
      });
    } else {
      $scope.users = Users.query();
    }
  }
])

To learn more about AngularJS $resource definition, see: ngResource.$resource. What is that “apiUrl” by the way? It’s a constant I’ve defined in my AngularJS app:

var app = angular.module("app", ["ngResource"])
  .constant("apiUrl", "http://localhost:9000\:9000/api")

Pro tip Why is there the 9000 port twice? That’s because if we had written http://localhost:9000/api, AngularJS syntax would have analyzed that as a url with a dynamic parameter named “9000” because it’s placed right after a : character. So we need that strange syntax to tell AngularJS that this is not a parameter but a real value in our url.

Next, we are creating our resource by extending this apiUrl with our routing “/users/:id”. This time, “:id” is a real parameter. We can now use “get” and “query” methods on the resource, passing or not a value to the id, in order to retrieve our Json code and assign it to the $scope. We will need two Scala views: one for the list and one for the detail of course.

@()

@template() {
  <h2>Users</h2>

  <ul>
    <li data-ng-repeat="u in users">
      <a data-ng-href="/users/">User# : </a>
    </li>
  </ul>
}
@()

@template() {
  <div data-ng-show="msg">
    <h2></h2>
  </div>

  <div data-ng-hide="msg">
    <h2>User#  </h2>
  </div>
}

And that’s it. We now have a list of users and links to each user detail, and all that data is fetch from a REST API using Json.

Pro tip By the way, since our Play routes doesn’t clash with AngularJS routes, you can load them directly in your browser. It will works because the Play route handling the redirection to the main page is at the end of the route file, so any real Play route will be loaded before the redirection. If you have the demo running, check http://localhost:9000/api/users and http://localhost:9000/api/users/1 to see your REST API working nicely.

Conclusion

I hope this article will help you bootstrap your next awesome application using awesome tools like AngularJS and PlayFramework. Your next step (if that’s not already the case) would be to learn more about the new PlayFramework Json API so you can have typesafe Json (if I can say so). You would also probably need to plug a database on it. One choice could be a MongDB database since it can store raw Json, and interact with it using a nice driver like ReactiveMongo if you want to go all the way down to asynchronous application.

It’s up to you!

Subscribe