Grape, Rack and assorted other things to build an API.

How We Got There

An API has been in discussion for a while now but recently the needs have grown stronger and the desire to create something to get more separation has been greater then ever. So after much discussion about microservices and how we would like to go about structuring our API servers it was decided to dive in with the user management and authentication first for some proof of concept. So the search for the right framework began.

Ruby on Rails

We could use Rails. We are already familiar with it (our main user interface is built on Rails) but it just felt too heavy. I don’t mean response times or speed (I will leave that to many other people on the Internet to work out), I mean heavy code wise. We don’t need ActiveRecord (we use Sequel in other places and some of us have taken a liking to it.) We don’t need an asset pipeline as we won’t be returning anything except data (mostly json). We don’t really need ActiveSupport. We just need something light allowing use to do some quick queries and return back some datasets.

Elixir

Elixir (specifically using the Phoenix Framework) was a compelling option. I have heard great things from other developers and the little bit of time I have spent playing around in it was good. It looks like a really great tool set and I would love to use it. But this is the real world where we get to think about things like ROI and long term maintenance costs. Developer hiring/training and a myriad of other reasons. This might not be the case in the future but right now where we stand it just didn’t feel right. It felt like we where using a new language because we wanted to learn a new language not because it was right for the business.

GO, Rust, and all the others we would love to try

See the last two sentences for Elixir above.

What’s left

Okay, so we want a lightweight ruby framework for serving apis. Well, there are a bunch of options and there is always just rolling your own. After searching high and low I kept ending up looking at Grape. It’s designed as a light-weight REST-like (their term not mine) API micro-framework for rack. It has some basic routing, some helpers, parameter validation, and it seems to be well supported (The last commit was just 6 days ago) with a growing set of plug ins and helpers. Great, let’s try that.

So Here We Are Now What Next

Well now that we had an idea of what framework it was time to get to work. After working on the code some and getting our normal setup, testing ci server, database, etc. in place I was left with one more major stumbling block. That was what’s the best way to format and serialize this data.

Formatting and Serializing

While looking through our format options I just kept getting a nagging feeling that just passing back objects or collections of objects was going to bite us later on when writing the consumer. I wanted more structure but this being a primarily json api what could I do. I mean it’s json and that’s pretty much follow the format, put your stuff in and if it passes a linter it’s good to go. Then during some head banging about this I started look at the format specs out there.

JSend

As I began looking at JSend first I liked it. But as I started using it some I found that it really wasn’t adding much over what I was already doing on my own. It doesn’t really help define collections vs object or relationships. It really was just a clean way of presenting success/failure and messages along with the failures. It ended up being a little too lightweight for me.

HAL

To be honest HAL just didn’t fit with me. I don’t know why. I can’t place my finger on the reason I just didn’t like it. I think it might of had to do with the embedded structure or maybe it just felt too much like XML. I tried it mostly while reading the spec and then moved on.

The Others

There are plenty more specs or design ideas out there. I read through a bunch of them but I had already started to look at JSON:API and honestly none of the others suited me as well. That left with me looking at JSON:API.

JSON:API

I may catch flak for this as I know that many people feel this spec wasn’t needed. We already have all the others why create a new one. Who is going to use it. Well I sent a couple samples out to some people and they all seemed to like JSON:API the best. So I started working with it. Initially I was hand rolling my own objects and not using anything to serialize. I was using Grape:Entities to get only the valid fields but not to serialize. As would be expected the writing of things like the code below into your code is no fun.

user_attributes = Entities::User.represent user
return {
  data: {
    type: "user",
    id: user.id,
    attributes: user_attributes.as_json,
    relationships: {
      account: {
        data: {
          id: user.account.id,
          type: "account"
        }
      }
    }
  },
  included: [
    get_include_object(user.account, Entities::Account.represent(user.account))
  ]
}

This led me down the path of finding a good serializer. Luckily I came across a wonderful gem jsonapi-serializers. This allowed me to have straight ruby code like the line below and end up with valid JSON:API spec results.

JSONAPI::Serializer.serialize(user, include: ['account'], skip_collection_check: true).to_json

Which I eventually ended up refactoring away into helpers like this.

def update_object(object_class, id, params, includes)

  object =  Object.const_get(object_class).find(id: id)
  if object
    begin
      object.update(params)
      JSONAPI::Serializer.serialize(object, include: includes, skip_collection_check: true).to_json
    rescue
      object_errors object
    end
  else
    send_error 403, object_class.downcase, "#{object_class} was not found"
  end
end

That re-factor (along with some other similar ones) allowed me to have really small endpoints that did nothing but validate params (Using grape’s param validation) and call the helper. For now that’s where the project stands and for basic crud it works. I like the direction it’s headed and the code still feels lightweight to me. I’m sure there will be more challenges and stumbling blocks down the road but so far it seems to be taking shape and moving forward and at the end of the day that is what counts. Till next time…