read

Since I had some free time over the past couple of weeks I decided to give the new Rails 5 API-only features a try. This post is based on Jorge Bejar's guide but adapted for use with Ember and including some extras as how to fix Devise not working with Rails 5 and other issues.

Since Rails 5 isn't released officially yet we'll need to clone the GitHub repo and build our application from there:

git clone git://github.com/rails/rails.git

Now navigate to the rails folder and run the code below. Just running 'rails new' won't work since it'd load your local version and not the one you just cloned.

bundle exec railties/exe/rails new emberandrails/my_api_app --api --edge

Let's use the scaffolds to quickly generate a resource to use

bin/rails g scaffold team name:string city:string championships:integer<br>
rake db:migrate

If you go take a look at the generated controllers you'll see they are super clean, being an API-only app means no more respond_to, #edit or #new. We now have to create a serializer for our team object:

bin/rails g serializer team name city championships

Now we can start the server and create a new object. We could install active_admin at this point to easily create entries but it's not worth the effort since it doesn't work out of the box; we'll use the curl command instead.

rails s
curl -H "Content-Type:application/json; charset=utf-8" -d '{"name":"Lakers","city": "Los Angeles","championships": 16}' http://localhost:3000/teams

Now if we run

curl http://localhost:3000/teams

we should get an array with the element we just created in it. Perfect. Ember.js is gonna expect a json with a root object's key though, so we should create config/initializers/amsjsonadapter.rb with:

ActiveModel::Serializer.config.adapter = :json

Now it's a good time to setup Devise before we move on to the frontend.

If you try to simply use 'gem 'devise'' you'll get an error like this:

/Users/fana/.rvm/gems/ruby-2.2.2/bundler/gems/devise-213aa51126d5/lib/devise/rails/routes.rb:446:in `block in with_devise_exclusive_scope': undefined method `[]='

Which is caused by the way Rails 5 handles routes apparently. I've looked around and found two forks of Rails and Devise that fix the issue, so you can use these on your gemfile:

gem 'rails', github: "ballPointPenguin/rails"
gem 'devise', github: 'twalpole/devise', branch: 'rails5'

While we have the gemfile open, let's uncomment rack-cors as well since we'll need it later on and then run bundle install. You should now go to 'config/initializers/cors.rb' and set what domains are allowed to access your API; if you only want your Ember app to do that, set origins 'localhost:4200' and you'll be set. Don't forget to add your production host later on.

We can now create a user through Rails' console with

User.create!(email: "test@test.com", password: "testtest")

Now on to Ember.js. I usually have the API and the frontend separated in two different repos and deploy them on two different servers, this means that locally they'll run on two different addresses and it needs a bit of tweaking. The first thing to do is set the host depending on your environment; you can do that in config/environment.js by setting:

if (environment === 'production') {
    ENV.api_host = 'https://coffeecodephillyapi.herokuapp.com'
  }

We can now setup our application adapter under app/adapters/application.js

import DS from 'ember-data';
import config from  '../config/environment'; 

export default DS.RESTAdapter.extend({
    host: config.api_host,
});

At this point, we should setup ember-simple-auth-devise for authentication. Since we'll store our session on Ember, we should tell Rails not to do that by adding

# config/initializers/session_store.rb
Rails.application.config.session_store :disabled

Now we can setup ember-cli-simple-auth; let's install it first:

ember install ember-cli-simple-auth
ember install ember-cli-simple-auth-devise

And then set it up in config/environment.js after the ENV declaration. We're gonna need to whitelist our APIs domains in the simple-auth setting and set what authorizer we're using (Devise in our case); after that, we're gonna have to setup the serverTokenEndpoint. Since I didn't need to login in local I've added the production host and called it a day, but you can set it dinamically for each environment just like we did for the api_host value earlier.

ENV['simple-auth'] = {
    crossOriginWhitelist: ['http://localhost:3000/*', 'https://coffeecodephillyapi.herokuapp.com/*'],
    authorizer: 'simple-auth-authorizer:devise'
  },
ENV['simple-auth-devise'] = {
    serverTokenEndpoint: 'https://coffeecodephillyapi.herokuapp.com/users/sign_in'
  }

Let's setup authenticated routes now; we generate the application route first

ember g route application

And then we edit it accordingly under routes/application.js

import Ember from 'ember';
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';

export default Ember.Route.extend(ApplicationRouteMixin);

A login/logout link can be added with

{{#if session.isAuthenticated}}
<a {{ action 'invalidateSession' }}>Logout</a>
{{else}}
{{#link-to 'login'}}Login{{/link-to}}
{{/if}}
<br/>
{{outlet}}

After creating a login route:

ember g route login

A login controller with 'ember g controller login' and editing to look like this:

import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';

export default Ember.Controller.extend(LoginControllerMixin, {
  authenticator: 'simple-auth-authenticator:devise'
});

And a template under templates/login.hbs:

<form {{action 'authenticate' on='submit'}}>
<label for="identification">Login</label>
{{input value=identification placeholder='Enter Login'}}
<label for="password">Password</label>
{{input value=password placeholder='Enter Password' type='password'}}
<button type="submit">Login</button>
</form>

Now whenever you need an authenticated route, you just need to define it like this:

import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin);

And you'll be set! Now we can add our teams model to the frontend and move on from there. If you want to get serious about Ember.js development, the Toptal blog has an awesome article about the 8 most common mistakes new Ember devs do, definitely worth the time! it's written by Balint Erdi, author of 'Rock and Roll with Ember.js', take a look at it!

Blog Logo

Alessio Fanelli

I'm a full stack developer with a love for sports.


Published

Image

Alessio Fanelli

Stay Sharp.

Back to Overview