Clarification on ids in attributes

I’ve read through https://github.com/json-api/json-api/pull/588 and https://github.com/json-api/json-api/issues/799

For the most part I get the argument why type shouldn’t be included in the attributes, but I’m not sure I completely understand id.

If my “id” attribute on a model was some other value like “uuid” would this be recommended against?:

{
  "type": "articles",
  "id": "1",
  "attributes": {
    "uuid": "1",
    "title": "Rails is Omakase"
  }
}

Similarly it seems like a lot of data implementations of models expect a json attributes object that includes the id. For backbonejs for instance you may need the data to be:

new MyBackboneModel({
  "uuid": "1",
  "title": "Rails is Omakase"
});
// but for some other model it may need to be
new MyBackboneModel({
  "id": "1",
  "foo": "bar"
});

So I understand not wanting different id values in the attributes from the root… but I’m not sure I understand why id shouldn’t exist in the attributes if it is the same id

Not that this is hard:

Backbone.Collection.extend({
  parse(response) {
    return _.map(response.data, data => {
      data.attributes.uuid = data.id;
      return data.attributes;
    });
  }
});

But I’d much rather be able to everywhere:

Backbone.Collection.extend({
  parse(response) {
    return _.map(response.data, 'attributes');
  }
});

The idea of the JSON Api specification is to create a uniform message structure and format for composition. The message content is not directly analogous to an object model, as complex structures are to be composed via the relationships between resources. The specification uses the special ‘id’ field internally as keys to compose the de-normalized model, or compound documents at the receiving end. Utilizing multiple field names as keys would reduce the readability and standardization of the format itself.

The ID field does not have any particular data format requirements, so there is no reason you can’t simply serialize the representation model as ‘id’ with an internal name of uuid if that is more helpful to your implementation. Think of it as a catch all for the ‘identifier’ of this particular resource. The only requirement is that to be valid JSON Api, it must have an id field as part of the resource object itself. This is required resource metadata for the entire structure to function correctly.

It totally makes sense that the id be in the root, and it’s good to know that there’s nothing specifically wrong with storing an identifier internally in attributes.

In some ways this mirrors what a Backbone.Model does anyhow

var Model = Backbone.Model.extend({
  idAttribute: "uuid"
});
var m = new Model({ uuid: '1', foo: 'bar'});
m.id === '1';
m.attributes = { uuid: '1', foo: 'bar' })

However not setting an idAttribute in Backbone would assume that the model’s id is also stored as id on the attributes.

Similarly I’m still having a difficult time grasping why having an attributes.id causes an issue with json-api if it is intended to represent the same value. I completely get why type should be avoided… the resource type doesn’t belong on the attributes. The type describes attributes, and it would be more confusing to have attributes.type represent some other value (and complicate filtering). But in the case of id the resource’s id and attributes.id would seemingly always carry the same meaning.

Sorry if I’m being dense. I’m just trying to have a full understanding without jumping in all the way.

For all intents and purposes the type and id parameters are actually just required attributes. By placing them as part of the root resource object, no fancy validation of the contents of attributes is required.

public class Foo{
  private String id;
  private static final String type = "Foo";
  private String bar;
}

The following would be the simple json serialized format.

{"id":"1","type":"Foo","bar":"bah"}

To have id in the attributes field, would be either redundant or cause a conflict so it is not allowed.

I get that it’s redundant. The redundancy seems useful when faced with the alternative being, having to always mutate incoming data. But I’m certainly concerned with possible conflict… if the id is redundant where would conflict occur?

Which ID would you be referencing? If the values are different, which is the id.

My advice is to write a default wrapper function, so you don’t need to manage this on every Backbone model and realize this is a constraint you can’t change because it is correct.

The point of making them redundant would be so that they are never different. But I guess I’ll have to live with “because I said so”

And we can write a parse that handles it for every backbone.model… that just means every time any data is fetched it must first go through additional processes to add the id to the attributes hash. It just seems like an unnecessary extra bit of work that has to be done for every single model simply “because it is correct”

But right now I’ve learned that it’s ok to store the id on the attributes as long as I call it something different because if I call it id there’s a concern that it might be a different value somehow for reasons beyond my ability to comprehend.

I’m sorry I couldn’t explain it in a way which was more helpful to you. The format has a much wider audience than your use case with Backbone, and whatever the requirement is to have the resource identifier not named ‘id’.

I don’t see any value added whatsoever by duplicating data in the message, the only potential side effects are negative so there is no reason to advocate doing this.

Representation translation is a very common task when using messages between systems across domains, I don’t see this as any different. The format has a constraint which requires the ID and TYPE to be super attributes to allow for other beneficial properties, e.g. normalization in compound documents and the inclusion of resource identifier objects. This is beneficial on the whole with no ‘new’ negative consequences on the use of the format as some level of translation is expected when using a hypermedia capable format like JSON Api. If you don’t want to deal with the work required to correctly utilize the format, you can’t expect to gain the benefits from it’s use.

I’m not trying to frame this as a ‘because I said so’ explanation, but really in some way it comes down to a ‘because the specification said so’ answer if you don’t want to really dig in to understand why it needs to be this way.