Ember and Rails, A Review - 10 Dec 2016
So I’ve been working on an app using Ember and Rails 5 API recently and I wanted to get out some ideas about working with these software tools. I’m also using JSONAPI as my intermediary. So my full stack is Ember v2.9.0, Rails 5 using the API featureset with the jsonapi-resources gem. Hopefully this post might help some people who want to do this but don’t know where to start, or don’t know some of the concepts involved.
The first thing I wanted to get right-ish was a login mechanism. There are quite a few great tutorials out there for this that I ended up having to combine to get what I needed, and most of them use
ember-simple-auth which is a nice library to handle session storage and authentication within Ember. I also chose to use JWT (JSON Web Tokens) as my authentication transport mechanism. JWT has nice libraries on both ends, Knock on the Rails end, and ember-simple-auth on the Ember side. It allowed for a more simple setup than otherwise writing my own token scheme although that is apparently pretty simple.
Unfortunately, JSONAPI and jsonapi-resources doesn’t have a convienient method for handling authentication. I’ve seen some examples where people were able to integrate the two, but it seemed kind of messy and required a deeper knowledge of Rails and JSONAPI than I was currently prepared to deal with. So instead I just created a route outside of the jsonapi-resources and went from there. Getting Knock setup with Rails was fairly easy. After that you should end up with controller methods to handle authentication on your API routes.
Ember on the otherhand was a different beast. I ended up having to write my own (copy and edit off the web)
authenticator from a tutorial. Some of the tutorials, just straight up don’t work correctly. I even tried a library called ember-simple-auth-token which had a built in
authenticator but I was never able to get it to work properly.
Another thing I had to deal with that is currently sort of gross was creating a service in Ember to pull the currently logged in user. I ended up creating a dedicated route in Rails that pulls this information, and the Ember service just calls it whenever it needs. It calls this route almost every time a route is loaded, but I’m not sure what a better way to do this would be.
Some things I still need to deal with are expiring tokens, and regenerating new tokens once they expire.
This is mostly on the Rails side, as doing this in Ember wouldn’t be very secure. One point to make is that authentication is asking ‘who is asking for stuff?’, and authorization is asking ‘what stuff is this person allowed to access?’. I figured that out pretty quickly once I got started. Surprisingly, jsonapi-resources didn’t have a built-in way to deal with authorization, so I ended up using jsonapi-authorization. This gem bridges the gap between jsonapi-resources and Pundit to deal with authorization of your resources. I haven’t figured it out exactly, but I have it setup and running. JSONAPI has lots of relationships and related resources, so managing who can see what and when is a challenge and I’m not sure Pundit is exactly setup for that task. For the time being I’ve just opened everything up so I can build the app without worrying about it.
A few things in Ember that are weird because of the convention over configuration philosophy is that names are super important in Ember. If you don’t name something correctly with the convention your code will not work. The system needs to use names to find things for you, and if you don’t use the correct naming conventions, it can’t do that. For instance in Ember if you want to use a service in your object, you use
Ember.inject.service('service-name'), but Ember can also look up the service automatically like this
let serviceName = Ember.inject.service();. Ember knows how to parse the camel case and look for the correct service object to pull in.
Getting familiar with the Ember API is sometimes difficult. This is also the case with Rails. The guided Ember documentation often doesn’t tell you the whole story about what an object or class is capable of. If your use case falls outside the norms of the framework it’s difficult to tell if you need to write your own code to handle the situation or that you might just need to dig a little deeper into the API docs to find an odd function or class that was built for your needs. I had this situation happen when I couldn’t find any built-in code for
<select> elements in Ember. Apparently you need to import a library that can handle these things for you.
Both frameworks have testing as a core tenet to their idea of how you should program, and thus both make getting your tests up and running fairly easy. Although it’s easy to setup and run tests, it often is another thing to learn. Both use small DSLs for tests that differ from normal classes and anyone who has written tests can tell you sometimes it’s complicated. Ember has systems in place to help you with integration testing, component testing, and unit testing, all of which are slightly different pieces to the testing story. When coding in Ember you have to think about synchronous code vs async code, and utilize special code to deal with it. Testing is no exception.
The main thing I dislike about this setup is that you end up having to manually distribute your model to every layer of your code. In Rails I have to define my model, then define what parts of my model my resources will use, then I have to define what my authorization policy is for every action on every resource. Then in Ember I have to encode my resource model again so that Ember will know how to setup an object for the data being pulled in. This becomes even more complex as when you are dealing with relationships between models and what various actions should be allowed by whom. I think this could all be included together somehow and Rails may not be the answer to how to do this. I’d love an opinionated JSONAPI server that has all of the pieces together in a more cohesive and ergonomic way. Obviously this isn’t the only way to setup this type of system, but it’s the way that I used, and so far it’s working OK.
As a mental model this setup is not difficult to think about. But with most things involving code the details bring with them complexity and edgecases. It’s a lot of work for one person to setup, build, and manage all of little issues that arise and be able to reason about what’s going on. Bringing two frameworks together that both require lots of knowledge can be challenging and often the only answer is to gain more experience and knowledge. With more practice and experience I’m confident things would seem easier to me.