The Pipedream

of Sharing Code Between
Node and the Browser

We find cool stuff in your city
for you and your friends to do
to make your life less boring
(while keeping your wallet fat).

@keithnorm, Front End Engineer

"Would it be possible to write a Backbone app in a certain way that would allow it to transparently run the same code on Node.js, rendering the same HTML output as it would on the client?"

[demo] FullScreen

JS Jabber: 004 with Jeremy Ashkenas and Yehuda Katz

Yehuda

Jeremy

"I think the entire 'Node is awesome because did you know if you write in JavaScript you write the same code on the client and server and if you use Mustache you use the same template'
I think the entire thing is a pipedream
people are missing the context of what a client side and a server side app is
and thinking about it very superficially so the end result is you imagine that something is cool
by now shouldn't Node have done something cool?
shouldn't there be a demo of something really awesome happening?
the fact is that there's just a totally different context
for what you're doing on the server and the client"

"For the record, I'm still a believer in this pipedream
and I don't think Node is necessarily there yet in terms of library support
for doing everything that you need it to do on the server side
but I do think that there is, especially for JavaScript web applications,
value in being able to have the same code run on the client and the server,
even if we haven't really reached that point yet."

#1 Agree on The Design

  • Will use the Backbone Router, delegate to express on server (TJ's take on it)
  • Only support GETs on client, POSTs, PUTs, etc. fall through to server
  • Override Backbone.sync on server to use request
  • Views must implement a toHTML method
  • Transparent server side support
    (no if onServer or if onClient conditionals in any app code)

The Simplest Backbone App


class Router extends Backbone.Router
  routes:
    '/': 'root'
  root: ->
    view = new HomeView()
    view.render()

class HomeView extends Backbone.View
  el: '#main'
  template: 'Welcome to SpainJS!'
  render: ->
   @$el.html @template
      
$ ->
  router = new Router()

  $('a').live 'click', (e) ->
    e.preventDefault()
    Backbone.history.navigate $(e.target).attr('href'), true
  
  Backbone.history.start
    pushState: true
          
          

[demo]

FullScreen

Now let's think about taking this code onto the server


# client
class Router extends Backbone.Router
  routes: 
    '/': 'root'
        

# server
app.get '/', (req, res) ->
        

# client
root: ->
  view = new HomeView()
  view.render()
      

# server
app.get '/', (req, res) ->
  view = new HomeView()
  res.render view.render()
      

What's the "Right" API?

Looking to Rails for help


class HomeController
  def index
    render 'home/index'
  end
end
        

root: ->
  this.render new HomeView()
        

[demo]

FullScreen

Packaging The Code From Node

With Browserify


async = require 'async'
        

browserify/bin/cmd.js server.coffee -o app.js
        

require.define "/async/package.json", (...) ->
    module.exports = {"main":"./index"}

require.define "/async/index.js", (...) ->
  module.exports = require('./lib/async')

require.define "/async/lib/async.js", (...) ->
  # async lib code
        

Browserify is Sweet

  • Node/npm require paths
  • Nested requires
  • File watcher
  • CoffeeScript

Let's add Browserify to the example

FullScreen

And finally, let's add a Model


class Tweets extends Backbone.Collection
  url: 'http://search.twitter.com/search'

  parse: (response) ->
    response.results

      

[demo]

FullScreen

Some bonus cool stuff

FullScreen

The demo project on Github