Barnski
Barnski

Reputation: 11

ActiveModel::Serializer returns null for empty has_one relationship (instead of JSON)

Problem

Our API shall return JSON objects (but not JSON API). How can we change the ActiveModel::Serializer to create a JSON object with data property set to null?

The response we get for an empty (has_one relationship) resource is NULL, but we expect it to be in a JSON format {data: NULL} just as we receive for a non-empty resource {data: {...}}, or for a list (has_many relationship) resource {data: []}.

What we have tried

We use ActiveModel::Serialiser and specify the key to be named "data" instead of the resource name (similar to JSON API, but the content of data is a direct JSON representation of the entity).

Models:

class User < ApplicationRecord
  has_one :profile

  def serializer_class
    V1::UserSerializer
  end
end

class Profile < ApplicationRecord
  belongs_to :user

  def serializer_class
    V1::ProfileSerializer
  end
end

Serializers:

class ApplicationSerializer < ActiveModel::Serializer
  def json_key
    'data'
  end
end

class UserSerializer < ApplicationSerializer
  attributes :id, :created_at, :updated_at #we do not include the profile here
end

class ProfileSerializer < ApplicationSerializer
  attributes :id, :created_at, :updated_at
end

Controllers:

class ProfilesController < ::ApplicationController
  before_action :authenticate_user!
  def show
    @profile = current_user.profile
    render json: @profile
  end
end

Responses:

We get a (for us wrong) response for an empty resource (GET /profile):

NULL

We correctly get a response for a non-empty resource, it looks like this (not JSON API):

{
  data: {
    id: ...,
    createdAt: ...,
    updatedAt: ...
  }
}

What we would like

We expect the response to be formatted like this in case that there is no entity associated (yet):

{
  data: null
}

Upvotes: 1

Views: 1760

Answers (2)

Barnski
Barnski

Reputation: 11

We decided to go for this more strict solution, where we respond with 404 for an empty resource.

def show
  @profile = current_user.profile
  raise ActiveRecord::RecordNotFound unless @profile
  render json: @profile
end

And in the ApplicationController we add this to handle the exception:

rescue_from ActiveRecord::RecordNotFound do |exception|
  render json: Api::ErrorSerializer.serialize(:not_found, 'Not Found'),
         status: :not_found
end

Upvotes: 0

Barnski
Barnski

Reputation: 11

I found this (workaround) solution solves the problem:

  def show
    @profile = current_user.profile
    if @profile
      render json: @profile
    else
      render json: { data: nil }
    end
  end

Upvotes: 0

Related Questions