# RABL: Ruby API Templates for JSON, XML and MessagePack

> RABL moves API response building out of ActiveRecord's to_json and into the view layer, using a small Ruby DSL. It fits Rails and Padrino projects that need node names, conditional children and multi-format output, and it costs a template engine to learn.

**nesquena/rabl** — General ruby templating with json, bson, xml, plist and msgpack support

- Repository: https://github.com/nesquena/rabl
- Website: http://blog.codepath.com/2011/06/27/building-a-platform-api-on-rails/
- Stars: 3,628 · Forks: 332
- Language: Ruby
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/nesquena-rabl

## The to_json ceiling RABL was written to break

ActiveRecord's to_json gives you the columns, plus whatever you can bend into as_json. RABL starts from the complaint that this is restrictive when the JSON does not match the database schema. The README lists the specific things it wants to be easy: nodes named by combining data in an object, arguments passed to methods with the result stored as a child node, partial templates with inheritance to cut duplication, attribute aliases, attributes appended from a child into a parent, and nodes included only when a condition holds. Those six items are the product definition. If your payload needs none of them, RABL is overhead.

The intended audience is a Rails or Padrino application that serves an API and wants the representation to live in the view layer. The README states this as an MVC argument: API data representations belong to the view. That is a design position, not a neutral one. It means your response shape is described in a .rabl file next to your other templates, and it means anyone reading the controller sees a render call rather than a serialized hash. Teams that prefer serializers living beside models will find this backwards.

RABL is ORM-agnostic. The data typically comes from models, but the template only needs a Ruby object. That is what makes the same template able to emit JSON, XML, MessagePack, PList and BSON.

## How a .rabl template turns objects into output

A template is a Ruby DSL evaluated against the objects you assign in the controller. The README's Padrino example assigns @posts and @user, then renders posts/index. The template declares a collection, lists attributes, nests a child block and adds a computed node:

```ruby
# app/views/posts/index.rabl
collection @posts
attributes :id, :title, :subject
child(:user) { attributes :full_name }
node(:read) { |post| post.read_by?(@user) }
```

The block form of node receives each object, so read is computed per post rather than stored. child(:user) opens a nested object and renders only full_name from it. The README shows the resulting JSON with a post root wrapping the fields, a user object nested inside, and read as a boolean.

The engine selection is configuration. Rabl.configure exposes json_engine, msgpack_engine, bson_engine and plist_engine, each a class with a dump class method. The README notes that if you use oj, Rabl sets the mode to :compat. Root inclusion is separate per format: include_json_root, include_msgpack_root, include_bson_root and include_plist_root all default to true in the commented defaults, while include_xml_root defaults to false. include_child_root controls whether nested objects carry their own root. That split is the part people get wrong first, because a JSON client and an XML client can receive differently shaped documents from the same template.

## Installing RABL and rendering a first collection

The gem installs from RubyGems. The README gives both forms:

```bash
gem install rabl
```

In a Bundler project, add the gem and a JSON parser. The README recommends either oj or yajl-ruby:

```ruby
# Gemfile
gem 'rabl'
# Also add either `oj` or `yajl-ruby` as the JSON parser
# If using `oj`, Rabl will set the mode to :compat
gem 'oj'
```

Run bundle install afterward. With Rails 2.3.8 and up, Rails 3.x or Padrino, the README states RABL works without configuration. Padrino has one ordering rule: the rabl gem must be listed after the padrino gem in the Gemfile, otherwise Rabl does not register as a template engine. That is a silent failure if you miss it, so check the order before debugging anything else.

For Sinatra or another tilt-based framework, registration is explicit:

```ruby
Rabl.register!
```

After registration, write a template and render it from the route. The README's Padrino route declares both formats and renders the template:

```ruby
get "/posts", :provides => [:json, :xml] do
  @user = current_user
  @posts = Post.order("id DESC")
  render "posts/index"
end
```

Visiting the .json path returns the array shown in the README, with each element wrapped in a post root. The examples directory in the repository contains base.json.rabl, demo.json.rabl and inherited.json.rabl, which are the smallest working templates to compare against once your own output looks wrong.

## Where RABL gets in the way

The breaking changes list is the honest limitation section. v0.9.0 changed the default node name for certain associations, especially around STI models, and the README tells you to verify for breakages and to be explicit with an alias such as @users => :users. A template engine that infers names will infer them differently across versions. If you rely on inference, an upgrade is a payload change.

Testing is the second trap. v0.6.14 requires render_views with RSpec to test templates, because otherwise the controller passes the render command through as it does with ERB. A test suite that appears to cover your API can therefore assert nothing about the template output. That is a real failure mode, and it is not obvious from the passing suite.

