Jsonapi-resources: Serializing an array of resources

Created on 23 Aug 2017  路  6Comments  路  Source: cerebris/jsonapi-resources

The documentation shows a clear example on how to do it for one resource:

post = Post.find(1)
JSONAPI::ResourceSerializer.new(PostResource).serialize_to_hash(PostResource.new(post, nil))

But how does one serialize an array of resources?

For example, suppose this is my controller:

class ProductsController < ApplicationController
  def index
    @products = Product.where(visible: true)
    render json: JSONAPI::ResourceSerializer.new(ProductResource).serialize_to_hash(ProductResource.new(@products, nil))
  end
  def show
    product = Product.find(params[:id])
    render json: JSONAPI::ResourceSerializer.new(Admin::ProductResource).serialize_to_hash(ProductResource.new(product, nil))
  end
end

GET /products/2
The show method returns 1 resource in JSON API compliant format. Even better, I could remove the entire show method and it will still work because the inherited methods do something similar.

GET /products/
However, the index method returns a 500 error. Removing the index method entirely would partially solve the problem, I get an array back containing ALL products. But what if I need it to return only the active=true products? For that I need an array serializer no?

Suggestions?
Am I missing something?
Is the documentation incomplete?
Have I perhaps not understood how to work with JSON API? I've got the impression the idea is to not add any logic to your controllers at all and let the URLs do all the work?

Most helpful comment

Why is there so much complexity for rendering a resource? I'm trying to find a good gem for using JSON API in my rails project, and I'm not a huge fan of the magical controllers

class ProductController < JSONAPI::ResourceControllerMetal
  # magic!
end

Without looking in the source code, I can't seem to find any documentation on what's happening in the controllers or how to explicitly render using a resource. It seems overly complex to have to call the following to render multiple resources.

@products = Product.where(visible: true)
  @product_resources = @products.map { |product| ProductResource.new(product, nil) }
  render json: JSONAPI::ResourceSerializer.new(ProductResource).serialize_to_hash(@product_resources)

All 6 comments

It took me a little while to find this because it seems like the source code has changed dramatically from 0.9 to what's in master? I had to dig into the source for the installed gem, as the docs didn't cover it and master is somewhat different.

Instead of passing the array of @products to ProductResource.new, you need to pass an array of ProductResources to serialize_to_hash. So, the correct way appears to be:

def index
  @products = Product.where(visible: true)
  @product_resources = @products.map { |product| ProductResource.new(product, nil) }
  render json: JSONAPI::ResourceSerializer.new(ProductResource).serialize_to_hash(@product_resources)
end

Brilliant, that works!

Why is there so much complexity for rendering a resource? I'm trying to find a good gem for using JSON API in my rails project, and I'm not a huge fan of the magical controllers

class ProductController < JSONAPI::ResourceControllerMetal
  # magic!
end

Without looking in the source code, I can't seem to find any documentation on what's happening in the controllers or how to explicitly render using a resource. It seems overly complex to have to call the following to render multiple resources.

@products = Product.where(visible: true)
  @product_resources = @products.map { |product| ProductResource.new(product, nil) }
  render json: JSONAPI::ResourceSerializer.new(ProductResource).serialize_to_hash(@product_resources)

@KidA001

I agree, the JSON API Resources controllers contain too much unknown and undocumented "magic". I'm uncertain how to use them, so I've resorted to overwriting them entirely and leverage Resources as serialisers only. For example:

# app/controllers/admin/products_controller.rb

def index
  @products = Product.all
  render status: 200, json: json_resources(Admin::ProductResource, @products)
end

def show
  @product = Product.find(params[:id])
  render status: 200, json: json_resource(Admin::ProductResource, @product)
end

def create
  @product = Product.new(attributes)
  @product.product_group_id = relationships[:'product-group']['data']['id']
  @product.product_sub_group_id = relationships[:'product-sub-group']['data']['id']
  if @product.save!
    render status: 201, json: json_resource(Admin::ProductResource, @product)
  else
    render status: 422, json: json_errors(@product)
  end
end

```ruby

app/controllers/application_controller.rb

class ApplicationController < JSONAPI::ResourceController
# Serialise and return 1 record into JSON using JSON API resources
# Example: render status: 200, json: json_resource(Admin::ProductResource, @product, nil)
def json_resource(klass, record, context = nil)
JSONAPI::ResourceSerializer.new(klass).serialize_to_hash(klass.new(record, context))
end

# Serialise and return an array of records into JSON using JSON API resources
# Example: render status: 200, json: json_resources(Admin::ProductResource, @products, nil)
def json_resources(klass, records, context = nil)
resources = records.map { |record| klass.new(record, context) }
JSONAPI::ResourceSerializer.new(klass).serialize_to_hash(resources)
end
end

```ruby
# app/resources/admin/product_resource.rb

module Admin
  class ProductResource < JSONAPI::Resource
    attributes :name,
               :slug,
               :public,
               :pitch,
               :body

    relationship :product_group, to: :one
    relationship :product_sub_group, to: :one
  end
end

@btoconnor
Your method does work . but when you check the content-type it is application/json
instead of application/vnd.api+json

I'm using jsonapi-resources 0.10.2
It appears the method JSONAPI::ResourceSerializer#serialize_to_hash does not exist, but there are some variants such as JSONAPI::ResourceSerializer#serialize_resource_set_to_hash_single That method, however, requires a ResourceSet as a parameter. not an ActiveRecord. It was not clear to me how to come by the required ResourceSet object.
TY!

Was this page helpful?
0 / 5 - 0 ratings