Why Can't Resource Object Attributes Contain "id," "type," or any "foreign keys?"

Within the Resource Object Fields section, it reads:

. . . a resource can not have . . . an attribute or relationship named type or id.

In order for certain libraries such as Backbone to function properly, id needs to be on the attributes object.

Although has-one foreign keys (e.g. author_id) are often stored internally alongside other information to be represented in a resource object, these keys SHOULD NOT appear as attributes.

Why are these two statements part of the spec?

I believe the first statement is because under data, it already has type and id, they’re just not included in what is considered “fields” as fields are only the attributes and relationships. They are defined as identification. The top level object is the resource you have requested.

I think the second statement is to stop people putting relationships to other resources inside the attributes.

I’m new to the specification and getting to grips with it, this is just my understanding from the spec. I hope I’m right! :slight_smile:

In the specification when it talks about “fields”, the members type, id, attributes (first-level attributes) and relationship names (rels) must share a common namespace. This allows the referencing of “fields” in query parameters such as filter, sort, or include without prefixing by namespaces such as “attributes” or “relationships” and makes these query parameters much cleaner, for example:

GET /articles/1?include=comments

GET /articles/1?include=comments.author

GET /people?sort=age,name

GET /articles/1?include=comments.author&sort=comments.author.name

compared to if fields did not share the same namespace and you had to prefix attributes or relationships because there could be a name collision:

GET /articles/1?include=relationships.comments

GET /articles/1?include=relationships.comments.relationships.author

GET /people?sort=attributes.age,attributes.name

GET /articles/1?include=relationships.comments.relationships.author&sort=relationships.comments.relationships.author.attributes.name

Or something along this line of thought, so I am grateful the authors added the “common namespace” requirement around type, id, attributes, and relationships.

As for the “foreign key” question, the authors use the word SHOULD NOT instead of MUST NOT so you could include the foreign keys if you really want to. IMHO, a better approach instead of including the foreign keys is to express the relationship that the foreign key represents as a first-class relationship in the relationships section of the respective resource object. If it is important to have the foreign key in the same document, then have the server auto-include the related resource where the foreign key is the “id” of the included related resource. This approach seems more in keeping with the json:api theme.

Related to the last bit about IMHO, a better approach instead… I wonder whether we could indeed provide a clean first-class relationship in the relationships section, but, what if in the data attributes we have a large list of foreign key relations?

As an example:

  • in the data attributes section for an order we have a collection of list items, e.g. order lines.
  • each order line needs to refer to a product specification, so has a foreign key relationship.

How would you relate/link the foreign key in the data attributes to the top level relations?

Would something along these lines be allowed in context of the specification?

{ "links": {
    "self": { "href": "/order/34" }
},
  "meta": {
"status": {
  "statuscode": "Success",
  "detailcode": []
},
"documentation": {
    "majorversion":"2",
    "minorversion":"0",
    "build":"17684"
},
"transaction": {
  "messageid":"38f3e3fe-deeb-40f8-8bbe-13816d2f17e7",
  "correlationid":"f4a42aaf-5121-4fc1-80d2-336c4f1678af",
  "conversationid":"5c328d34-b1f9-46dc-b526-b4bd813ad8f0"
}
},
   "data": { 
"type":"order",
"id":"44a42aaf-5121-4fc1-80d2-336c4f167844", 
"attributes": {
  "lineitem": [
      { 
        "lineno":"1",
        "assignedproduct":{ "type": "productspecification", "id": "63465" }
      },
      { 
        "lineno":"2",
        "assignedproduct":{ "type": "productspecification", "id": "76345" }
      }
   ]
}
  },
  "relationships": {
"assignedproduct":{ 
  "links": {
   "self": "http://example.com/productspecification/63465"
  },
  "data": { "type": "productspecification", "id": "63465" }
},
"assignedproduct":{ 
  "links": {
   "self": "http://example.com/productspecification/76345"
  },
  "data": { "type": "productspecification", "id": "76345" }
}
  },
  "included": [{
}]
}

As you can see the type and id of the product specifications in the data section and in the relationships section are aligned, so I could relate the foreign keys in the data to the links in the relationships section.

But this seems fairly cumbersome and unnecessarily complex.

Better suggestions are welcome too :slight_smile:

PS: I am aware that I am not using plural consistently.
PS: I am new to JSON (1 week) - please be constructive :slight_smile:

1 Like

Could someone please help me understand this Question? I would be interested in your opinion how to manage foreign-key relationships with the example given?

Thanks!!!

The short answer is no you can’t include the line items as attributes within the data section because you are able to compose the representation with a relationship.

The important point to understand here is that resource representations are not objects, complex types in JsonAPI are intended to be composed of resources via defined relationships to add context. These constraints aid the development of a better design with flatter, and simpler resources which will likely be much more flexible over time.

In terms of an OO mindset you can generalize the concept by saying the attributes will be your primitive data and generic collections and the relationships will be complex types or other objects.

There is a few caveats to this rule of thumb, a complex type can be an attribute if it doesn’t make sense on its own, and a primitive collection might be serving as a generic complex type and be better served broken out as a related resource.

You’ve already understood the reason to break the line items out, there is no need to keep the items in the attributes section. It is redundant, and you should be encouraging consumers of your service to use the composable nature of JsonAPI.

Thanks Michael,

Just confirm a Q to all:

  1. if the data represented are value objects which cannot be resolved independently (as order lines make no sense on their own, only within the context of an order) then it is OK to carry them as attributes, but if they can be modeled as another resource they should be a relationship.
  2. In my example the order line items only make sense within the context of the resource, so the line items can be attribute arrays.
  3. the order lines however refer to other resources. This must be documented in the relationships section. For this purpose, in the relationships section I have the links to the data it refers to. So far so good…
  4. now back to my original Q: if I have order lines for which I can find the link in the relationships section, I need a way for the consumer to understand which link belongs to which order line. I have proposed here that this link actually consists out of the foreign resource identifier (type/id) which is present both in the attributes section as well as the relationships section. Is this an allowed approach in the context of json-api?
  5. I had simplified the order structure in the above example. I have more data in the real-life order resource - for example, description, value, amount, currency et cetera. IMHO it does not make sense to drop the order lines as attributes, since this is what gives business value to this resource. the listed data is used for example to create an invoice later. But at the same time, the referenced data links to detail about e.g. product catalog resource, which IS a separate resource. Can you confirm whether or not it makes sense to keep the order lines in this case, and if not, why?

Thanks

I would actually assert that order lines do in fact make sense on their own. They can be uniquely identified, and modified without requiring the context of the larger order. The line ID does not need to be the sequence key for the record, and you can add a data element sequence_number to order the line items if you filter by order_id.

This goes back to my statement that JsonAPI does not represent an object model, but a representation of a resource. Complex resources are composed via their relationship to the base resource. By including multiple ‘line-item’ relationships in the compound document you send back to the consumer you build your complex model. You include the previously mentioned sequence_number to assist in sorting the model for use by the consumer.

The very wording of your question backs up the assertion. If you require a foreign key you are very likely dealing with a situation where the item is a resource and not an attribute. Remove the items in the data element, and you remove the requirement for matching altogether.

To some extent, but not completely - if I delete the order then the lines have no meaning, they don’t have an independent existence.

In my mind this highlights an area where JSON API could be improved. For example: the relationship between a car and its owner is of a different type from the relationship between a car and its wheels. Or, more generically, A “relates to” B and “relates to” C is different from A “is comprised of” B and C. (In UML terms this is aggregation and composition.)

JSON API currently models these different relationships the same way. I’ve started to think about what difference it would make if the spec defined a resources fields as being attributes + components + relationships rather than just attributes + relationships.

I completely understand the thought process you describe, and my first response would be to ask if you can look at the issue from a different angle. Consider the following statement before the next paragraph for a moment. Resource representations are not objects.

UML is a fantastic modeling language, but it clearly shows its basis on objects because the aggregation vs composition argument is dependent upon the concept of a strong and weak relationship. This is not a statement I make to detract UML in any way as this is hugely beneficial in certain tasks. However, I would argue the concept does not hold much utility in this domain.

To directly counter your statement of ‘no meaning’, I would simply say that is a big stretch. The line item certainly no longer has a relationship to an order, but just like the ‘deleted’ order it probably has analytics and other value, it would be fair to say it no longer has an active valid context and can be automatically garbage collected if necessary. However, suppose the service provides a mechanism for un-deleting an order, would you still suggest the line item has no meaning when an order is deleted?