Configuration defaults cut both ways. raise_on_missing_attribute defaults to false, so a typo in an attribute name does not raise; it produces output you have to notice. replace_nil_values_with_empty_strings, exclude_nil_values and exclude_empty_values_in_collections are all off by default, which means null handling is your decision rather than the gem's. The README does not document rollback or migration steps for the v0.9.0 node name change beyond verifying and being explicit.

Performance is not something this material quantifies. Caching exists as configuration (cache_all_output, cache_sources, cache_engine, perform_caching), but the README does not state what any of them cost or save. Treat caching as a switch to test in your own environment, not as a documented win.

## RABL against ActiveModel::Serializers and jbuilder

The closest comparison in the Rails world is ActiveModel::Serializers, which puts the representation in a serializer class tied to a model and renders through the controller's render :json path. RABL puts it in a template file and renders through the view layer, which is the MVC argument the README makes. If your team already thinks in serializers, RABL adds a second mental model for the same job.

jbuilder is also a view-layer builder, so it shares RABL's placement but not its shape. jbuilder templates are Ruby blocks that build a JSON structure directly; RABL templates declare attributes, collections and children against objects, with format engines selected by configuration. That difference matters for multi-format output: RABL's engine settings let the same template target JSON, XML, MessagePack, PList and BSON, while a builder that emits one structure is tied to that structure. If you only ever serve JSON, the multi-format machinery is weight you carry for nothing.

The third option is doing nothing and shaping to_json with as_json. That is the baseline RABL was written against, and for a payload with no computed nodes, no conditional children and no aliases, it remains the smaller solution.

## Maintenance, licence and the cost of upgrading

The repository is not archived. The last push was on 2026-03-18, and the most recent release listed is v0.17.0 on 2024-11-26, preceded by v0.16.1 and v0.16.0 the day before. Release cadence is slow rather than dormant, and the CHANGELOG.md at the repository root is where version-to-version changes are recorded. Read it before bumping the gem, because the README's breaking changes section shows that this project has changed output shape in minor releases before.

The upgrade cost is concentrated in two places: default node names for associations, and the JSON parser. v0.8.0 removed the multi_json dependency and relies on Oj or JSON, so any references to MultiJson in your application need removing. v0.6.14 changed what RSpec requires. Each of these is a one-time edit, but each one changes behaviour rather than just dependencies.

The licence is MIT, with MIT-LICENSE at the repository root. That permits use, modification and redistribution with the licence and copyright notice retained. This is not legal advice; if you redistribute RABL inside a product, read the file itself and get your own counsel.

## Conclusion

Adopt RABL when your API responses need node names, conditional children or several output formats and you are already rendering through Rails or Padrino views. Do not adopt it for a single simple to_json payload, or if you are not prepared to test templates through render_views, because RSpec otherwise passes the render command straight through. Before committing, verify the default node name for any STI association you serialize, since v0.9.0 changed it, and confirm that your Gemfile lists rabl after padrino if you use Padrino.

## FAQ

### How do I install RABL in a Rails or Padrino app?

Install the gem with gem install rabl, or add gem 'rabl' to your Gemfile along with either oj or yajl-ruby as the JSON parser, then run bundle install. Rails 2.3.8 and up, Rails 3.x and Padrino work without configuration, but with Padrino the rabl gem must be listed after the padrino gem or it will not register as a template engine.

### Why does my RSpec suite pass while the RABL template output is wrong?

Since v0.6.14, RABL templates require render_views with RSpec to be tested. Without it the controller passes the render command through as it does with ERB templates, so the test never exercises the template.

### Which output formats does RABL support?

The README lists JSON, XML, MessagePack, PList and BSON. Each format has its own engine setting (json_engine, msgpack_engine, bson_engine, plist_engine) and its own root inclusion flag, and include_xml_root defaults to false while the JSON, MessagePack, BSON and PList root flags default to true.

### Does RABL work with Sinatra?

Yes, through tilt. The README says to call Rabl.register! to initialize it, and points to the Sinatra Usage guide in the project wiki for setup details.

## Sources

- [License: MIT](https://github.com/nesquena/rabl/blob/master/LICENSE)
- [nesquena/rabl on GitHub](https://github.com/nesquena/rabl)
- [Project website](http://blog.codepath.com/2011/06/27/building-a-platform-api-on-rails/)
- [README](https://github.com/nesquena/rabl/blob/master/README.md)
- [Releases](https://github.com/nesquena/rabl/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nesquena-rabl
