Simple JSON:API compliant parameters translator.
JSON:API standard specifies not only responses (that can be handled nicely, using gems like fast_jsonapi from Netflix), but also the request structure.
As we couldn't find any gem that would make it easier in Rails to use these structures, we decided to create something that will work for us - a translator that transforms JSON:API compliant request parameter strucure into a Railsy structure.
Add this line to your application's Gemfile:
gem 'jsonapi_parameters'
And then execute:
$ bundle
Or install it yourself as:
$ gem install jsonapi_parameters
Usually your strong parameters in controller are invoked this way:
def create
model = Model.new(create_params)
if model.save
...
else
head 500
end
end
private
def create_params
params.require(:model).permit(:name)
end
With jsonapi_parameters, the difference is just the params:
def create_params
params.from_jsonapi.require(:model).permit(:name)
end
JsonApi::Parameters supports ActiveRecord relationship parameters, including nested attributes.
Relationship parameters are being read from two optional trees:
relationships
,included
If you provide any related resources in the relationships
table, this gem will also look for corresponding, included
resources and their attributes. Thanks to that this gem supports nested attributes, and will try to translate these included resources and pass them along.
Passing a resource that is a single entity in relationships tree will make JsonApi::Parameters assume that it is a belongs_to
relationship.
Example:
class Movie < ActiveRecord::Model
belongs_to :director
end
Request body:
{
data: {
type: 'movies',
attributes: {
title: 'The Terminator',
},
relationships: {
director: {
data: {
id: 682, type: 'directors'
}
}
}
}
}
Will translate to:
{
movie: {
title: 'The Terminator',
director_id: 682
}
}
Example:
class Movie < ActiveRecord::Model
belongs_to :director
accepts_nested_attributes_for :director
end
Request body:
{
data: {
type: 'movies',
attributes: {
title: 'The Terminator',
},
relationships: {
director: {
data: {
id: 682, type: 'directors'
}
}
}
},
included: [
{
type: 'directors',
id: 682,
attributes: {
name: 'Some guy'
}
}
]
}
Will translate to:
{
movie: {
title: 'The Terminator',
director_attributes: { id: 682, name: 'Some guy' }
}
}
Passing a resource that is a an array of entities in relationships tree will make JsonApi::Parameters assume that it is a has_many
relationship.
Example:
class Movie < ActiveRecord::Model
has_many :genres
end
Request body:
{
data: {
type: 'movies',
attributes: {
title: 'The Terminator',
},
relationships: {
genres: {
data: [{
id: 1, type: 'genres'
},
{
id: 2, type: 'genres'
}]
}
}
}
}
Will translate to:
{
movie: {
title: 'The Terminator',
genre_ids: [1, 2]
}
}
Example:
class Movie < ActiveRecord::Model
has_many :genres
accepts_nested_attributes_for :genres
end
Request body:
{
data: {
type: 'movies',
attributes: {
title: 'The Terminator',
},
relationships: {
genres: {
data: [{
id: 1, type: 'genres'
}]
}
}
},
included: [
{
type: 'genres',
id: 1,
attributes: {
name: 'Genre one'
}
}
]
}
Will translate to:
{
movie: {
title: 'The Terminator',
genres_attributes: [{ id: 1, name: 'Genre one' }]
}
}
If the input is in a different convention than :snake
, you should specify that.
You can do it in two ways:
- in an initializer, simply create
initializers/jsonapi_parameters.rb
with contents similar to:
# config/initializers/jsonapi_parameters.rb
JsonApi::Parameters.ensure_underscore_translation = true
- while calling
.from_jsonapi
, for instance:.from_jsonapi(:camel)
. The value does not really matter, as anything different than:snake
will result in deep keys transformation provided by ActiveSupport.
params = { # JSON:API compliant parameters here
# ...
}
class Translator
include JsonApi::Parameters
end
translator = Translator.new
translator.jsonapify(params)
If the input is in a different convention than :snake
, you should specify that.
You can do it in two ways:
- by a global setting:
JsonApi::Parameters.ensure_underscore_translation = true
- while calling
.jsonapify
, for instance:.jsonapify(params, naming_convention: :camel)
. The value does not really matter, as anything different than:snake
will result in deep keys transformation provided by ActiveSupport.
As stated in the JSON:API specification correct mime type for JSON:API input should be application/vnd.api+json
.
This gems intention is to make input consumption as easy as possible. Hence, it registers this mime type for you.
The gem is available as open source under the terms of the MIT License.