Subresources and relationships

Assume you have a resource with a collection of subresources that only make sense in the context of the given resource, they can’t really exist on their own.

  1. Is this modeled via relationships?

  2. Does it make sense for the subresources to have a “self” link that is top-level, e.g. /subresources/{id}? Or is it OK to just have the only link that can get you to a subresource be /resources/{id1}/subresources/{id2}

  3. It seems that even though a subresource exist only in the context of resource, the ID of a subresource still needs to be globally unique so that multiple subresources coming from different resources can be in “included”, correct? It’s not hard to generate a unique ID for a subresrouce, it can be something like “resourceID-subresourceIndex”

It could be. Or you could represent the subresources as attributes of the primary resource.

Depends on your domain.

Yes.[quote=“tkjapi, post:1, topic:484”]
It seems that even though a subresource exist only in the context of resource, the ID of a subresource still needs to be globally unique
[/quote]

Yes (when combined with the type, the id alone doesn’t have to be globally unique, see here )

I am reposting my response here from a different thread, because this is exactly the same problem:

I am currently thinking about the same problem. Please allow me to brain dump my thoughts:

We have a data structure, where an object has many children and the children have attributes that are only valid within the context of this relationship. As an example, let’s say we have a photo album, which contains many photos, and each photo can contain many likes. A like is a relationship between a photo and a user. So far so simple. But: the same photo can have different likes in different albums.

From an api perspective it would be nice to add a photo to an album by its unique photo id

POST /albums/345/relationships/photos/

{
    "type": "photos",
    "id": "4"
}

So we can load the photo by

GET /albums/345/photos/4

Then we can add a like by

POST /albums/345/photos/4/relationships/likes

{ "type": "users", "id": "478" }

Then photo with id: 4 has a like by user 478 in album 345.

However in a different album, the same photo might have a different set of likes. So now we have an entity uniquely identified by an ID, but with different likes depending on the album.

From an API perspective this is not even a problem I guess. But a client would need to know that it can store the likes of a photo only in the context of an album and not on the photo object itself. Does this make sense?

To model this more explicitly, we could define a album_photo, that is: a photo contained in an album:

POST /albums/345/photos/

{
    "type": "album_photo",
    "data" : {
        "relationships" : {
            "photo": { "type": "photos", "id": "4"},
            "likes": [ { "type": "users", "id": "478" } ]
        }
    }
}

Then the server could respond with an opaque ID object which identifies this resource

CREATED

{
    "type": "album_photo",
    "id": "eyJwaG90b0lkIjoiNCIsImFsYnVtSWQiOiIzNDUifQo%3D",
    "data" : {
        "relationships" : {
            "photo": { "type": "photos", "id": "4"},
            "likes": [ { "type": "users", "id": "478" } ]
        }
    }
}

If the album already contains an album_photo with a link to the same photos entity, the server would need to either upsert the like, or better revoke the POST. Then the client could load the list of album_photos, find the one with the right photo id and then POST the like to its like relationship.

So, if you don’t want to have the album_photo id persistent on the server, it can infact be a compound value that is generated on the server, and the server knows how to convert it. Eg. {“photoId”:“4”,“albumId”:“345”} base64 encoded.

Do you guys have any thoughts on this? Somehow I don’t like either approach very much…

I have posted this question before Clarification on Resources Composed of Other Resources so please review that thread. To summarize the 1.0 specification does not allow resources to be composed of other resources, i.e. having a resource in the attributes of another resource. Instead use resource linkage from the parent to the child where the child is added in the included part of the document.

The document is basically a flattened, one level list of resources with primary resource(s) and optional included resource(s) all linked together with resource linkage as documented in “full resource linkage” in the specification. The URL building of a resource truly being a child of another resource is done by the Hypermedia API implementation when building the resource self links and depends on the domain model of the resources for the system under consideration.

For example order, order items, and payments - the order items self links would always be under a parent order (self: /orders/1234/order-items/5678) as if you delete the order, the order items are also deleted via the whole/part design. But payments are associated to an order and would not be under order (self: /payments/9999). Again this is all implementation details which depends on the “domain model” of the resources for the system under consideration that are returned by your Hypermedia API server. But you could have a JSON API document with primary data of orders, and related order items and payments in the include section with resource linkage, etc.