The server could provide some links with “expired” or “orphaned” rels (however it is defined in your vocabulary) to ‘suggest’ the client delete these unnecessary resources, but the line item resource remains self contained without a relationship to a particular order. Without batchable processing this is probably a chatty solution, the implementer is also free to remove orphaned line items as a side effect of removing the order itself as an encapsulated implementation detail. The decision of how to manage relationships between resources when one resource is deleted should be left entirely to the service implementer. The important distinction is this is unnecessary for the consumer to know, they shouldn’t have to concern themselves with a complex resource representation which can and should be represented with a relationship. In this way everything is an ‘aggregation’ and the inclusion of a true sub resource denotes the UML defined ‘composition’, and any exception cases are properly encapsulated by the service to preserve the simplest interface possible.

Your suggestion of components breaks the paradigm of hypermedia as you have another vehicle to carry some form of application state. I believe this is a poor choice to make for a couple of reasons off the top of my head. First, adding the additional container not only invalidates some core tenets which allow Json API to be utilized in a fielding restful manner ie hypermedia driven, but it makes crud ‘regular’ consumption of the format more complex as well. If the two use cases for the format both get harder to use in order to support a change which exposes unnecessary complexity to the consumer then I think this is a non-starter.

To understand why, is a long trip but I would suggest starting with my first statement, which is simply that resources are not objects but representations of a resource which can send and receive different messages. As a contemporary Java developer, it was hard for me to get my head around this in the beginning because the contemporary definition of an object in java has somewhat diverged from the object oriented definition of an object, the latter of which more closely resembles a http resource. My learning vector into this was a foray into HAL and ALPS through Spring, then watching a ton of talks by none other than @steveklabnik and mike amundsen.

1 Like

I agree with that. However, my difficulty here is around what happens when deleting one resource affects the attributes of another. For example, suppose the deletion is the other way around - if I remove a line item from an order, then potentially this could affect the order’s attributes (e.g. total price). If I have a CRUD-like API (rather than an event-driven one) then what should I return as the response? (I’ve come up with a number of different approaches, but they all feel like hacks rather than a natural use of JSON API)

Your suggestion of components breaks the paradigm of hypermedia as you have another vehicle to carry some form of application state.

I don’t see how that follows. In my current vision, the “components” section of the response would have the same structure as relationships currently does, it would just have a different label. This would allow different semantics to be defined for operations on the resources identified as “components”. For example, when an operation is carried out on a component, the response could be an updated view of the parent resource, rather than an updated view of the modified resource.

This is one of the main weaknesses of the CRUD model, not only is the hierarchy structurally brittle, but the strength of the relationships between the resources is unclear. The best idea in this case, would be to return the lowest common denominator resource which is effected by this change. Using your order example, I would return the order with a location header pointing to the entire resource which was effected by the change. The use of ETag and if-unmodified-since headers can significantly reduce the stress of working on the order resource if you are unsure if the client has the most up-to-date representation.

To explain this a bit more, I would argue that total_price is actually not an attribute, it’s an aggregate of values of related items. In other words it’s actually a convenience field, not a true property in and of itself. If your service presents things in the appropriate manner, they should get total_price from the summation of all line item’s price + tax. In this way, the hierarchical model hides yet another relationship in plain view. This is probably even incomplete, as the tax of line items can vary depending on where the item is being sold, where it’s shipping from and to, so even my example isn’t completely devoid of hidden relationships, and is meant to illustrate the point only.

The reason is touched on above in addition to the constraint that all relationships between resources are supposed to be described as relationships. From a high level, you have added a second place for a different type of relationship, so interacting with the resource has become 4x more complicated. Secondly, even your suggestion belies the fact that they the same thing. If the representation is the same for the component and relationship, then they are both the same thing, and should not be interacted with differently.

Ultimately what you are really asking for is the consumer to do some of the work necessary to decide which representation to send back to them with appropriate location header. This is unnecessary, as you already have all the information you need to make this determination on your own without depending upon external input. As the implementer, you know if a relationship is more of a composition or action relationship by the vocabulary and implementation details. You can most easily decide how to handle updates to particular relationships as you have the most information to act upon.

There is nothing blocking applying different semantics to your implementation to accomplish the goals you set. There is also no need to burden the consumer with any additional complexity to accomplish your goals.

