A few days ago, following long discussions, Rails API was merged into Rails master branch.
Because of that, in our latest post we discussed how to build an API only application. We’ve also shown how to integrate it with a client-side application implemented using Backbone. If you’ve missed this post, check it out and learn more about how to integrate Rails API with an Backbone application.
In this post, we are going to show how a very similar client-side Ember application can be integrated with the same backend application implemented using Rails API. Like in our previous post, we are going to use the TodoMVC application.
In addition, we are going to comment about some issues that were fixed in Rails and Active Model Serializers in order to make the integration easier. These improvements were the result of testing Rails API with an Ember application, as part of our efforts to make it work properly with some of the most popular JavaScript frameworks.
Building the Backend
Generate the Rails API only application
First of all, we need to generate a new Rails API only application from scratch. Rails 5 is not released yet, so we have to clone the Github repository and generate our application directly from the source code.
git clone git://github.com/rails/rails.git cd rails bundle bundle exec railties/exe/rails new /tmp/my_api_app --api --edge
In case you want a detailed explanation about the list of directories and files generated by this command, please take a look at our previous post.
However, if we compare with what was described in our previous blog post, we can see a subtle difference in the generated Gemfile: the active-model-serializer
gem version changed from 0.10.0.rc1 a 0.10.0.rc2.
The new Active Model Serializer release candidate includes a new adapter that works properly with the Ember’s RESTAdapter. This addition simplified a lot the integration of our Rails API only backend and the Ember client-side application.
Scaffolding the Todo resource
The Todo items in the Ember application have two attributes: a string title
and a boolean isCompleted
. The following step to build our backend application is precisely to add a resource representing these Todo items.
We can do it just running the rails generate scaffold
command:
bin/rails g scaffold todo title isCompleted:boolean
Since we’re now using a new version of the active-model-serializer
gem, specifically the version 0.10.0.rc2, the scaffold command generates the serializer file for this resource. At the time of writing our previous post, we had to run another command to generate the serializer. It is now changed to be automatically run along the scaffold generator. In fact, this was an enhancement implemented only a few days ago.
Don’t forget to run bin/rake db:migrate
to update the database
schema.
Choose the appropriate JSON serialization format
Our Rails API only application is going to respond incoming requests in a given JSON format. The process to convert the data into this format is called serialization and this will be possible in our backend application thanks to Active Model Serializer adapters.
By default, Active Model Serializer uses a format provided by the flatten_json
adapter which is a very simple format that only includes the list of attributes without any additional metadata about the data being serialized. For instance, using this adapter, a Todo item would be serialized like:
{ "id": 1, "title": "Todo 1", "isCompleted": false }
Luckily, we have some adapters shipped with Active Model Serializer in 0.10.0.rc2, giving us a lot of flexibility. In particular, we need to pick a JSON format matching our Ember application. We can achieve that by selecting a format that works well with the Ember’s RESTAdapter. The main requirement specified by the RESTAdapter is the presence of the root object’s key as part of the JSON payload, as it is explained in the Ember RESTAdapter documentation. It means we want to serialize a Todo item like this:
{ "todo": { "id": 1, "title": "Todo 1", "isCompleted": false } }
This is easy to do with Active Model Serializer if we choose the json
adapter instead of the flatten_json
. We can configure it by creating a new initializer file config/initializers/ams_json_adapter.rb
including the following line:
ActiveModel::Serializer.config.adapter = :json
At this point, the backend application should be ready, so it’s testing time! Start the web server with bin/rails s
and let’s create our first Todo using curl
:
curl -H "Content-Type:application/json; charset=utf-8" -d '{"todo": {"title":"Todo 1","isCompleted":false}}' http://localhost:3000/todos
The API application should return the created item serialized in JSON format, including the root element:
{"todo": {"id":1,"title":"Todo 1","isCompleted":false}}
Now, let’s get the Todo items list:
curl http://localhost:3000/todos
and the response should look like this (note the root element in plural):
{"todos": [{"id":1,"title":"Todo 1","isCompleted":false}]}
Integrating with the Ember client-side application
We want to have both components working together, integrating our Rails API only application with the Todo list frontend application implemented with Ember.
The original implementation from TodoMVC is our starting point, but we must do a few changes to have it working properly with our backend. In fact, the TodoMVC Ember example uses the browser local storage to persist Todo items, but we want to have our Rails API application doing this job.
After downloading the Ember application code from TodoMVC, we need to have a newer version of the ember-data
library to integrate properly this frontend application with Rails API. If you’re interested on reading more about this, we’ve opened a pull request on the TodoMVC repository.
So whilst the pull request is not merged or ember-data updated in other way, we need to update this library by hand using curl:
curl http://builds.emberjs.com/release/ember-data.js > node_modules/ember-data/ember-data.js
We have now all the vendored JavaScript code updated, so we are prepared to connect the frontend with our Rails backend. We configure that by changing the Ember adapter from LSAdapter (local storage) to RESTAdapter. You can learn more about Ember adapters in the Ember documentation page.
Let’s replace the following piece of code in the js/app.js
file:
Todos.ApplicationAdapter = DS.LSAdapter.extend({
namespace: 'todos-emberjs'
});
with the RESTAdapter’s definition pointing to our backend:
Todos.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://localhost:3000'
});
Finally, we have to configure CORS in the Rails API only backend because both applications will run in different domains (we will test the backend in localhost:3000
and the client-side application in localhost:9000
).
In brief, we need to uncomment the rack-cors
gem reference in the Gemfile
, run bundle install
and finally put the following code in the config/initializers/cors.rb
file:
# Avoid CORS issues when API is called from the frontend app
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests
# Read more: https://github.com/cyu/rack-cors
Rails.application.config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins 'localhost:9000'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
You can read more about how to setup CORS in our previous post.
We are now ready to test the whole application! Run the backend server with:
bin/rails s
and start a simple web server to test the Ember application:
ruby -run -e httpd . -p 9000
You can already try it out and start adding your first Todo items, just browse to localhost:9000.
Closing Thoughts
I hope this example illustrates another possible use of the new Rails API functionality that will be included in Rails 5.
We’re aware there’s still room to improve the experience. If you have the chance to try it out, we invite you to comment about your experience with Rails API.
We’d love to hear your feedback!
Resources
You can find the backend and frontend applications that we built in this article in Github:
The Ember application was borrowed from the TodoMVC project.
If you’re looking for a more modern & idiomatic implementation of the Ember TodoMVC check out this one. A piece of advice: the code in master doesn’t fully work when it’s integrated with our backend. There is an issue where Todo items are not completed in the Rails API only application when clicking on checkboxes. As far we tested, this open pull request fixes the problem.