Rails::API vs. Sinatra vs. Grape: which Ruby microframework is right for you?
Rails dominates Ruby web frameworks: the next most popular framework, Sinatra, has 5% of the popularity of Rails. However, that doesn't mean non-Rails frameworks like Sinatra and Grape don't have their place.
When does it make sense to step away from
ActionController and use another framework? What are the sweet spots and gray areas for these frameworks?
I'll explore these questions below.
Introducing Sinatra, Grape, and Rails::API
Sinatra, released in the fall of 2007, was the first of the non-Rails web frameworks to gain traction. Its focus on quickly creating web apps with minimal effort carved out a niche versus the Rails monolithic approach. A small Sinatra app can easily be contained in a single file, which is a sharp contrast to a Rails app.
With its first release in late fall of 2010, Grape was the next web framework to gain significant traction. Grape is an "opinionated framework for creating REST-like APIs in Ruby". In the same way that Rails conventions help you quickly build an MVC app, Grape's conventions help you build a robust REST-like API in Ruby.
Rails::API was released in the spring of 2012. Rails::API loads a subset of the Rails stack for apps that only generate JSON content, skipping out on middlewares that don't apply to API apps (examples: cookie, flash, and session management). Rails::API started as a stand-alone gem, but with the release of Rails 5 in the summer of 2016, its now included by default in Rails.
There are other alternative web frameworks in Ruby (see "Rails alternatives in 2016"), but their usage is significantly lower than Sinatra. For simplicity, we won't discuss those in this article.
Sinatra, Grape, and Rails::API have sweet spots and overlapping usage scenarios. Let's go over some questions that can identify which framework might be the best fit for your scenario.
Are you complementing an existing Rails app or starting fresh?
If you are building a simple web app, you may think you don't need the special sauce that Rails provides. However, just like peeling away the the layers of an onion, you start to realize that even simple apps require decisions that Rails has already made for you.
For example, if you choose Sinatra or Grape for an initial version of an app, you'll need to think about:
- Logging - Rails logs all requests by default
- Environments - Loading environment-specific versions of your app (development, staging, production, etc)
- Reloading - Rails automatically reloads changes in development
In short, when you don't go Rails, you'll need to make some decisions and have some opinions. If you are building an app as part of a team, this can add a layer of complexity and differing opinions on direction that don't exist with Rails' configuration-over-convention approach.
For these reasons, I typically suggest starting with a Rails app and breaking out services when there's a clear need and scope.
Could your app grow beyond serving JSON?
Sinatra, Grape, and Rails::API can all easily service JSON content. However, if it's possible your app could grow beyond simply returning JSON responses (ex: render HTML content, have interactive forms, users sessions, etc) you can rule out Grape. Grape adds significant special sauce to delivering JSON APIs...and that's its sole focus.
What if you make the wrong framework choice?
Thanks to Rack's support for mounting apps, you aren't stuck with a rewrite if the scope of your app changes and your chosen microframework begins to get outside its sweet spot. You can easily mount Grape and Sinatra apps within a Rails app, or even mount a Grape app alongside a Sinatra app.
Is this for a public-facing or highly used JSON API?
While Sinatra and Rails::API easily serve JSON content, there's a lot more than JSON when building public-facing or highly used API. Just like Rails adds conventions for Rails apps, Grape adds conventions for JSON API apps:
Grape emerges as a clear choice when you outgrow a roll-your-own approach to an API. The conventions it covers make it easier for a team of developers to have a clear understanding of best practices that don't exist in custom API apps.
These benchmarks from November 2016 generated by Eugene Melnikov include comparisons of Sinatra, Grape, and Rails::API. Benchmarks can vary widely from real-world use cases, but Sinatra and Grape clearly outperform Rails::API:
I'd view these with some caution: Rails::API still contains a significant amount of middlewares that may be required in a production Sinatra or Grape app. These middlewares add overhead.
Which framework should I use?
There are areas where the decision between frameworks isn't clear cut. Some of these areas:
- Performance - while it appears Sinatra and Grape outperform Rails::API, it's possible removing unused middlewares from a Rails::API app may deliver similar performance as a Sinatra or Grape app.
- Shared logic - if a new service shares some logic with an existing Rails app (database access, ActiveRecord models, etc), you are frequently left with making the best of not-perfect decisions on how to handle that.
There are some sweet spots for each of these frameworks:
- Rails::API - you have an existing Rails app and are looking for a simpler, moderately faster approach to delivering a JSON API than
- Sinatra - an app for a clearly scoped service that shares little logic with another app and may need to do more than just deliver a JSON API.
- Grape - you've outgrown a homegrown API app and need a more advanced framework dedicated to delivering JSON APIs.
- Which Ruby background job framework is right for you?
- State of the 2017 Rails Stack
- Unicorn vs. Puma vs. Passenger: which app server is right for you?
Subscribe for more
Want more Ruby insights like this delivered monthly to your inbox? Just put your email into the sidebar form.