Skip to content

JSON Serialization

David Copeland edited this page Nov 8, 2018 · 1 revision

A stitches-based microservice is designed to be as Rails-like as possible, which means that your initial implementation of any API is to expose the underlying resource the way Rails defaults to encoding it in JSON, i.e. to_json.

This prevents your controller from having to build up a customized hash, or from having to remember to use a thid party library. JSON serialization can be slow, but it's unlikely to be a bottleneck as you build your service, and it's also not a foregone conclusion that making your endpoint faster requires fancier JSON serialization.

The Rails Doctrine says Convention over Configuration, and the convention is Rails (and thus stitches) is to use Rails' default JSON serialization.

You may want to customize it. Some developers have a strong distaste for exposing database primary keys, or you may have data that you need to keep private to give your API a smaller surface area. You may also wish to include a nested object.

All of these use-cases can be served by Rails default serialization mechanism, which boils down to the method serializable_hash.

Suppose we have a Person object that contains an id, name, email, encrypted_password, created_at, and updated_at. Suppose further that a person has an instance of EmailPreferences that contains an id, marketing_opt_in, created_at, and updated_at.

We want to expose a structure like so:

{
  "name": "Chris Jones",
  "email": "chris@jones.info",
  "signed_up_on": "2018-01-03",
  "email_preferences": {
    "marketing_opt_in": true,
    "updated_at": "2018-03-02 12:34:15 -0000"
  }
}

This:

  • omits several fields
  • uses a derived field signed_up_on
  • has a nested object
  • that itself omits some fields

You might decide you need Fancy Serialization™ or Hand-Crafted Hashes™ to do this. You don't! Rails has this.

class Person
  def signed_up_on
    self.created_at.to_date
  end

  def serializable_hash(options = {})
    super(options.merge({
      only: [ :name, :email ],
      methods: [ :signed_up_on, :email_preferences ],
    }))
  end
end

class EmailPreferences
  def serializable_hash(options = {})
    super(options.merge({
      only: [ :marketing_opt_in, :updated_at ]
    }))
  end
end

When you do this, anywhere you serialize a Person, you'll get the same structure, which is generally what you want. This is most handy if your service sends messages - the messages will have the same structure as your API.