Thanks guys for your valuable feedback. I feel (given the discussions here and discussions I had with other professionals) that JSON-API is trying to over-simplify by flattening the structure of the data (attributes).

A few key statements from other areas of expertise:

Law of the instrument:
if you only have a hammer then every problem looks like a nail.

This is something often repeated in the context of microservices and does not only affect the way services/component are implemented, but directly affects the way interfaces are modeled. Interfaces are in our case implemented as JSON-API documents. I feel we are using JSON-API as a hammer and try to make everything look flat. Note the use of the word document here. Please see below “Documents not parameters”.

Documents not parameters:
REST resources should represent functional documents rather than technical parameters. From Fowler’s microservices article: A naive conversion from in-memory method calls to RPC leads to chatty communications which don’t perform well. Instead you need to replace the fine-grained communication with a coarser -grained approach.

In the example that I have the order structure was still fairly flat. Only one level of complexity for the order lines. If the order however triggers a workorder, in our business model the workorder can have items, which can have tasks and subtasks, so three nesting levels of complex types. Even if we trigger another resource “workorder” (respecting functional normalization of resources), the nested structure of the elements inside the workorder do not make sense standalone as tasks and subtasks are interrelated and depend on each other, so have an order of execution.

Should we follow your approach of splitting workorder items, and tasks (and perhaps subtasks) into separate resources then (besides becoming very chatty in our conversations with the resources) suddenly we need to have knowledge in the resource consumers to make sense out of this. This jeopardizes one of the main concerns (decoupling) of REST. Consumers are now coupled (have logic) that should reside in the resource provider instead. Bear in mind that decoupling is not only needed from technical PoV, but also from functional PoV.

One of your statements was

Resource representations are not objects

Here I concur but I would also like to add that resource representations are not parameters only. Resources manage business documents from a functional point of view. The above description on documents not parameters supports this point of view.

Componentization via services
… a unit of software that is independently replaceable and upgradeable… (Fowler)

Another important concern for microservices is that they must independent. This reflects on the current discussion since, if we would implement items, tasks and subtasks as standalone resources, we now need to manage dependencies between these resources (need to have logic in one resources to manage another resource).

I can understand your example that the total price could be a calculated amount, but I can also see accounting people disagreeing with this - as the price can be established (for a final invoice) only once. Even in an invoice document where we would have invoice lines and amounts, the price of the individual lines as well as the total invoice amount has to be finalized and baselined at some point in time, meaning that it has to be recorded as part of the resource as standalone attributes.


All this leads me to believe that (and I concur with jlangley here) resources can have more complex structures if they don’t make sense as standalone resources. This keeps a more coherent view on things that belong together functionally, so from a business point of view. This is of course relevant only in scenarios where we try to build microservices. I also can understand the reasoning behind composition/aggregation. This is not only applicable to object models but (michaelbay please be open to another PoV): also applicable to business documents. An order document without order lines just does not make any sense, as neither do standalone orderline documents…

I do realize that at some point in time the documents may become large and hard to process, defying one of the ‘rules’ of microservices to keep them small and simple, but bear in mind that if it does not make sense to map as a separate (independent) resource, what other option would we have? In my humble opinion, none… A big advantage of having a more complex order structure is that all the logic to manage the contents of an order is located in one single place, instead of three different (related) resources or worse. across all consumers who now need to know about the relation between resources and manage all the consequences.

The way I read the spec, unfortunately that is not allowed: The response document MUST include a representation of the updated resource(s) as if a GET request was made to the request URL.
If an Order Line is a separate resource with its own URL, a GET on that URL has to return an Order Line, not an Order. Therefore a PUT to the same URL must also return an Order Line, not an Order.
We can work round this by having a GET to the Order Line URL return a Compound Document that includes the related Order. This then allows the PUT to return the same Compound Document. But this feels like a kludge to me.

I would argue that total_price is actually not an attribute, it’s an aggregate of values of related items. In other words it’s actually a convenience field, not a true property in and of itself.

I would argue that an API should be making life easier for its clients. And since total_price is likely to be something many clients are interested in, the API should provide this rather than force all its clients to implement the order totalling logic.

interacting with the resource has become 4x more complicated.

Is that number specific (based on a power law perhaps)? Or is it just arbitrary to represent an increase?

