mattpolzin/JSONAPI

Nested Relationships

KazimAhmad opened this issue · 3 comments

Hi,
How to access the following nested relationship:
object in INCLUDE array has relationship to other objects present in the same INCLUDE array

I'll just reiterate what I am hearing to confirm I know what you would like to be able to do. For some primary resource (we'll say dog) which has a relationship for its owner (of type person), you want to access a dog's owner's friends (for example). Something like (straw man syntax to follow) primaryResource.value.owner.friends to access an array of person objects from the INCLUDE array.

If this is what you are looking for, it is not something built into this JSON:API framework right now. I stayed away from relationship access intentionally (exposing only the Id of related objects via primaryResource.value ~> \.owner). There are a few tangentially related reasons why I stayed away from relationship access, but suffice to say the most user-centric one is the assumption that any sizable project will make multiple API requests for interrelated resources and therefore it will be practical to have a local cache of resources (whether it is a SQLite database or just an in-memory store of some kind) and accessing relationships localized to one particular response document would be much less useful because you would want to reach out to your local store for relatives anyway. This does put isolated API request handling at a bit of a disadvantage because there is no trivial way to access relationships as you are describing in this question.

Just for the sake of completeness, I will stub out a simple strategy for doing what your question gets after, even though there is no one-liner built into the library to handle the use-case in question. Note I am throwing caution to the wind below for the sake of brevity (force unwrapping and no error handling).

let response = ... // decode a JSON:API Document with a single `Dog` primary resource and `Person` includes.

let dog = response.body.primaryResource!

// Grab people from includes, map them to pairs of (Id, Person)
let peopleIdPairs = response.body.includes![Person.self].map { ($0.id, $0) }

// Create a dictionary of people
// In a more complete example, this step might actually be adding new people to an existing dictionary of people from previous API requests.
let people = Dictionary(peopleIdPairs, uniquingKeysWith: { $1 })

// The dog's owner can be retrieved from the people cache by Id.
let dogsOwner = people[dog ~> \.owner]!

// The dog's owner's friends can be retrieved by mapping the Ids of the dog's owner's friends to people in the cache.
let dogsOwnersFriends = (dogsOwner ~> \.friends).compactMap { people[$0] }

I'm going to close this as answered but feel free to follow-up anyway.

Just following up for future readers because there are now two relevant clientside storage examples in the following repository: https://github.com/mattpolzin/JSONAPI-ResourceStorage

The two modules in that repository showcase different approaches to resource management and one of them (the resource store) provides resources that know how to access relationships similarly to how this question hoped to.