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
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
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
So now my create action looks like just this:
def create respond_with Tag.create(permited_params) end
We're still developing the API, so we may have other problems worth sharing here. Thanks for reading.