Skip to content

Latest commit

 

History

History
269 lines (200 loc) · 7.86 KB

adapters.md

File metadata and controls

269 lines (200 loc) · 7.86 KB

Back to Guides

Adapters

ActiveModelSerializers offers the ability to configure which adapter to use both globally and/or when serializing (usually when rendering).

The global adapter configuration is set on ActiveModelSerializers.config. It should be set only once, preferably at initialization.

For example:

ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi

or

ActiveModelSerializers.config.adapter = :json_api

or

ActiveModelSerializers.config.adapter = :json

The local adapter option is in the format adapter: adapter, where adapter is any of the same values as set globally.

The configured adapter can be set as a symbol, class, or class name, as described in Advanced adapter configuration.

The Attributes adapter does not include a root key. It is just the serialized attributes.

Use either the JSON or JSON API adapters if you want the response document to have a root key.

IMPORTANT: Adapter configuration has no effect on a serializer instance being used directly. That is, UserSerializer.new(user).as_json will always behave as if the adapter were the 'Attributes' adapter. See Outside Controller Usage for more details on recommended usage.

Built in Adapters

Attributes - Default

It's the default adapter, it generates a json response without a root key. Doesn't follow any specific convention.

Example output
{
  "title": "Title 1",
  "body": "Body 1",
  "publish_at": "2020-03-16T03:55:25.291Z",
  "author": {
    "first_name": "Bob",
    "last_name": "Jones"
  },
  "comments": [
    {
      "body": "cool"
    },
    {
      "body": "awesome"
    }
  ]
}

JSON

The json response is always rendered with a root key.

The root key can be overridden by:

Doesn't follow any specific convention.

Example output
{
  "post": {
    "title": "Title 1",
    "body": "Body 1",
    "publish_at": "2020-03-16T03:55:25.291Z",
    "author": {
      "first_name": "Bob",
      "last_name": "Jones"
    },
    "comments": [{
      "body": "cool"
    }, {
      "body": "awesome"
    }]
  }
}

JSON API

This adapter follows version 1.0 of the format specified in jsonapi.org/format.

Example output
{
  "data": {
    "id": "1337",
    "type": "posts",
    "attributes": {
      "title": "Title 1",
      "body": "Body 1",
      "publish-at": "2020-03-16T03:55:25.291Z"
    },
    "relationships": {
      "author": {
        "data": {
          "id": "1",
          "type": "authors"
        }
      },
      "comments": {
        "data": [{
          "id": "7",
          "type": "comments"
        }, {
          "id": "12",
          "type": "comments"
        }]
      }
    },
    "links": {
      "post-authors": "https://example.com/post_authors"
    },
    "meta": {
      "rating": 5,
      "favorite-count": 10
    }
  }
}

Include option

Which serializer associations are rendered can be specified using the include option. The option usage is consistent with the include option in the JSON API spec, and is available in all adapters.

Example of the usage:

  render json: @posts, include: ['author', 'comments', 'comments.author']
  # or
  render json: @posts, include: 'author,comments,comments.author'

The format of the include option can be either:

  • a String composed of a comma-separated list of relationship paths.
  • an Array of Symbols and Hashes.
  • a mix of both.

An empty string or an empty array will prevent rendering of any associations.

In addition, two types of wildcards may be used:

  • * includes one level of associations.
  • ** includes all recursively.

These can be combined with other paths.

  render json: @posts, include: '**' # or '*' for a single layer

The following would render posts and include:

  • the author
  • the author's comments, and
  • every resource referenced by the author's comments (recursively).

It could be combined, like above, with other paths in any combination desired.

  render json: @posts, include: 'author.comments.**'

Note: Wildcards are ActiveModelSerializers-specific, they are not part of the JSON API spec.

The default include for the JSON API adapter is no associations. The default for the JSON and Attributes adapters is all associations.

For the JSON API adapter associated resources will be gathered in the "included" member. For the JSON and Attributes adapters associated resources will be rendered among the other attributes.

Only for the JSON API adapter you can specify, which attributes of associated resources will be rendered. This feature is called sparse fieldset:

  render json: @posts, include: 'comments', fields: { comments: ['content', 'created_at'] }
Security Considerations

Since the included options may come from the query params (i.e. user-controller):

  render json: @posts, include: params[:include]

The user could pass in include=**.

We recommend filtering any user-supplied includes appropriately.

Advanced adapter configuration

Registering an adapter

The default adapter can be configured, as above, to use any class given to it.

An adapter may also be specified, e.g. when rendering, as a class or as a symbol. If a symbol, then the adapter must be, e.g. :great_example, ActiveModelSerializers::Adapter::GreatExample, or registered.

There are two ways to register an adapter:

  1. The simplest, is to subclass ActiveModelSerializers::Adapter::Base, e.g. the below will register the Example::UsefulAdapter as "example/useful_adapter".
module Example
  class UsefulAdapter < ActiveModelSerializers::Adapter::Base
  end
end

You'll notice that the name it registers is the underscored namespace and class.

Under the covers, when the ActiveModelSerializers::Adapter::Base is subclassed, it registers the subclass as register("example/useful_adapter", Example::UsefulAdapter)

  1. Any class can be registered as an adapter by calling register directly on the ActiveModelSerializers::Adapter class. e.g., the below registers MyAdapter as :special_adapter.
class MyAdapter; end
ActiveModelSerializers::Adapter.register(:special_adapter, MyAdapter)

Looking up an adapter

Method Return value
ActiveModelSerializers::Adapter.adapter_map A Hash of all known adapters { adapter_name => adapter_class }
ActiveModelSerializers::Adapter.adapters A (sorted) Array of all known adapter_names
ActiveModelSerializers::Adapter.lookup(name_or_klass) The adapter_class, else raises an ActiveModelSerializers::Adapter::UnknownAdapter error
ActiveModelSerializers::Adapter.adapter_class(adapter) Delegates to ActiveModelSerializers::Adapter.lookup(adapter)
ActiveModelSerializers::Adapter.configured_adapter A convenience method for ActiveModelSerializers::Adapter.lookup(config.adapter)

The registered adapter name is always a String, but may be looked up as a Symbol or String. Helpfully, the Symbol or String is underscored, so that get(:my_adapter) and get("MyAdapter") may both be used.

For more information, see the Adapter class on GitHub