“...I've been working since 2008 with Ruby / Ruby on Rails, love a bit of Elixir / Phoenix and learning Rust. I also poke through other people's code and make PRs for OpenSource Ruby projects that sometimes make it. Currently working for InPay...”

Rob Lacey (contact@robl.me)
Senior Software Engineer, Brighton, UK

JBuilder Arrays and non-collections

JBuilder may not be the most efficient way to present an API but for simple cases it works pretty well. Our JSON-API standardized API could return the current authenticated user like so.

show.json.jbuilder

json.data do
  json.id current_user.id
  json.type 'users'
  json.attributes do
    json.(current_user, :uuid, :email, :full_name)
  end
end

/api/v1/me

{
  "data": {
    "id": "1a2b3c4d000001",
    "type": "users",
    "attributes": {
      "uuid": "aaaaaaaabbbbbbbbccccccccc",
      "email": "rob.lacey@buttonmoon.co.uk",
      "full_name": "Mr Rob Lacey"
    }
 }

Ideally I want to now add an included block so that can included related objects such as Tenant. Something like

{
  "data": {
    "id": "1a2b3c4d000001",
    "type": "users",
    "attributes": {
      "uuid": "aaaaaaaabbbbbbbbccccccccc",
      "email": "rob.lacey@buttonmoon.co.uk",
      "full_name": "Mr Rob Lacey"
    },
    "included": [
      {
        "id": "1",
        "type": "tenants",
        "attributes": {
          "name": "superadmin"
        }
      }
    ]
  }
}
All of the JBuilder examples talk in terms of Arrays of objects being built from a collection. Most of the time they probably are.
json.array! @comments do |comment|
  next if comment.marked_as_spam_by?(current_user)

  json.body comment.body
  json.author do
    json.first_name comment.author.first_name
    json.last_name comment.author.last_name
  end
end
Or
json.array! @people, :id, :name
But not always. If we want to add an array that is made up or arbitrary builder blocks, you find yourself thinking in terms of doing.
json.included do
  json.merge! do
    json.id current_user.tenant_id
    json.type 'tenants'
    json.attributes do
      json.(current_user.tenant, :name)
    end
  end
end
json.included [
  json.data do
    json.id current_user.tenant_id
    json.type 'tenants'
    json.attributes do
      json.(current_user.tenant, :name)
    end
  end
end
]

Neither of which work, turns out the way to do it is using the undocumented child! method.

json.data do
  json.id current_user.id
  json.type 'users'
  json.attributes do
    json.(current_user, :uuid, :email, :full_name)
  end

  json.included do
    json.child! do
      json.id current_user.tenant_id
      json.type 'tenants'
      json.attributes do
        json.(current_user.tenant, :name)
      end
    end
  end
end
Think I’ll do a pull request for the README as this took me a while to work out. https://github.com/rails/jbuilder/pull/507