Ember and Sockets


#1

Hello,

I’m starting a new project for my MSc with an ember-cli app + nodejs backend. I have some doubts about whats the best decision to implement my interaction between ember and backend. My two possible approaches are the follow:

1- Build a rest API on nodejs backend and consume that api on ember with ember-data. This is the most common way. However, i need that server have the possibilite to inform the REST adapter of new records created/updated/deleted on the backend and i don’t know how to edit rest adapter to do this.

2- Get a socket.io adapter for ember-data and use web sockets instead of a casual REST API.

What do you think that is the best choice guys?

But i have an another doubt too. How can i include socket.io in emberJS in way that i can use the socket.io variable in any place of EmberApp? I don’t know how to do this too, and i think that i’m gonna need this, to only use one single socket connection for all the events.

Regards


#2

As an interim thing, you can have your models set up, and call the “reload” method on the models which forces the model to be updated from your API, based on a signal from the socket.

And you’re right, it can be tough to get “inside” Ember from “outside”. I’m sure someone will give you a good solution, but one thing I’ve tried is to have a method on an Ember object which wraps the call from the outside in an “Ember.run” block, so it’s seen by Ember.

Another possibility would be to use Firebase – https://www.firebase.com – and either the EmberFire adapter or Fireplace. You can have quite a bit of data in their free plan, and it pushes updates. For that matter, you could look at the source for EmberFire and Fireplace, and see how they’re doing it.

Let us know how it goes, and what you come up with… it’s not the standard use-case, but it’s quite useful in some special contexts.


#3

@mistic

For starters, don’t think about using WebSockets as a different approach to data. WebSockets allow you to consume data faster and more efficiently in real time, as well as to receive data (and not just request data), but really they are just a transport, and a transport is just an implementation detail.

###Start by building the API

Use express / express + sails or some other method and build a REST API that meets your needs, as well as an adapter / serializer that allows you to consume it with Ember-Data. Once you have this API, using this same API via websockets and receiving push events is trivial. Having a solid API is the most important detail.

###How do I include socket.io?

This part depends on context. Here are a few of the lesson’s I’ve learned.

####Mobile

WebSockets don’t work well with Mobile. Specifically, reconnections following someone closing their browser or Cordova app aren’t consistent. This is because phones shut down processes when the app is closed, so while your webpage might still be right there when the app resumes, your connection is not.

If you really need to support Mobile, build a socket-controller to wrap your socket.io connection to ferry requests / rebind event listeners to whatever websocket instance it has. This makes it very easy to simply swap out websocket instances without breaking functionality/listeners defined in another part of your app.

####Use an initializer

You only want one socket.io instance. Create an initializer for your app that registers it.

e.g.

import config from "../config/environment";

export default {

  name: "socket",

  initialize: function (container, application) {
    var socket = io(config.io.url, config.io.options);
    container.register('socket:main', socket, { instantiate: false, singleton: true });
    
    //expose it within controllers / routes / adapters as `this.get('socket')`
    application.inject('controller', 'socket', 'socket:main');
    application.inject('route', 'socket', 'socket:main');
    application.inject('adapter', 'socket', 'socket:main');
  }

};

Inject where needed

Ember now has services. This is a great candidate for use as a service, because you can inject it only where needed using the services API.

Mine do not use services, because Services weren’t available when I built the app. Instead of injecting into all routes / controllers / adapters as I do you may want to only inject it specifically when desired.

####Socket Listeners

Event though you may expose the socket throughout your app, only add listeners in a centralized place.

For instance, I have a pusher class

import Ember from "ember";

export default Ember.Object.extend({

    socketEvents : {},

    init : function () {
      this._super();

        var socket = this.get('socket'),
            events = this.get('socketEvents'),
            eventNames = Ember.keys(events),
            cb = function (method, data) {
              Ember.run.schedule(
                'backgroundPush',
                this,
                method,
                data
              );
            },
            name,
            i;

        for (i = 0; i < eventNames.length; i++) {
          name = eventNames[i];
          socket.on(name, cb.bind(this, events[name]));
        }

    }

});

Which I use to add events in an organized manner.

You would use it like this

import Pusher from "../utils/pusher";

export default Pusher.extend({
    socketEvents : {
       'an-event-name' : function () {
            /* handle the event */
       }
    }
});

And it would need to be initialized (I use an initializer to start all my pushers).

####Errors will close your socket

My pusher class is doing two special things you need to know about. The first, is that it wraps the method you define for the callback in a call to Ember.run.schedule. This means that ALL the processing for your event is done asynchronously. Otherwise, any error thrown in your callback (or very long running callbacks) will cause your socket connection to close.

####Scheduling will save you from processing data at the wrong time

The second thing to notice is that I’m not using setTimeout or Ember.run.later or Ember.run.next to make the method async, I’m using Ember.run.schedule. If you don’t know anything about Ember’s run loop, I suggest reading up on it, but basically by using schedule you can give Ember information about the relative importance of the function running, and ensure that anything more critical is run first.

For instance, getting a push form your server with a lot of data right while you are in the middle of rendering a new route will likely cause your app to lock up for a moment noticeably. Its better with “push” data to process it after anything that’s affecting the view at the moment.

You could use the afterRender queue for this processing, but personally I like to reserve afterRender for use by tasks involved with what’s on the screen at the moment and I define a new queue for handling stuff that isn’t (such as push data).

backgroundPush is a custom queue that I’ve defined. If you want to use the same concept, in app.js before you call Ember.Application.extend you will want to add the line

Ember.run._addQueue('backgroundPush', 'afterRender');

which tells Ember to add the backgroundPush queue and to run that queue after it runs the afterRender queue.

####Authentication

In my apps, the websocket handles everything, including authentication. With socket.io, this is tricky.

  • socket.io sends a GET request to establish the socket, so no payload can be sent
  • socket.io does not allow the use of cookies or custom headers, so auth information cannot be sent as cookies or as custom headers

In fact, the “recommended” way of sending an auth token with socket.io when creating a new connection is to send it in the url, like this

https://example.com:3030/?auth=my-auth-token

This is not secure, even on HTTPS. In my setup, I allow unauthenticated sockets to connect, and once the connection is established, I send the authorization handshake though the socket connection, which avoids exposing your auth regardless of what method you are using to authenticate.


#4

This is a fascinating set of suggestions, and has definitely shaved days of investigation from finding an idiomatic way to integrate websockets. I’ve got a couple of related further questions based on these examples.

Firstly, where would you locate the pusher implementations? They’re obviously not sitting in the utils folder, based on

import Pusher from “…/utils/pusher”;

Secondly, and this is probably an easy one for someone with more ember experience, how are you actually communicating from the example pusher handler here to the rest of the app? Some ideas for what might be involved are:

  • Use the service locator to access a specific controller, or maybe the store (can the pusher use this.get() somehow?)
  • Associate the pusher with a component, by attaching it in the init of the component (maybe load the pusher from the service locator?)

Right now the pushers seem to be ambient in the application, and I’m trying to understand what the vector is for letting them interact with things. For context I am personally expecting to want to either fire a component action, or to push new data into the store.

Any thoughts appreciated.


#5

DM me on the ember slack and I’ll walk you through some ideas.