Ultimately what you are really asking for is the consumer to do some of the work necessary to decide which representation to send back to them with appropriate location header.

I don’t understand how this follows. In my idea the URL isn’t included twice (once under relationships and again under components), but only once. The consumer doesn’t get to decide anything. Rather the API communicates that if a related resource is updated then the response will be a representation of that related resource; whereas if a component resource is updated then the response will be a representation of the current resource.

However, having done some more reading and thinking about this I’m cooling a little on the idea of adding components. components solves the problem only for resources in a hierarchy, it doesn’t solve the more general problem of how the API can communicate that multiple resources have changed in response to a client requesting an update of a single resource.

For clients that support callbacks this isn’t a problem. But for other for clients, although we can use ETags so subsequent GETs return the latest resource representations, we can’t communicate (at runtime) to the client which resources they should refetch.

We could try using an event here - if the client creates an Order Update Event instead of updating an Order or an Order Line, the response could be a Compound Document that includes all the changed resources. However, if the affected resources are not linked directly but only via one or more other intermediary resources then:

  1. The response could get quite large
  2. Not all the resources in the response are guaranteed to have been updated, so the API needs to flag these somehow or the client needs to do an internal diff process to discover this if it wants to highlight the changed resources to a user

I need to think a bit more about this indirectly linked scenario as it might not be a genuine possibility - so far the only examples I have are where a resource update causes the resource to leave a collection; and I think in JSON API a collection of resources is not itself a resource so there may be some subtleties to work through here.

We may be wandering off-topic. Should continue this discussion in a separate thread?

I would urge you to read more on the topic of real REST generally and hypermedia specifically. I fear with your emphasis you are leaning too heavily on a tangible definition of documents and missing at least some of the nuance which hypermedia provides to address the concerns you have.

The referenced ‘naive’ implementation is analogous to a poorly ‘normalized’ crud API, it requires and represents the extremely chatty nature described in your quote. However, hypermedia formats provide solutions to your concerns via JSON APIs includes parameterization for example to include all relevant relationships to reduce the chattiness.

I’ve already addressed the chattiness above, but you do make an excellent point in that a key benefit and concern of REST is highly decoupled client and server. However, I believe your concern is stemming from a naive approach to affordance definition (i.e. CRUD only or OAS). If your domain is getting that complex, I strongly urge you to look into the things I’m posting and linking regarding hypermedia. Creating a vocabulary which models the domain, and designing your API to match that vocabulary will greatly reduce the interface design complexity. You’ll have a 1-1 mapping of messages to send for complex actions to happen, which simply can not map into the CRUD / url partitioning pattern. It is quite OK for clients to have logic on them, in fact it’s encouraged as part of the REST dissertation, however the type of logic is key. You DO want your clients to have the logic to manage what they want to do, while you DON’T want your clients to have to contain the logic of HOW to do it (the business logic).

Yes, but I again caution you to avoid conceptualizing the resource as business documents as analogs to physical forms for processes. It is a very easy trap to fall into, but this leads you away from the many benefits of correctly defining the API interactions through your vocabularies. The complexity you describe is best handled by message representations, analogous to HTML forms, and is one of the primary benefits of vocabulary defined resource and message representations as it frees you of the burden of mapping complex processes to 4 methods on various resources.

It is imperative to understand that resource representations and message representations are two distinct concepts. Avoid letting the CRUD mentality slip over into a hypermedia service design. In CRUD the resource representation is the only message representation a resource accepts and understands.

Microservices is an entirely different discussion, but if you take my educational illustration and deploy them all as independent services you are of course correct it would be ridiculous - each service should be independent and self sufficient. That isn’t analogous to individual resources, which are very likely to be only a part of a larger complete service. However, to your deployment point on microservices, they will always require dependency management and orchestration. If you aren’t doing that, you are asking for trouble when nodes start to preform strangely and you don’t have visibility into the architecture.

Yes, that example was intentionally overboard to illustrate the point, I even said that a couple times to make sure nobody would take it at face value.

