Code Monkey home page Code Monkey logo

Comments (18)

danivek avatar danivek commented on August 17, 2024

In the JSON API specification, on “relationships object”, we have the following:

null for empty to-one relationships.
an empty array ([]) for empty to-many relationships.

See http://jsonapi.org/format/#document-resource-object-linkage for more info.

In my opinion, I think it's better to avoid adding this option, because the outputed data will not be JSON API compliant. And the role of a serializer is to prevent generating non-compliant response.

@Mattii any thoughts ?

from json-api-serializer.

mattiloh avatar mattiloh commented on August 17, 2024

Yes, I think JSON API compliance should be our goal here. @karellm, could you explain why you don't want to have the data-field included?

from json-api-serializer.

karellm avatar karellm commented on August 17, 2024

I'm not familiar with the entire spec and I agree that it is a good idea to follow it. Maybe I'm missing something.

I use Ember.js, and if a relationship as data: [] or data: null, then it removes all the items already associated. That is to be expected. I actually think that it is the proper behaviour.

Ember also relies on links to know how to grab the data. So it is important to include these, otherwise, Ember doesn't know how to get the "school students".

In an application (think optimization), I might want to fetch only the school from the database, without bothering with the students. In that scenario, I would like the serialized payload to include the links to the students so it knows where to grab them if needed but ignore the data altogether. I would skip the attribute. I can't have it be [] or null since it would clear the potentially existing relations in my Ember app. That said, Ember wouldn't do anything if the data attribute is not there.

Does it clarify a bit the use case I'm hitting? Does the spec say anything about skipping the data attribute? I understand the valid values but that's not my concern here. It is really its presence.

from json-api-serializer.

mattiloh avatar mattiloh commented on August 17, 2024

Yes, that's a valid use-case. Could you show us the complete source object, that you pass to the serializer?

I have a similar use-case and if I don't define the relationship in my source-object, the relationship will only contain the links field (like you need it).

from json-api-serializer.

karellm avatar karellm commented on August 17, 2024

I use sequelize so I have little control over the source object. Here is what I can tell you (assuming a school with 1 student):

const school = await School.findById(1);
school.students // function
school.students() // []

Now if I include students:

const school = await School.findById(1, {include: [Student]});
school.students // [{id: 1, name: 'John'}]

I don't know if that helps. What I can say is that this would greatly help us in our current application. It allows for some neat optimizations of the db queries.

from json-api-serializer.

karellm avatar karellm commented on August 17, 2024

I think that if the relation students is on of null, { ... } or [ ... ] it could be included (even if these are empty). Otherwise it could be skipped (undefined, string, function).

It could either be a default behaviour (I think it is a fair one) or an option skipDataIfMissing.

from json-api-serializer.

mattiloh avatar mattiloh commented on August 17, 2024

What do you pass to the serializer? The school object? How does it look?
Maybe it's best if you post the complete code from fetching the database to serializing the response.

If you don't want the students relationship's data-field to be set, then the students field in school needs to be undefined.

from json-api-serializer.

karellm avatar karellm commented on August 17, 2024

@Mattii The result of JSON.stringify(school) in my first example (without the include) doesn't have a students key at all, so yes it would be undefined:

const school = await School.findById(1);
JSON.stringify(school);
{
  "id": "1",
  "name": "School Name"
}

const school = await School.findById(1, {include: [Student]});
JSON.stringify(school);
{
  "id": "1",
  "name": "School Name",
  "students": [
    { "id": 1, "name": "John" }
  ]
}

I'm not familiar with the internal of Sequelize or json-api-serializer so I'm not sure what object you rely one. If you need more informations, let me know.

from json-api-serializer.

danivek avatar danivek commented on August 17, 2024

@karellm, I'm not familiar with Ember or Sequelize, but if I correctly understand, you are facing the issue only on your first example, without include, right ?

If so it should be undefined. If not, can you post the result of typeof school.students on your first example ?

from json-api-serializer.

danivek avatar danivek commented on August 17, 2024

@karellm I think you are trying to serialize the Sequelize model instance instead of a JSON object. I take a look at the Sequelize doc, and I think you will have better result with calling toJSON() before passing it to the serializer.

from json-api-serializer.

karellm avatar karellm commented on August 17, 2024

I will summarize everything. But I think I mislead the conversation bringing Sequelize and Ember into the mix. In short if the students is undefined (first example below), I'd like a way to avoid data to be set on the relationship.

