jsonapi-rb / jsonapi-rails

Rails gem for fast jsonapi-compliant APIs.
http://jsonapi-rb.org
MIT License
321 stars 63 forks source link

Fields of relationship objects are missing in result JSON #111

Open drbarto opened 5 years ago

drbarto commented 5 years ago

I have an issue with jsonapi-rails (v0.4.0) and rendering relationships. I have a simple model consisting of Challenge and ChallengeAnswer; a Challenge has_many ChallengeAnswers, and a ChallengeAnswer has a value. Here are my serializable classes:

class SerializableChallenge < JSONAPI::Serializable::Resource
  type "challenges"

  has_many :answers do
    data do
      puts "SerializableChallenge: custom answers relation called"
      @object.challenge_answers
    end
  end
end

class SerializableChallengeAnswer < JSONAPI::Serializable::Resource
  type "challenge_answers"

  belongs_to :challenge

  attribute :value do
    puts "SerializableChallengeAnswer: custom value attribute called"
    @object.value
  end
end

And this is the render call in my controller action:

render jsonapi: challenges, include: [:answers]

Now I would except that my result JSON contains a relation named answers, and the relation contains elements with a value. Instead, this is what I get:

"relationships": {
  "answers": {
    "data":[
      { 
        "type": "challenge_answers",
        "id": "cb0afe1f-fe68-4eb1-9454-8b56f419a4e9"
      }
    ]
  }
}

So the relationship's data is correctly looked up (the id is correct), but the data fields (i.e. value) are missing.

As you can see in my serializable classes above, I added log messages, and both messages are logged. This means that my custom value attribute implementation is executed, but its value is not included in the result.

I tried various fixes, e.g. using fields: { answers: [:value] }, but without success. Can someone point out what I'm doing wrong here? Maybe I'm getting the whole point of "relationship" wrong.

patricklewis commented 4 years ago

@drbarto Were you able to resolve this? I think that in your example changing your include to read include: [answers: [:value]] will add the data you are looking for to the included array of the response, though it seems to not add the value field to the relationships.answers.data key.

I am trying to resolve a similar issue with multiple levels of relationships in my app that doesn't involve having to look up objects in the included array by id/type.

drbarto commented 4 years ago

@patricklewis thanks for the hint! I'm not working on that codebase anymore, so unfortunately I cannot give your suggestion a try -- but hopefully it will be helpful to others :)

IIRC I ended up writing a custom attribute which returned the nested data:

attribute :answers do
  @object.challenge_answers
end

While I would have preferred the nice declarative form, this also worked and even made it simpler to generate the output format which I was after.

patricklewis commented 4 years ago

@drbarto Nice.. I also ended up using a similar delegation approach in my application after continuing to have trouble with multiple levels of nested relationships in the jsonapi call.

This is what I ended up with:

controller

            render(
              jsonapi: outcome.result,
              include: [:messages],
              fields: {
                messages: %i[body recipient_names sender_name]
              }            

'message' serializer

    attribute :sender_name do
      @object.sender_name
    end

    attribute :recipient_names do
      @object.recipients.includes(:person).map(&:name)
    end

As you noted, this gave me more control over the format of the response and seems to be the best workaround. Thanks!