My arguments were intentionally generalized as this is a forum to discuss JSON API, I was not using it as a soap box from which to preach the solution as I see it. I make these statements as hints towards what my extensive research has shown me is actually the way forward through the difficulties we all share. Those items do make sense independently, and it would require you to understand why they make sense because it isn’t immediately intuitive. In that light I would invite you to read my guidelines on hypermedia APIs on my blog, the discussions of the individual points, as well as the comparisons to current approaches for a background information and summary of much of what I have read and listened to on the subject. I can assure you I have thought about a lot of these concerns previously and at great length while compiling the data for my hypermedia API work. Take a look, and I whole heartedly invite you to challenge my points and posts on this which don’t seem to make sense. I’m not omniscient and it is quite possible I have missed many nuanced concerns in my research.

The spec you are pointing to is the update portion and not delete. However addressing the delete method, nothing in there suggests you can’t send a new location header and new ETag for the resource which has expired. This can be utilized by caching layers, and clients to expire the now stale resource.

The compound document for the Order has changed, but the DELETE to line-item has succeeded, so the resulting response would be something like:

Request:

DELETE /line-item/2

Response:

location:/order/1
ETag: ad8jrjwfns8naf3afdhadaf1jfda
204 NO CONTENT
(no body)

The message this sends is double meaning, but complete and adheres to the specification. In this example the server responds to the delete request by removing the resource, and returning the 204 status code with an empty body. However the headers to the response tell the story of the highest common denominator resource which can expire cached copies through intermediaries and ultimately on the client itself. The location of the effected resource is the cache key, and the new ETag would invalidate all cache copies along the way.

I said a few times I think this discussion was illustrative for educational purposes to understand the nuanced points I am making, and not necessarily an advocacy of an overly pedantic service. You are entirely correct, to remove all the convenience fields as I described them would be a pretty lousy idea in a real implementation.

Yes, 4 is not a random number, but a function of the power of 2.

You aren’t including the URL twice, but again increasing the complexity of managing the representations in two locations. While you’re trying to reduce the complexity of dealing with relationships, the result is actually a 4x increase in the difficulty - obviously not your intent.

I would advise you be very wary of ‘nounifcation’ of verbs in order to create resources which describe affordances, they are snowflake and almost always unintuitive to consumers. The better way would be to properly use a vocabulary of resources, affordances, and goals to handle the actions.

Regarding your statements about cache invalidation, you are obviously beginning to see why some of the most famous contemporary computer scientists consider it one of the most difficult problems to solve. To your first point, its possible to simply return resource identifier ‘objects’, and use them to invalidate the cached copies of resources in the client to ensure integrity while reducing the message size. Additionally, my previous suggestion helps to handle your second point and reduce the HEAD traffic for cache verification when there is a single resource you can return which is the lowest common denominator. It is likely this won’t always be the case, and you will be stuck performing duplicate calls on failed ETag validation and that simply is the nature of HTTP, it’s designed to fail elegantly.

I do agree this is wandering off topic, as this is directly related to the topics I discuss on my blog on hypermedia and not to JSON API, I would invite you to continue the discussion there with a link for interested parties to follow if they wish.

True, but it’s just another aspect of the same problem - updating a line item (e.g. changing quantity ordered) affects the order resource as well.

Why is a power law involved here?

I’m currently thinking about an alternative mechanism to handle this scenario - instead of adding a components section, perhaps it would be simpler to allow a 303 response in some cases (rather than mandating a 200)? This could allow a server to direct the client to an updated order resource when an order line is modified.

The spec doesn’t say anything about how the Location and ETag headers need to behave in the GET scenario, this might be an omission or intentional. As stated in 1.0 I believe you could return a location header and ETag of the root resource you want to invalidate in the caches, and return the full order “resource object” as a relationship of the update itself. This feels pretty hackish, and would probably be a bad idea from a caching perspective.

In this case, I think the only real option is to return the order as a relationship to the line item, with a meta property of ETag to update the end clients cache with the new resource representation, and new ETag version to invalidate caches upstream.

You can’t always get the perfect outcome, and in that case it is good that HTTP has a very resilient failure design.

There would now be 2^2 combinations of locations to process or add type matches, where there was 1.

I think I like it. I can get behind that kind of a solution. That would gracefully update the clients cache through the entire cache hierarchy, but it would complicate the implementation of the JSONAPI clients. Another issue is the specification said it MUST respond with the behavior of a GET, and this change would present a breaking change from 1.0. I don’t know how likely in that case it would be to get the change allowed.