Without include

const school = await School.findById(1)
typeof school.students // function
school.students() // []
school.toJSON()
{
  "id": "1",
  "name": "School Name"
}

Currently serialized as:

{
  data: {
    id: 1,
    type: 'schools',
    attributes: {
      name: 'some school'
    },
    relationships: {
      data: null,
      links: { /*...*/ }
    }
  }
}

Expected serialized content:

{
  data: {
    id: 1,
    type: 'schools',
    attributes: {
      name: 'some school'
    },
    relationships: {
      // no data!
      links: { /*...*/ }
    }
  }
}

With include

const school = await School.findById(1, {include: [Student]})
typeof school.students // object
school.students // [{ "id": 1, "name": "John" }]
school.toJSON()
{
  "id": "1",
  "name": "School Name",
  "students": [
    { "id": 1, "name": "John" }
  ]
}

Current (and expected) serialized content:

{
  data: {
    id: 1,
    type: 'schools',
    attributes: {
      name: 'some school'
    },
    relationships: {
      data: [
        {
          id: 1,
          type: 'students'
        }
      ],
      links: { /*...*/ }
    }
  }
}

from json-api-serializer.

danivek avatar danivek commented on August 17, 2024

I made a simple try with version 1.7.0

const JSONAPISerializer = require('json-api-serializer');
const Serializer = new JSONAPISerializer();

// Schools type
Serializer.register('schools', {
  relationships: {
    students: {
      type: 'students',
      links: {
        self: '/students'
      }
    }
  }
});

// Students type
Serializer.register('students');

// Input data for schools
const input = {
  "id": "1",
  "name": "School Name"
};

const output = Serializer.serialize('schools', input);
console.log(JSON.stringify(output, null, 2));

output is:

{
  "jsonapi": {
    "version": "1.0"
  },
  "data": {
    "type": "schools",
    "id": "1",
    "attributes": {
      "name": "School Name"
    },
    "relationships": {
      "students": {
        "links": {
          "self": "/students"
        }
      }
    }
  }
}

from json-api-serializer.

karellm avatar karellm commented on August 17, 2024

I definitely don't get the same results. I actually get:

Running exactly the code you pasted, I get the same result. That's really weird, a similar setup in my project (even passing an object like the input you shared) returns data: undefined.

UPDATE: I'm trying to isolate what part of my config triggers this issue. At the moment even super simple serializer still include the data: undefined.

from json-api-serializer.

danivek avatar danivek commented on August 17, 2024

Maybe I repeat myself but I think you try to serialize Sequelize model instance. Try this:

const input = school.toJSON();
Serializer.serialize('schools', input);

or to be sure (bad practice):

const input = JSON.parse(JSON.stringify(school));
Serializer.serialize('schools', input);

from json-api-serializer.

karellm avatar karellm commented on August 17, 2024

@danivek Ok so actually JSON.stringify strips the undefined values. Try to run this:

const JSONAPISerializer = require('json-api-serializer');
const Serializer = new JSONAPISerializer();

// Schools type
Serializer.register('schools', {
  relationships: {
    students: {
      type: 'students',
      links: {
        self: '/students'
      }
    }
  }
});

// Students type
Serializer.register('students');

// Input data for schools
const input = {
  "id": "1",
  "name": "School Name"
};

const output = Serializer.serialize('schools', input);
console.log(output.data.relationships.students);

I get:

{ links: { self: '/students' }, data: undefined }

This undefined is my issue. I can also confirm that whether my output is school or school.toJSON() doesn't change anything.

from json-api-serializer.

danivek avatar danivek commented on August 17, 2024

@karellm Yes, and Ember has a different behavior between undefined and missing property ?

from json-api-serializer.

mattiloh avatar mattiloh commented on August 17, 2024

Your problem seems not to be related to json-api-serializer, but to the way your server formats its responses. I recommend to use JSON.stringify for every response, since it produces a valid JSON-string and removes undefined values of an object:

> console.log(JSON.stringify({ links: { self: '/students' }, data: undefined }, null, 2))
{
  "links": {
    "self": "/students"
  }
}

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

from json-api-serializer.

karellm avatar karellm commented on August 17, 2024

@danivek @Mattii Thanks for the great help here. I will definitely introduce JSON.stringify.

from json-api-serializer.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.