Building an API for Neighbor.ly

We're building Neighbor.ly's first API to be used primarily in the new Dashboard also currently being built.

The API is being built on top of Rails, but it's not just a common Rails application, we are doing it as a gem using Rails Engine. So, with it we have a separated repository that we can track issues only related to that part of the system. We also have a separated test suite that will run faster because it has less dependencies.

For free we get the advantage of mounting the gem where we want. For example, we are now developing it under https://neighbor.ly/api/, but when we put it in production, the ideal place would be https://api.neighbor.ly/. With the ability to mount it where we want, it's easy to change it later. Since Neighbor.ly's code is all open source, you may want to fork the main project and adapt it to create your crowdfunding platform, but you may not need the API. With it being a gem, you can just remove it and unmount and you are ready to go!

One of the biggest annoyances so far is knowing which modules to include in controllers.

For a better performance we chose to not inherit our Api::BaseController from ActionController::Base. Instead, we are inheriting it from ActionController::Metal but then we need to manually include some modules needed to perform common things that we are used to, such as render, respond_with and many others.

But it's not easy to just inherit from Metal, you are going to have problems with it. See one of them that I faced.

I had this problem with respond_with on a create action that should return an http status code as 201 and the created object as a json in the response body, but it kept returning 200 and an empty response body. So, how did I fix it? Well, my primary solution was to make the create action like this:

def create  
  tag = Tag.new(permited_params)

  if tag.save
    render json: tag, status: :created
  else
    respond_with tag
  end
end  

But that doesn't seems right, does it? Why should I be doing this if statement if the responde_with already does it?

So I changed my Api::BaseController to inherit from ActionController::Base and guess what? My specs passed!

But that wasn't the solution. I didn't want to inherit from ActionController::Base. What did I do then?

I went to Rails source code to learn what modules it includes in the ActionController::Base class. After looking at that, I changed my Api::BaseController to include all these modules and then ran my specs again. It still passed! So I started removing some modules and kept running the specs to see when it failed.

After removing the ActionView::Layouts it started to fail again. Okay then, so I needed to include ActionView::Layouts as well. But it didn't make sense. I wasn't using layouts. I went again to rails source code to see what we have in the ActionView::Layouts. Turns out that it includes another module called ActionView::Rendering that the respond_with uses. So in order to solve the problem we just added this module to our Api::BaseController.

So now my create action looks like just this:

def create  
  respond_with Tag.create(permited_params)
end  

Simple right?

We're still developing the API, so we may have other problems worth sharing here. Thanks for reading.