Using Ember.js With a Rails 4 or Sinatra Backend / Api

I’ve been using Ember.js for nearly a year now. Things have changed a lot for the better. One of the hardest bits about learning Ember is realizing most guides are out-dated (the framework evolves quickly) and may no longer recommended best practice. An example would be any guides which use views (hint: components instead) – and there are probaby some of this very blog with views.

One learning curve was making Ember Data work with my Rails 4 api. If you are a Sinatra / ActiveModel lover, you can probably follow the same steps.

A high level of overview:

1) Set Ember adapter to DS.ActiveModelAdapter;
2) Install ActiveModel::Serializers & setup routes;
3) Setup Serializers; and
4) Handle controller actions.


1: Set Ember adapter to DS.ActiveModelAdapter

The first and only Ember step is changing your adapters to DS.ActiveModelAdapter, vs DS.RestAdapter or whichever you are using.

From the Ember docs:

The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate with a JSON API that uses an underscored naming convention instead of camelCasing.

tldr; Use ActiveModelAdapter for Rails backends, and you won’t have to do much work at all!

Adapter Tips:

  • Namespacing your API (i.e. v1) will save you headaches later;
  • If Ember & Rails model names differ, consider the pathForType method.

2: Install ActiveModel::Serializers & setup routes

From the docs:

ActiveModel::Serializers brings convention over configuration to your JSON generation. AMS does this through two components: serializers and adapters. Serializers describe which attributes and relationships should be serialized. Adapters describe how attributes and relationships should be serialized.

In short, we will use ActiveModel::Serializers to avoid manual hash mangling. That’s never a good idea.

Add the following to your Gemfile and run bundle install:

1
gem "active_model_serializers"

Setup Routes:

Ember expects your API to follow restful conventions. You are a good developer, and see little need to deviate from this pattern.

To config/routes.rb, add:

1
resources :model_name

3: Setup Serializers

A mostly correct ActiveModel::Serializer tldr is they define how a model will be reflected as JSON.

All of your serializers should be placed in /app/serializers/model_name.rb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app/serializers/my_model_serializer.rb

class MyModelSerializer < ActiveModel::Serializer
  embed :ids, include: true

  attributes :id,
             :user_id,
             :name

  def name
    return object.title
  end

end

Serializer Tips:

  • Within a serializer, access your model as “object” i.e. “object.name”;
  • Attributes define keys which will be reflected in JSON; and
  • You can define methods as attriutes to return custom or sanitized values.

4: Handle Controller Actions

This is where the magic happens. An example controller (intentionally long hand for learners):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class ModelController < ApplicationController

  def index
    models = current_user.models

    render json: models, each_serializer: ModelSerializer
  end

  def create
    model = current_user.models.create(model_params)

    if model.valid?
      render json: model, serialzier: ModelSerializer
    else
      render status: 500, nothing: true
    end
  end

  def update
    model = ClientToken.find(params[:id])

    if model.update_attributes(model_params)
      render json: model

      #You can specify add'l keys i.e.
      #render json: model,
      #       serializer: DifferentSerializer, 
      #       root: 'different_model'
    else
      render status: 500, nothing: true
    end
  end

  def destroy
    model = Model.find(params[:id])

    if model.destroy
      render status: 200, nothing: true
    else
      render status: 500, nothing: true
    end
  end

  private
    def model_params
      params.require(:model).permit(:params)
    end
end

Controller Tips:

  • When rendering json, set ‘root’ to change the root key name;
  • Rendering an object in a 200 response (i.e. after save) means any properties updated on the backend will also be reflected in Ember.

That is all

With not a lot of work, you have a pretty speedy implementation which persists your data, is easy to test, and lets you focus on more important things.

Ember is fun.