Two weeks ago I had the opportunity to give a presentation on Single-Page Apps as part of our biweekly Tech Talk series at Airbnb HQ in San Francisco.  Here’s links to the slides and a video of the full presentation and Q&A session:

Video on YouTube  |  Slides on SlideShare

Thanks to all who attended!  We had a packed house of about 150 engineers who got warmed up on fancy tacos and beer, courtesy of Airbnb’s Chef Sam.

Building Wish Lists using Backbone.js

The talk included a number of specific tips and tricks on Backbone.js, Handlebars, and Rails that the team employed to build Wish Lists, which we launched in June.  I mention how we bootstrap the app from Rails to Backbone, our data lazy-loading strategy, how we keep our Backbone views modular and DRY, how we handle CDN and asset fingerprinting when generating HTML client-side, and our approach to I18n in the client, among other things.

Polyglot.js

One of these examples that got the biggest reaction was was Airbnb’s homegrown JavaScript I18n library, which we just open sourced under the name Polyglot.js.  Polyglot is a tiny utility that makes it easy to use translated phrases in both the browser and in Node.  Check it out and let us know what you think!

The Future of Single-Page Apps

In the talk I also introduced what it means to build a single-page app and outlined two different approaches: the Easy Way and the Hard Way.

The Easy Way

Client-js-app

The Easy Way is my name for single-page apps that run almost entirely in the browser.  Routing, view rendering, model layer, persistence, I18n, currency formatting, etc. all run in the client.  The app is loosely coupled to the server, which mostly serves static files, and to some RESTful API, which may or may not be running on the same server.  This approach is gaining popularity with the help of libraries and frameworks like Backbone.js and Ember.js.  You can find tons of resources and tutorials online about how to architect these client-side apps.

There are two main drawbacks to the Easy Way: performance and SEO.  The performance hit comes from the fact that the browser needs to download and evaluate all external and in-page scripts before the app can render any meaningful HTML.  Twitter wrote a great blog post about this a few months ago.  They replaced client-side rendering with server-side rendering and saw the “time to first tweet” drop by a factor of five.  That’s huge.

SEO is a huge consideration for a content-heavy app like Airbnb.  We want to build a rich, interactive searching and browsing experience but we absolutely need to allow search engines to crawl us.  It’s hard to do both; we’ve tried a few approaches for sharing view rendering between JavaScript in the browser and Rails on the server and the results have been awkward at best.

The Hard Way

Shared-js-app

In contrast, the Hard Way is what I call a webapp that can run seamlessly both on the cilent and the server.  Application logic and view rendering can run on the server to serve up HTML for performance and SEO, but can also run in the client to provide a rich, single-page experience.

Enter Node.js: finally a fast, low-barrier-to-entry JavaScript runtime for the server.

Building better Single-Page Apps using Node.js

We started our dive into Node.js with a survey of the open source application frameworks available now.  The first we found were Geddy and Tower.  These are heavily inspired by Rails, and so have a fairly traditional MVC architecture.  A URL maps to a controller and an action, and the server returns a page of HTML for each request.  These don’t solve for the Hard Way, and in my opinion don’t take advantage of Node’s strengths.

Next was SocketStream.  SocketStream is neat; it’s realtime at its core, quite modular, and it provides some neat adapters for using WebSockets to persist Backbone or Ember models to the server.  However, it’s made to support fully client-side apps: aka, the Easy Way. 

Next up was Meteor.  Meteor is the most-hyped Node application framework of the bunch, and it does seem like a revolutionary way to write apps.  Like SocketStream, it’s realtime, but it provides much more structure to your application, and you have to buy into the Meteor Way.  It even provides its own plugin system, eschewing NPM in favor of a Meteor-specific approach.  That scares me off a bit; why reinvent the wheel?  Finally, it doesn’t solve for the Hard Way.  This was a mistake in my talk — I said that rendering on the server and client was a core principle of Meteor, but it turns out it just has a hacky plugin that uses PhantomJS to boot up a headless WebKit instance on the server, and is labled as “Don’t Use” in the documentation.

Finally, Derby.  Derby is the only Node project I’ve seen that actually solves for the Hard Way as a design goal.  It has a novel approach to sharing templates and application logic between client and server, relying heavily on its realtime, MongoDB-based model-synchronization engine Racer.  However, like Meteor, it’s not very modular, and it’s very alpha.  I was very stoked to give Derby a spin, as I mentioned in the talk, and afterwards I sat down with Nate Smith, core Derby contributor to see how we might be able to leverage it.  As as I was afraid, it’s not quite ready for prime time, especially if like us, your data comes from a set of RESTful APIs that are decoupled from the Node app.

Backbone on the Server?

So, we’ve begun to prototype our own Node application framework.  It’s too early to give away any details, but we’re starting by porting over Backbone-style Models, Collections, and Views to Node, and seeing what issues arise.  We’re taking some inspiration from Henrik Joretag’s Capsule, and Mikito Takada’s view.json. More exciting news on that to come!  Let me know in the comments and on Twitter at @spikebrehm if this is an approach that interests you.

Past and Future Tech Talks

Check out our Tech Talk page to see videos of our past talks and schedules of upcoming talks.  If you’re a San Francisco nerd, come check ‘em out!

 

Want to work with us? We're hiring!