GithubHelp home page GithubHelp logo

seyz / jsonapi-serializer Goto Github PK

View Code? Open in Web Editor NEW
732.0 18.0 195.0 797 KB

A Node.js framework agnostic library for (de)serializing your data to JSON API

License: MIT License

JavaScript 99.77% HTML 0.23%
json-api serializer deserializer javascript

jsonapi-serializer's Introduction

I am looking for new maintainers

If you are interested, send me an email: [email protected]

JSON API Serializer

JSONAPI Serializer Logo

Build Status npm version download

A Node.js framework agnostic library for (de)serializing your data to JSON API (1.0 compliant).

psst: If you need an off-the-shelf admin panel for your app, check out what I build in my day job at forestadmin.com - it uses jsonapi-serializer to de/serialize data data coming from/to the APIs.

Installation

$ npm install jsonapi-serializer

Documentation

Serialization

var JSONAPISerializer = require('jsonapi-serializer').Serializer;
new JSONAPISerializer(type, opts).serialize(data);

The function JSONAPISerializer takes two arguments:

  • type: The resource type.
  • opts: The serialization options.

Calling the serialize method on the returned object will serialize your data (object or array) to a compliant JSONAPI document.

Available serialization option (opts argument)

  • attributes: An array of attributes to show. You can define an attribute as an option if you want to define some relationships (included or not).
    • ref: If present, it's considered as a relationships.
    • included: Consider the relationships as compound document. Default: true.
    • id: Configurable identifier field for the resource. Default: id.
    • attributes: An array of attributes to show.
    • topLevelLinks: An object that describes the top-level links. Values can be string or a function
    • dataLinks: An object that describes the links inside data. Values can be string or a function (see examples below)
    • dataMeta: An object that describes the meta inside data. Values can be a plain value or a function (see examples below)
    • relationshipLinks: An object that describes the links inside relationships. Values can be string or a function
    • relationshipMeta: An object that describes the meta inside relationships. Values can be a plain value or a function
    • ignoreRelationshipData: Do not include the data key inside the relationship. Default: false.
    • keyForAttribute: A function or string to customize attributes. Functions are passed the attribute as a single argument and expect a string to be returned. Strings are aliases for inbuilt functions for common case conversions. Options include: dash-case (default), lisp-case, spinal-case, kebab-case, underscore_case, snake_case, camelCase, CamelCase.
    • nullIfMissing: Set the attribute to null if missing from your data input. Default: false.
    • pluralizeType: A boolean to indicate if the type must be pluralized or not. Default: true.
    • typeForAttribute: A function that maps the attribute (passed as an argument) to the type you want to override. If it returns undefined, ignores the flag for that attribute. Option pluralizeType ignored if set.
    • meta: An object to include non-standard meta-information. Values can be a plain value or a function
    • transform: A function to transform each record before the serialization.

Examples

Simple usage:

var data = [
  { id: 1, firstName: 'Sandro', lastName: 'Munda' },
  { id: 2, firstName: 'John', lastName: 'Doe' }
];
var JSONAPISerializer = require('jsonapi-serializer').Serializer;

var UserSerializer = new JSONAPISerializer('users', {
  attributes: ['firstName', 'lastName']
});

var users = UserSerializer.serialize(data);

// `users` here are JSON API compliant.

The result will be something like:

{
  "data": [{
    "type": "users",
    "id": "1",
    "attributes": {
      "first-name": "Sandro",
      "last-name": "Munda"
    }
  }, {
    "type": "users",
    "id": "2",
    "attributes": {
      "first-name": "John",
      "last-name": "Doe"
    }
  }]
}

Deserialization

var JSONAPIDeserializer = require('jsonapi-serializer').Deserializer;
new JSONAPIDeserializer(opts).deserialize(data);

The function JSONAPIDeserializer takes one argument:

  • opts: The deserializer options.

Calling the deserialize method on the returned object will deserialize your data (JSONAPI document) to a plain javascript object.

Available deserialization option (opts argument)

  • keyForAttribute: A function or string to customize attributes. Functions are passed the attribute as a single argument and expect a string to be returned. Strings are aliases for inbuilt functions for common case conversions. Options include: dash-case (default), lisp-case, spinal-case, kebab-case, underscore_case, snake_case, camelCase, CamelCase.
  • AN_ATTRIBUTE_TYPE: this option name corresponds to the type of a relationship from your JSONAPI document.
    • valueForRelationship: A function that returns whatever you want for a relationship (see examples below) can return a Promise (see tests)
    • transform: A function to transform each record after the deserialization.

Examples

Simple usage:

{
  data: [{
    type: 'users',
    id: '1',
    attributes: {
      'first-name': Sandro,
      'last-name': Munda
    }
  }, {
    type: 'users',
    id: '2',
    attributes: {
      'first-name': 'John',
      'last-name': 'Doe'
    }
  }]
}
var JSONAPIDeserializer = require('jsonapi-serializer').Deserializer;

new JSONAPIDeserializer().deserialize(jsonapi, function (err, users) {
  // `users` is...
});
[
  { id: 1, firstName: 'Sandro', lastName: 'Munda' },
  { id: 2, firstName: 'John', lastName: 'Doe' }
];

Relationship:

{
  data: [{
    type: 'users',
    id: '54735750e16638ba1eee59cb',
    attributes: {
      'first-name': 'Sandro',
      'last-name': 'Munda'
    },
    relationships: {
      address: {
        data: { type: 'addresses', id: '54735722e16620ba1eee36af' }
      }
    }
  }, {
    type: 'users',
    id: '5490143e69e49d0c8f9fc6bc',
    attributes: {
      'first-name': 'Lawrence',
      'last-name': 'Bennett'
    },
    relationships: {
      address: {
        data: { type: 'addresses', id: '54735697e16624ba1eee36bf' }
      }
    }
  }]
}
var JSONAPIDeserializer = require('jsonapi-serializer').Deserializer;

new JSONAPIDeserializer({
  addresses: {
    valueForRelationship: function (relationship) {
      return {
        id: relationship.id,
        'address-line1': '406 Madison Court',
        'zip-code': '49426',
        country: 'USA'
      };
    }
  }
}).deserialize(jsonapi, function (err, users) {
  // `users` is...
});
[{
  id: '54735750e16638ba1eee59cb',
  'first-name': 'Sandro',
  'last-name': 'Munda',
  address: {
    id: '54735722e16620ba1eee36af',
    'address-line1': '406 Madison Court',
    'zip-code': '49426',
    country: 'USA'
  }
}, {
  id: '5490143e69e49d0c8f9fc6bc',
  'first-name': 'Lawrence',
  'last-name': 'Bennett',
  address: {
    id: '54735697e16624ba1eee36bf',
    'address-line1': '406 Madison Court',
    'zip-code': '49426',
    country: 'USA'
  }
}]

Notes on Promises

The deserialization option valueForRelationship supports returning a Promise and so this library uses Promises under the hood. bluebird was previously used as a dependency, but due to bundle size concerns on both node and the web it was replaced with native promises.

bluebird is definitely more performant than native Promises. If performance is a major concern Promise can be globally polyfilled

  • node - via global.Promise = require('bluebird')
  • web - global Promise automatically gets assigned when using the script tag to load bluebird

Error serialization

var JSONAPIError = require('jsonapi-serializer').Error;
var error = new JSONAPIError(opts);

The function JSONAPIError takes one argument:

  • opts: The error options. All options are optional.

Available error option (opts argument)

  • id: a unique identifier for this particular occurrence of the problem.
  • status: the HTTP status code applicable to this problem, expressed as a string value.
  • code: an application-specific error code, expressed as a string value.
  • title: a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
  • detail: a human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized.
  • source: an object containing references to the source of the error, optionally including any of the following members:
    • pointer: a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute].
    • parameter: a string indicating which URI query parameter caused the error.
  • links: a links object containing the following members:
    • about: a link that leads to further details about this particular occurrence of the problem.
  • meta: a meta object containing non-standard meta-information about the error.

Examples

Simple usage:

var JSONAPIError = require('jsonapi-serializer').Error;

var errors = new JSONAPIError({
  code: '123',
  source: { 'pointer': '/data/attributes/first-name' },
  title: 'Value is too short',
  detail: 'First name must contain at least three characters.'
});

// `errors` here are JSON API compliant.

The result will be something like:

{
  "errors": [
    {
      "code":   "123",
      "source": { "pointer": "/data/attributes/first-name" },
      "title":  "Value is too short",
      "detail": "First name must contain at least three characters."
    }
  ]
}

License

MIT

jsonapi-serializer's People

Contributors

alexsotocx avatar ar-mac avatar awochna avatar cesine avatar craigcartmell avatar dakolech avatar hartzis avatar ianvs avatar ilroux avatar itsfadnis avatar jamesdixon avatar javiacei avatar jelhan avatar kiwiupover avatar kpollich avatar lostintime avatar m-bodmer avatar nvh avatar ozanmuyes avatar sarus avatar seyz avatar skysplit avatar slimee avatar testica avatar tonylukasavage avatar vincentmolinie avatar winglian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonapi-serializer's Issues

Filtering Attribute for Object

Hey there,

I think we should be able to still specify if the object can still be filtered out by attributes settings even if its not a compound document or something... For example, I have settings like this:

{
    attributes: ['status', 'roles']
    roles: {
        attributes: ['name']
    }
}

However, when an opts setting does not include ref: as seen on line #128 then the response will look like this still (unfiltered):

{
  "data": {
    "type": "users",
    "id": "7fb4a2bd-a30c-4b9a-a15b-f9a13ed0ffca",
    "attributes": {
      "status": "active",
      "roles": [
        {
          "id": 245,
          "name": "sysadmin",
          "userId": 216,
          "updatedAt": "2016-01-18T12:53:36.000Z",
          "createdAt": "2016-01-18T12:53:36.000Z"
        }
      ]
    }
  }
}

Do you think we can work this into it?

Test creating invalid JSON API result?

I just updated to v2.0.4 (from the 1x branch) and a lot of my tests are breaking now. I noticed the following test in jsonapi-serializer that appears to be creating an invalid json payload:

https://github.com/SeyZ/jsonapi-serializer/blob/master/test/serializer.js#L958

The test is testing a top level links object for a single resource. My understanding based on the JSON API 1.0 specification is that the links.self attribute should point back to the single resource rather than the collection of resources.

Here is the example given on the json api website:

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json

{
  "links": {
    "self": "http://example.com/articles/1" // <-- Notice the link is to the specific resource
  },
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!"
    },
    "relationships": {
      "author": {
        "links": {
          "related": "http://example.com/articles/1/author"
        }
      }
    }
  }
}

In your test however, you are creating a self link that points to the resource collection rather than the resource object. Here is the test right now:

  describe('Top level links with a single resource', function () {
    it('should be set', function (done) {
      var dataSet = {
        id: '54735750e16638ba1eee59cb',
        firstName: 'Sandro',
        lastName: 'Munda',
      };

      var json = new JsonApiSerializer('users', dataSet, {
        topLevelLinks: {
          self: 'http://localhost:3000/api/users'
        },
        attributes: ['firstName', 'lastName'],
      });

      expect(json).to.have.property('links').eql({
        self: 'http://localhost:3000/api/users'
      });

      done(null, json);
    });
  });

To be valid JSON-API shouldn't the test condition be:

      expect(json).to.have.property('links').eql({
        self: 'http://localhost:3000/api/users/54735750e16638ba1eee59cb'
      });

While the test as written passes it appears to be validating an invalid json api document. I think it would be better if the tests created and tested valid JSON-API payloads to help provide a second source of documentation for users of this library.

Apologies If I'm misunderstanding the JSON API specification which is definitely possible!

Handling `null` values as a 404 error

Hello,

I wanted to get your thoughts on having the jsonapi-serializer library handle 404 resource not found errors for you. I know the error object specified by the JSON API is pretty open ended so it might not make sense to try and include it in this library.

I'm imagining something where if you pass null as the value to the JSONAPISerializer constructor it would return back a properly formatted JSON API 404 error

{
        "errors": [{
            "status": "404",
            "code": "Not Found",
            "title": "'User Not Found",
            "detail": "The resource 'user' could not be found",
            "source":{
                "pointer": "/data"
            }
        }]
}

This is of course fairly easy to handle directly outside of JSONAPISerializer but it might be nice to provide the basic error handling for when a resource doesn't exist. The other 404 like condition is when a relationship doesn't exist for a resource that already exists. In this case you have to return an empty array for the data versus a 404 so there needs to be a way to differentiate these two cases.

Other types of errors such as Bad Request would probably have to be handled outside of this library.

JSON API Documenation related to this:
http://jsonapi.org/format/#fetching-resources-responses-200
http://jsonapi.org/format/#fetching-resources-responses-404
http://jsonapi.org/format/#errors

Why promises?

It looks like this library is performing only synchronous operations. It could just return payload, but instead it does:

return new P(function (resolve) {
  resolve(payload);
});

Why introduce the asynchronicity?

how should relationship data be sent in?

I've outputted below the options object, user object (with relationships data) and the JSONAPI response. Everything works however relationships are not being added to the response object. I'm assuming I'm sending in the relationships data wrong? How Am I suppose to attach relationships data to the user object? Thank You

***************** OPTIONS *********************
{ attributes: 
   [ 'firstName',
     'lastName',
     'email',
     'password',
     'active',
     'createdAt',
     'updatedAt' ],
  businesses: 
   { attributes: 
      [ 'legalBusinessName',
        'name',
        'timezone',
        'tin',
        'phoneNumber',
        'website' ],
     address: { attributes: [Object] },
     primaryContact: { attributes: [Object] },
     bankInformation: { attributes: [Object] },
     ref: '_id' },
  businessAccounts: 
   { attributes: [ 'ssn', 'observedHolidays', 'biography', 'totalPaidToDate' ],
     ref: '_id' },
  pluralizeType: false }
***************** USER ************************
{ id: 2,
  firstName: 'John',
  lastName: 'Doe',
  email: '[email protected]',
  phoneNumbers: null,
  secondaryContactInfo: null,
  address: null,
  active: true,
  archived: false,
  createdAt: Wed Sep 09 2015 19:28:11 GMT+0000 (UTC),
  updatedAt: Wed Sep 09 2015 19:28:11 GMT+0000 (UTC),
  businessAccounts: 
   [ { id: 2,
       userId: 2,
       businessId: 2,
       ssn: null,
       rateOfPay: null,
       availability: null,
       observedHolidays: null,
       biography: null,
       totalPaidToDate: null,
       phoneNumbers: null,
       descriptions: null,
       modesOfTransport: null,
       address: null,
       kind: null,
       status: null,
       active: false,
       archived: false,
       createdAt: Wed Sep 09 2015 19:28:11 GMT+0000 (UTC),
       updatedAt: Wed Sep 09 2015 19:28:11 GMT+0000 (UTC) } ],
  businesses: 
   [ { id: 2,
       name: 'DogsRus',
       hasSetup: false,
       legalBusinessName: null,
       tin: null,
       address: null,
       email: null,
       phoneNumber: null,
       website: null,
       timezone: null,
       primaryContact: null,
       bankInformation: null,
       observedHolidays: null,
       availability: null,
       active: false,
       archived: false,
       createdAt: Wed Sep 09 2015 19:28:11 GMT+0000 (UTC),
       updatedAt: Wed Sep 09 2015 19:28:11 GMT+0000 (UTC),
       _pivot_id: 2,
       _pivot_user_id: 2,
       _pivot_business_id: 2 } ] }
***************** RESPONSE *********************
{ data: 
   { type: 'user',
     id: '2',
     attributes: 
      { 'first-name': 'John',
        'last-name': 'Doe',
        email: '[email protected]',
        active: true,
        'created-at': Wed Sep 09 2015 19:28:11 GMT+0000 (UTC),
        'updated-at': Wed Sep 09 2015 19:28:11 GMT+0000 (UTC) } } }

Duplicate includes

Hi,
I found out that, when serializing an array of objects related to one same resource (e.g. many Articles written by the same Author), that resource is duplicated many times in the included array.

I'll try to solve this and submit a Pull Request when I get a moment.

One-to Included relationships Incorrect when the relationship isn't defined

When including a one-to relationship in a compound document and that relationship isn't defined, according to the spec, it should return a null data attribute. Currently, if the relationship exists, but isn't defined, I get an incorrect response. For example, say there is a staff relationship on an appointment model. If the appointment has an undefined staff_id, I get the following:

      "relationships": {
        "staff": {
          "data": {
           "type": "staff",
           "id": "undefined"
         }}}

Unless I'm reading the spec wrong, I believe it should be:

      "relationships": {
        "staff": {
          "data": null
        }}

I've already whipped up a PR, but wanted to run this by you first. I believe it's different than #34, which is why I opened a new issue.

Thanks, as always, for the the great work!

James

Nested Attributes are not Dasherized

Data Set (data)

    [ { books: 
         [ { id: 1,
             title: 'A Game of Thrones',
             createdAt: '2015-08-04T06:09:24.864Z',
             updatedAt: '2015-08-04T06:09:24.909Z',
             author: 1 } ],
        id: 1,
        name: 'George R. R. Martin',
        createdAt: '2015-08-04T06:09:24.857Z',
        updatedAt: '2015-08-04T06:09:24.896Z' } ]

Serialization

let opts = { 
    attributes: [ 'name', 'books', 'createdAt', 'updatedAt' ],
    books: { 
          attributes: [ 'title', 'createdAt', 'updatedAt' ] 
    } 
};

let jsonApiRes = new JSONAPISerializer('author', data, opts);

Result

    { data: 
       { type: 'authors',
         id: '1',
         attributes: 
          { books: 
             [ { id: 1,
                 title: 'A Game of Thrones',
                 createdAt: '2015-08-04T06:16:33.255Z',
                 updatedAt: '2015-08-04T06:16:33.295Z',
                 author: 1 } ],
            name: 'George R. R. Martin',
            'created-at': '2015-08-04T06:16:33.254Z',
            'updated-at': '2015-08-04T06:16:33.285Z' } } }

Notice that created-at is dasherized in the authors attributes, but not in the book attributes. Is that intentional?

Allow dynamic meta content

@SeyZ - first off, thumbs up for a great framework-agnostic, lightweight and easy-to-use library 👍 Just what I've been looking for!

The only thing I find lacking at the moment is the ability to add dynamic meta data at the point of serialisation. At the moment, you can only add meta when creating the serialiser instance, and seems a bit unintuitive to recreate the serialiser every time I need to change the meta.

In my case, I'm looking to add some extra information regarding pagination, such as total count. In keeping with the spec, this type of additional information should live in the top-level meta. An example of this can be seen in the extensions section here, under Profiles: link

The easiest way I can think of is to add an optional opts param to the serialise method. You can then specify a meta value in opts, and on the plus side, this also serves as a nice container if you want to allow any other options for the serialise method in the future. We can then merge it with any existing meta from the serialiser instance like so:

module.exports = function (collectionName, records, opts) {
  this.serialize = function (records, opts) {
    var that = this;
    var payload = {};

    ...

    if (that.opts.meta) {
      payload.meta = that.opts.meta;
    }

    if (opts.meta) {
      if (payload.meta) {
        for (var i in opts.meta) {
          payload.meta[i] = opts.meta[i];
        }
      } else {
        payload.meta = opts.meta;
      }
    }

    ...

Just gave it a run-through, and seems to work well. If anybody seconds the approach, I'm happy to do a PR.

Using jsonapi-serializer in the browser

Has anyone tried using this in the browser? I'm curious if it can be combined with ember-cli-mirage to mock server endpoints for ember testing.

Would be super useful to be able to use it in that capacity. I'm going to play around with it a bit to see if I can get this working but wanted to check and see if this is already a use case for anyone.

Thanks!

Differing collection name from type

If I have:

// GET /users/1
{
  data: {
    type: 'users',
    id: 1,
    attributes: {
      name: 'Charles',
      ...
    },
    relationships: {
      friends: {
        data: [
          { id: 2, type: 'users' },
          { id: 3, type: 'users' }
        ]
      }
    }
  }
}

In my dataset friends is actually type users. It doesn't seem possible to tell jsonapi-serializer this. Is this correct?

A use case I see is if I am using the model user for 2 relationships. 1 called contractors and one called clients

Great library, thanks! Saving me a tonne of time/effort!

UPDATE Improved the json example above for clarity

Compound document attributes should be part of top-level included member.

If I have the following config:

JSONAPISerializerConfig = {
    resourceType: resourceType,
    topLevelLinks: {
        self: apiEndpoint
    },
    links: {
        self: function(collection) {
            return apiEndpoint + collection.id;
        }
    },
    attributes: ['title', 'slug', 'subcategories'],
    subcategories: {
        ref: 'id',
        attributes: ['id', 'title', 'description']
    }
}

My data object looks like:

{
    id: 203,
    title: 'Hockey practice',
    slug: 'hockey-practice',
    subcategories: {
        description: 'Practices for strength, balance, flexibility, and vitality.',
        id: 114,
        title: 'Vitality'
    },
}

I get the following output:

{
    type: "practices",
    id: "203",
    attributes: {
       title: 'Hockey practice',
    slug: 'hockey-practice',
        subcategories: {
            type: "categories",
            id: 114,
            attributes: {
               description: 'Practices for strength, balance, flexibility, and vitality.',
               id: 114,
               title: 'Vitality'
            }
        }
    }
}

However, my subcategories.attributes should exist as part of the top-level included member. So, it should look like the following:

data: {
    type: "practices",
    id: "203",
    attributes: {
       title: 'Hockey practice',
    slug: 'hockey-practice',
        subcategories: {
            type: "categories",
            id: 114
        }
    }
}
included: {
  {
    "type": "categories",
    "id": "114",
    "attributes": {
      description: 'Practices for strength, balance, flexibility, and vitality.',
      id: 114,
      title: 'Vitality'
    }
  }
}

Bookshelf?

@SeyZ I noticed in one of your other posts that you're using Ember on the front-end. By any chance are you using Bookshelf as your ORM on the backend?

No documentation for breaking changes between 2.0.x and 2.1.0

The API for this module has changed entirely in a non-backwards compatible way in 2.1.0. I'm sure I'm not the only greeted by JSONAPISerializer is not a function this morning.

It would be beneficial to other developers using this module to know, from the main README.md, that the API changed in 2.1.0 and will break any code prior to 2.1.0.

Usage with Sequelize

Trying to use this serializer with the ORM Sequelize. Is this possible?
As of now I am receiving a the JSON API formatted json but with empty attributes.

routes/employee.js

router.get('/', function (req, res) {
  models.Employee.findAll().then(function(employees) {
    var json = new EmployeeSerializer(employees).serialize();
    res.send(employees);
      });
    });

serializers/employee.js

function EmployeeSerializer(employee) {

  this.serialize = function () {
    return new JSONAPISerializer('employees', employee, {
      topLevelLinks: { self: 'http://localhost:3000/employees' },
      dataLinks: {
        self: function (employee) {
          return 'http://localhost:3000/employees/' + employee.id
        }
 },
      attributes: ['firstName', 'lastName'],
    });
  };
}

module.exports = EmployeeSerializer;

But the output to the above is

{
  "links": {
    "self": "http://localhost:3000/employees"
  },
  "data": [
    {
      "type": "employees",
      "id": "1",
      "attributes": {},
      "links": {
        "self": "http://localhost:3000/employees/1"
      }
    }
  ]
}

Any idea as to why this happens? Have I made an error?

Nested array data not serialized

Hi.

I'm trying to serialize a nested array data but I got an array with undefined.

import Serializer from 'jsonapi-serializer';

new Serializer('sample', {
  id: 'test',
  list: [ 
    [ 'john', 'doe' ],
    [ 'jane', 'doe' ]
  ]
}, {
  attributes: ['list']
});

Above resulted in this

{
  data: {
    id: 'test',
    type: 'sample',
    attributes: {
      list: [ undefined ]
    }
  }  
}

I may be missing something here or could it be because of the jsonapi specification?
Any help will be appreciated!

Allow meta option to be a function

I have dynamic meta data per item instance and right now it appears I am only able to accomplish this by creating a new schema every time?

Adding a per item relationship meta function

We have a use case as described http://discuss.jsonapi.org/t/metadata-about-relationships/86 and http://discuss.jsonapi.org/t/pivot-data-for-many-to-many-relations/113 where the relationship has specific data about it we would like to return. The current version (3.0.1) allows us to meet this need via the relationshipMeta and using a function to pull the desired data out of the original objects results in the following payload.

{
  "type": "objective",
  "id": "00559422-02E7-11DB-8468-BA18C3C820AE",
  "relationships": {
    "concepts": {
      "data": [
        { "type": "concept", "id": "02B7" },
        { "type": "concept", "id": "3AF6" },
        { "type": "concept", "id": "CB23" }
      ],
      "meta": {
        "strengths": [ 
          {
            "id": "02B7",
            "strength": "primary"
          },
          {
            "id": "3AF6",
            "strength": "secondary"
          }
          {
            "id": "CB23",
            "strength": "tertiary"
          }
        ]
      }
    }
  }
}

We would prefer to colocate this meta data on each object in the relationship as seen below

{
  "type": "objective",
  "id": "00559422-02E7-11DB-8468-BA18C3C820AE",
  "relationships": {
    "concepts": {
      "data": [
        { "type": "concept", "id": "02B7", "meta": { "strength": "primary" } },
        { "type": "concept", "id": "3AF6", "meta": { "strength": "secondary" } },
        { "type": "concept", "id": "CB23", "meta": { "strength": "tertiary" } }
      ]
    }
  }
}

The linked discussion at the top indicate that both are allowed through the spec. Is this something you are open to adding to the library. Something along the lines of metaRef or relationshipItemMeta could be added to the same named relationship object?

Relationships without data

Hi,

I'm serializing a mongoose model, which works great. Im after adding a relationship, that is not in the original model.

The relationship returned should be without resource linkage (data) and provide only links(as is allowed by the specification), so that the end result would look like this:

{
    "type": "users",
    "id": "1",
    "attributes": {
        "name": "John"
    },
    "relationships": {
        "notes": {
            "links": {
                "related": "http://localhost/users/1/notes"
            }
        }
    }
}

However, as I understand you currently cannot declare a relationship links without attribute being present in the model?

2.1 breaks semver

2.1 is a breaking change and should be published to npm as 3.0

please un publish this, and republish the package as 3.0 and 3.0.1 to make this right.

npm unpublish

How to serialize a relationship request (no attributes)

I'm trying to create the response to a relationship URL such as

/articles/1/relationships/tags

According to the JSON API 1.0 spec (http://jsonapi.org/format/#fetching-relationships) the response should be:

{
  "links": {
    "self": "/articles/1/relationships/tags",
    "related": "/articles/1/tags"
  },
  "data": [
    { "type": "tags", "id": "2" },
    { "type": "tags", "id": "3" }
  ]
}

My data is in the format:

[
  {
    "id": 2
  },
  {
    "id": 3
  }
]

How can I serialize this using jsonapi-serializer?

I tried something like this:

                var json = new JSONAPISerializer('tags', result.rows, {
                    ref: true,
                    included: false,
                    topLevelLinks: {
                        self: "/articles/1/relationships/tags",
                        related: "/articles/1/tags"
                    },
                    attributes: []
                });

but that results in an empty attributes object being added to the data

{
  "links": {
    "self": "/articles/1/relationships/tags",
    "related": "/articles/1/tags"
  },
  "data": [
    { "type": "tags", "id": "2", "attributes":{} },
    { "type": "tags", "id": "3", "attributes":{} }
  ]
}

If I don't specify attributes as an empty array then the serializer crashes with an indexOf error.

TypeError: Cannot read property 'indexOf' of undefined
        at isAttrAllowed (/workspace/server/node_modules/jsonapi-serializer/lib/serializer-utils.js:89:22)
        at /workspace/server/node_modules/jsonapi-serializer/lib/serializer-utils.js:204:12

bad links returned in include

Hi can You look at example below
it returns wrong links in include section

var dataSet = {
        id: 203,
        title: 'Hockey practice',
        slug: 'hockey-practice',
        subcategories: {
            description: 'Practices for strength, balance, flexibility, and vitality.',
            id: 114,
            title: 'Vitality'
        }
    };
    var p = new JSONAPISerializer('pratices', dataSet, {
        topLevelLinks: {
            self: 'http://example.com'
        },
        dataLinks: {
            self: function (collection) {
                return 'http://example.com/' + collection.id;
            }
        },
        attributes: ['title', 'slug', 'subcategories'],
        subcategories: {
            ref: 'id',
            attributes: ['id', 'title', 'description'],
            includedLinks: {
                self: function (subcategory) {
                    return 'http://localhost:3000/subcategories/' + subcategory.id;
                }
            },

        }
    })

after run i gotlinks: { self: 'http://localhost:3000/subcategories/203' } as below. I'm pretty sure that it should should be links: { self: 'http://localhost:3000/subcategories/114 }

{ links: { self: 'http://example.com' },
  included: 
   [ { type: 'subcategories',
       id: '114',
       attributes: 
        { id: 114,
          title: 'Vitality',
          description: 'Practices for strength, balance, flexibility, and vitality.' },
       links: { self: 'http://localhost:3000/subcategories/203' } } ],
  data: 
   { type: 'pratices',
     id: '203',
     attributes: { title: 'Hockey practice', slug: 'hockey-practice' },
     links: { self: 'http://example.com/203' },
     relationships: { subcategories: { data: { type: 'subcategories', id: '114' } } } } }

output comes from version 2.0.0-beta.3
unfortunatelly last working wersion seems 1.0.2 it gives correct output like below:

{ _bitField: 268435456,
  _fulfillmentHandler0: undefined,
  _rejectionHandler0: undefined,
  _progressHandler0: undefined,
  _promise0: undefined,
  _receiver0: undefined,
  _settledValue: 
   { data: 
      { type: 'pratices',
        id: '203',
        attributes: { title: 'Hockey practice', slug: 'hockey-practice' },
        links: { self: 'http://example.com/203' },
        relationships: { subcategories: { data: { type: 'subcategories', id: '114' } } } },
     included: 
      [ { type: 'subcategories',
          id: '114',
          attributes: 
           { id: 114,
             title: 'Vitality',
             description: 'Practices for strength, balance, flexibility, and vitality.' },
          links: { self: 'http://localhost:3000/subcategories/114' } } ] } }

Promises in Deserializer

I couldn't find an existing discussion of the design, but I was wondering the motivations behind using Promises in the Deserialization API. It looks like it was used for serialization as well, but removed in #17 - why the reversal?

Thanks!

Ember expects singularized types for included data in relationships

... and thus can't properly parse the results.

Given the following example

"relationships": {
      "foobars": {
        "data": [
          {
            "type": "foobars",
            "id": "1"
          },
          {
            "type": "foobars",
            "id": "2"
          },
          {
            "type": "foobars",
            "id": "3"
          }
        ]
      }
    }
  },
"included": [
    {
      "type": "foobars",
      "id": "1",
      "attributes": {
        "foo": "barbor",
        "delete-id": 0
      }
    },
    {
      "type": "foobars",
      "id": "2",
      "attributes": {
        "foo": "barbar",
        "bar": 1
      }
    },
    {
      "type": "foobars",
      "id": "3",
      "attributes": {
        "foo": "barber",
        "bar": 2
      }
    }
  ]
}

This should be instead:

"relationships": {
      "foobars": {
        "data": [
          {
            "type": "foobars",
            "id": "1"
          },
          {
            "type": "foobars",
            "id": "2"
          },
          {
            "type": "foobars",
            "id": "3"
          }
        ]
      }
    }
  },
"included": [
    {
      "type": "foobar",
      "id": "1",
      "attributes": {
        "foo": "barbor",
        "delete-id": 0
      }
    },
    {
      "type": "foobar",
      "id": "2",
      "attributes": {
        "foo": "barbar",
        "bar": 1
      }
    },
    {
      "type": "foobar",
      "id": "3",
      "attributes": {
        "foo": "barber",
        "bar": 2
      }
    }
  ]
}

The Problem

Actually it is only possible to specify the typeForAttribute option, and thus it is not possible to specify them for nested or compound attributes. Maybe we also need a singularize optionality?

Note: This seems to be intended, but yet undocumented emberjs/data#3687

Compound Document Included Resource Self Links Incorrect?

I'm still trying to learn json-api, but the example in the README does not seem to correspond to the spec at: http://jsonapi.org/format/#document-compound-documents

The issue is the self links in the included resources. The readme shows these as links to a relationship:

"included": [{
  .
  .
  .
  "links": {
    self: 'http://example.com/relationships/author',
    related: 'http://example.com/books/1/author'
  }
}

whereas the spec gives a link to the resource itself:

"included": [{
  .
  .
  .
  "links": {
    "self": "http://example.com/people/9"
  }
}

Is this an error in the README or am I misunderstanding?

Top level ID access for compound document links

Here is my desired output:

    type: "posts",
    id: "236",
    attributes: {
        title: "A post to boost your energy",
    },
    relationships: {
        subcategory: {
            data: {
                type: "subcategories",
                id: "114"
            },
            links: {
                // I want to get the ID of the current "post"... in this case it is 236
                self: "http://localhost:3000/posts/236/relationships/subcategory"
            }
        },
    }

In my jsonapi-serializer config, I have the following:

    attributes: ['title'],
    subcategory: {
        ref: 'id',
        attributes: ['id'],
        relationshipLinks: {
            self: function(topLevelResourceID) {
                // For the above sample out, I want to be able to get id 236 here
                return apiEndpoint + topLevelResourceID.id + '/relationships/classes';
            }
        }
    }

Is this possible?

README should have some documentation on the new Deserializer

The title makes it pretty self-explanatory, I think. I'm excited about this feature because I've reached a stage in my project where I need to consume information from the outside, and I really like not having to add another library for deserialization.

Unfortunately, I had to do some digging to find out that this library can do that and I have to do some experimenting to figure out how!

I might be able to submit a PR for this, but if someone else gets to it first, please feel free!

jsonapi-serializer freezes the browser when nested include is used

jsonapi-serializer 3.0.1

The resources I include: calendar_event, space,host.profile

new Deserializer({keyForAttribute: 'snake_case'}).deserialize(input, (err, resource) => {
          console.log(resource);
          onSuccess(response, resource);
        });

Here is the input:

{
  "data": {
    "id": "524",
    "type": "events",
    "attributes": {
      "name": "Omnis labore laboriosam magni qui.",
      "status": "pending",
      "cost_cents": 7577,
      "description": "perferendis molestiae voluptatem",
      "is_public": true,
      "bartender": false
    },
    "relationships": {
      "calendar_event": {
        "data": {
          "id": "424",
          "type": "calendar_events"
        }
      },
      "host": {
        "data": {
          "id": "1",
          "type": "users"
        }
      },
      "space": {
        "data": {
          "id": "1",
          "type": "spaces"
        }
      }
    }
  },
  "included": [
    {
      "id": "424",
      "type": "calendar_events",
      "attributes": {
        "title": "Booking",
        "availability": "busy",
        "starts_at": "2016-03-25T06:30:00.000Z",
        "ends_at": "2016-03-25T13:30:00.000Z",
        "ical_schedule": null
      },
      "relationships": {
        "resource": {
          "data": {
            "id": "524",
            "type": "events"
          }
        }
      }
    },
    {
      "id": "1",
      "type": "users",
      "attributes": {
        "email": "[email protected]",
        "roles": []
      },
      "relationships": {
        "profile": {
          "data": {
            "id": "1",
            "type": "profiles"
          }
        }
      }
    },
    {
      "id": "1",
      "type": "profiles",
      "attributes": {
        "first_name": "Anton",
        "last_name": "Anton",
        "birthday": "1905-12-10",
        "avatar_original_url": "aha!",
        "avatar_thumb_url": "aha!",
        "bio": null,
        "phone": "aha!",
        "address_line_1": "aha!",
        "address_line_2": null,
        "city": "aha!",
        "state": "aha!",
        "postcode": "aha!",
        "country_code": null
      },
      "relationships": {
        "user": {
          "data": {
            "id": "1",
            "type": "users"
          }
        }
      }
    },
    {
      "id": "1",
      "type": "spaces",
      "attributes": {
        "name": "Space #1",
        "from_people": 1,
        "to_people": 10,
        "picture_original_url": "aha!",
        "picture_thumb_url": "aha!",
        "picture_thumb_2x_url": "aha!",
        "credits_per_slot": 10
      }
    }
  ]
}

JSON objects in the `attributes`

One of my attributes is a JSON object that I don't wan't to define as a relationship. I also don't know the structure/attributes of this object. How can I define it to embed it as-is in the attributes section? As I understand the spec this is actually allowed construct.

So basically what I'm expecting is something like this:

{
  "links": {
    "self": "http://example.com/articles/1"
  },
  "data": [
    {
      "type": "articles",
      "id": "1",
      "attributes": {
        "id": 1,
        "object-attribute": {"unknown-prop": "value"}
      }
    }
  ]
}

Automatic char conversion

Hi,

I've got one problem with serialization. Now, if attribute (options) contains '_' ie. underscore character, it will be converted to '-' ie. minus character (inside data). Is this correct behavior, because I'd really like to leave attributes untouched? Anyway ,if you look here:

http://jsonapi.org/format/#document-member-names

it looks like underscore is allowed character.

Thank you very much

Deserializer without included member

I think the deserializer always try to find an included member on the root object. I read the specifications again and we can send a reference directly in the relationships member such as the example below.

Dataset (POST request)

{
  "data": {
    "type": "article",
    "attributes": {
      "title": "My first title",
    },
    "relationships": {
      "contributors": {
        "data": [{"type":"user", "id": "1"}]
      }
    }
  }
}

Returned

{
  title: 'My first title',
  id: undefined,
  contributors: [ null ] 
}

Excepted

{
  title: 'My first title',
  contributors: [ '1' ] 
}

Make top-level member included optional

According to the JSON API spec http://jsonapi.org/format/#document-top-level:

A document MAY contain any of these top-level members:

  • jsonapi: an object describing the server's implementation
  • links: a links object related to the primary data.
  • included: an array of resource objects that are related to the primary data and/or each other ("included resources").

In my specific use case, I do not want to have an included top-level member.

A suggestion for passing in what I want to have excluded

JSONAPISerializerConfig = {
    attributes: ['id', 'title'],
    exclude: ['included']
}

Then when building the serialized object, see if the property exists in the array of excluded items.

Thoughts?

Attribute with false value is not being added to attributes

Seems like false values are being ignored in latest version.

new Serializer('test', { foo: false }, { attributes: ['foo'] });

// output
{ 
  data: { 
    id: undefined,
    type: 'test',
    // missing attributes
  }
}

Same for any falsy values such as 0.

The npm version which is same 2.0.4 (Released 2015-10-16T12:25:03.617Z) works fine with above examples.

RFC: JSONAPIDeSerializer

Hi, I'm running an express backend application and I'm using this lib, but it doesn't Deserialize JSONAPI request payloads to construct functional Objects, so in order to do that, I'm making this RFC so we can have an starter point. thanks :)

'use strict';
var _ = require('lodash');
var inflection = require('inflection');
var SerializerUtils = require('./serializer-utils');

// {
//   "data": {
//     "attributes": {
//       "order-type": "subscription",
//       "production-state": "0",
//       "payment-state": "0",
//       "shipment-state": "0",
//       "created-at": null,
//       "ship-date": null,
//       "season": "FA15"
//     },
//     "relationships": {
//       "plan": {
//         "data": {
//           "type": "plans",
//           "id": "1-shirt"
//         }
//       },
//       "coupon": {
//         "data": null
//       },
//       "address": {
//         "data": null
//       },
//       "customer": {
//         "data": null
//       }
//     },
//     "type": "orders"
//   }
// }

var DeSerializer = new SerializerUtils('', null, null, {
  keyForAttribute: 'CamelCase'
});

module.exports = function (collectionName, payload) {
  var attributes = payload.data.attributes;
  var relationships = payload.data.relationships;

  var relations = Object.keys(relationships);
  var collectionAttrs = {};
  Object.keys(attributes).forEach(function (key) {
    collectionAttrs[_.camelCase(key)] = _.clone(attributes[key], true);
  });
  var collection = new collectionName(collectionAttrs);
  // console.log('COLLECTION: ', attributes);

  relations.forEach(function (relation) {
    // (e.g 1)
    // "relationships": {
    //   "plan": {
    //     "data": {
    //       "type": "plans",
    //       "id": "1-shirt"
    //     }
    //   }
    // }
    // (e.g 2)
    // "relationships": {
    //   "coupon": {
    //     "data": null
    //   }
    // }
    var relationshipPayload = relationships[relation];
    var relationshipData = relationshipPayload.data;
    // relationType => Plan
    if (!relationshipData) {
      return;
    }
    if (_.isArray(relationshipData)) {
      // TODO:
      // Order.products = [Product.new(...), Product.new(...), Product.new(...)];
      collection[_.camelCase(relation)] = _.map(relationshipData, function (relationData) {
        return _.omit(relationData, 'type');
      });
    } else {
      // Plan.new({ id: '1-shirt' });
      var relationObject = _.omit(relationshipData, 'type');
      // Order.plan = Plan.new({ id: '1-shirt' });
      collection[_.camelCase(relation)] = relationObject;
    }
  });
  return collection;
};

// var newOrder = JSONAPIDeSerializer(Order, req.body);

Attribute names always dasherized?

Hi,
I have noticed that attribute names written in camelCase (such as nbItems) are dasherized (like nb-items). It sounds nice, but wouldn't it be even nicer to give the choice through a flag (like dasherize, which could be true by default)?

Links property for relationships

If I have code such as the following:

new JSONAPISerializer('users', data, {
  apiEndpoint: 'http://localhost:3000/api/users',
  attributes: ['firstName', 'lastName', 'books'],
  books: {
    ref: '_id',
    attributes: ['title', 'ISBN']
  }
}).then(function (users) {  
  // `users` here are JSON API compliant. 
});
{
  "links": {
    "self": "http://localhost:3000/api/users"
  },
  "data": [{
    "type": "users",
    "id": "1",
    "attributes": {
      "firstName": "Sandro",
      "lastName": "Munda"
    },
    "relationships": {
      "books": {
        // Is there add a links attribute such as this one for relationships?
        "links": {
          "self": "http://example.com/users/1/relationships/books",
          "related": "http://example.com/users/1/books"
        },
        "data": [
          { "type": "books", "id": "1" },
          { "type": "books", "id": "2" }
        ]
      }
    }
  }

Is there a way I can add the links attribute for compound documents?

relationships toOne null value

Hi ,
I found problem with lib in case when defined relationship data is with null value ex. address

var dataSet = [
 {
    id: '5490143e69e49d0c8f9fc6bc',
    firstName: 'Lawrence',
    lastName: 'Bennett',
    address: null
}];

var json = new JsonApiSerializer('users', dataSet, {
    attributes: ['firstName', 'lastName', 'address'],
    address: {
        ref: 'id',
        included:false,
        //ignoreRelationshipData: true,
        attributes: ['addressLine1', 'addressLine2', 'zipCode', 'country']
    }
});

console.log(JSON.stringify(json, null, 2));

the generated output is:

{
  "data": [
    {
      "type": "users",
      "id": "5490143e69e49d0c8f9fc6bc",
      "attributes": {
        "first-name": "Lawrence",
        "last-name": "Bennett",
        "address": null
      }
    }
  ]
}

It's not exactly correct because address should be in relationships sections, in worst scenario shouldn't be at all.

for my purposes I created a patch in serializer-utils.js probably it can be refactored to be better, but in dirty form it looks like this:

//...
 this.serialize = function (dest, current, attribute, opts) {
    var that = this;

    if (isComplexType(current[attribute]) || (opts && opts.ref)) { // additional check if ref exist
      if (opts && opts.ref) {
//...     

now output is generated correctly:

{
  "data": [
    {
      "type": "users",
      "id": "5490143e69e49d0c8f9fc6bc",
      "attributes": {
        "first-name": "Lawrence",
        "last-name": "Bennett"
      },
      "relationships": {
        "address": {
          "data": null
        }
      }
    }
  ]
}

BTW with this fix all tests passed, and work pretty well , of course now I can use ignoreRelationshipData param to get links only without data.

Can You take a look and fix it in proposed or maybe better way?

Creating Nested Resources

This could be more of a question, however I've dug through all of the issues and the source code and I can't find the answer.

I've got a simple person object that is returned from a model:

{ 
  id: 1,
  firstName: 'John',
  lastName: 'Smith',
  homeTown: 'Smallville',
  homeState: 'KS'
}

I would like to homeTown and homeState to be nested in the return object like this:

{
  "links": {
    "self": "..."
  },
  "meta": {
    "updated": "2016-01-25T00:00:00.000Z"
  },
  "data": {
    "type": "person",
    "id": "1",
    "attributes": {
      "FirstName": John",
      "LastName": "Smith",
      "Home": {
        "HomeTown": "Smallville",
        "HomeState": "KS"
      }
    }
  }
}

So reading through the documentation and based on search around I expected this would return that result:

const response = new API('players', model, {
  keyForAttribute: 'CamelCase',
  topLevelLinks: {
    self: req.protocol + '://' + req.hostname + req.path
  },
  attributes: ['firstName', 'lastName', 'home'],
  home: {
    attributes: ['homeTown', 'homeState']
  }
});

res.json(response);

But that doesn't return the home object and only the firstName and lastName fields.

Doesn't handle being passed mongoose models well.

I've encountered an issue where I'm trying to serialize a mongodb document and using mongoose as my ODM. I need my server to output the information using JSON API.

When testing the the serializer's output with some of my mongoose documents as input, I get an empty attribute object. Using raw JSON gives the right output.

I know this seems like it has to do with how mongoose represents document objects internally, but I found the solution in how this module attempts to access attributes for the passed record.

I'll be submitting a pull request that should fix this soon!

Error handling

Related to #13, I'm going to fork this library to add in proper error object support in order to conform with JSON API's standards on throwing errors.

Anything in particular I should be aware of/attempt to implement while I'm at it?

I'm not planning on parsing error codes and such within jsonapi-serializer - just properly handling a supplied error object. My biggest question is whether I should add an err (like JSONAPISerializer(type, data, opts, err)) or magically parse a supplied error type, or include it in opts ala meta, or something else? (Could also add a JSONAPISerializer.error(type, data, opts)).

I know part of the goal with jsonapi-serializer is to be as agnostic as possible and I plan on submitting a PR when I'm done so any thoughts on best practices as I work on this would be appreciated. :)

Serialize To One relationship

Hi, I'm having the following issue
I have this serializer

var JSONAPISerializer = require('jsonapi-serializer');
var _ = require('lodash');

module.exports = function CustomerSerializer(data) {
  this.serialize = () => {
    return new JSONAPISerializer('customers', data, {
      topLevelLinks: {
        self: (body) => '/customers' + (_.isArray(body) ? '' : '/' + body.id)
      },
      dataLinks: {
        self: (customer) => '/customers/' + customer.id
      },
      attributes: [
        ...,
        'orders',
        'plan'
      ],
      plan: {
        ref: 'id',
        included: false,
        relationshipLinks: {
          self: (customer) => '/customers/' + customer.id + '/relationships/plan'
        },
        attributes: []
      },
      orders: {
        ref: (_, item) => item,
        included: false,
        relationshipLinks: {
          self: (customer) => '/customers/' + customer.id + '/relationships/orders'
        },
        attributes: []
      },
    });
  };
};

but the data's plan attribute I'm passing to serializer is an already plain string e.g

{ "plan": "UUID-STRING" }

but in the response, plan is kept in attributes hash, instead of in relationships, I think the issue is here https://github.com/SeyZ/jsonapi-serializer/blob/master/lib/serializer-utils.js#L122 because you are making an assumption that relationships comes in Objects or Arrays, for example if you take a look in orders hash I'm doing ref: (_, item) => item because orders attribute is an Array of Strings

Specify different types for compound document relationships

For the following example, I would like to give my author relationship the type of people

// ...
{
  "type": "articles",
  "id": "1",
  "attributes": {
    "title": "Rails is Omakase"
  },
  "relationships": {
    "author": {
      "links": {
        "self": "http://example.com/articles/1/relationships/author",
        "related": "http://example.com/articles/1/author"
      },
      "data": { "type": "people", "id": "9" }
    }
  },
  "links": {
    "self": "http://example.com/articles/1"
  }
}
// ...

dashes are removed in strings

Hello currently I have a deep array that is stored as Business.availability.hoursOfOperation.exception. exception is an array. In postgres it is stoerd as ["2015-09-21", "2015-09-23", "2015-09-25"] although passing Business into the serializer it comes out as ["20150914", "20150922", "20150924"]. It appears the dashes are gone. Is the attribute possibly too deeply nested? Thanks for your help!

Serializers

The current JSONAPISerializer is more of a JSONAPISerialization, becuase it serializes a single payload.
I expected to be able to specify a single schema, then re-use the serializer on various payloads.
I'm using it with ember-cli http-mocks.
I think the JSONAPISerializer constructor should take the type name and schema, and return an object with a serialize method, similar to what I'm doing below:

var JSONAPISerializer = require('jsonapi-serializer');
var participants = require('../data/participants');

module.exports = function(app) {
  ...
  participantsRouter.get('/', function(req, res) {
    serialize(participants)
      .then(function(participants) {
        res.send(participants);
      });
  });

  participantsRouter.get('/:id', function(req, res) {
    idx = parseInt(req.params.id) - 1;

    serialize(participants[idx])
      .then(function(participants) {
        res.send(participants);
      });
  });
  ...
};


var schema = {
  topLevelLinks: { self: '/participants/' },
  ...
}

function serialize(data){
  if (Array.isArray(data)) {
    return new JSONAPISerializer('participants', data, schema);
  } else {
    return new JSONAPISerializer('participant', data, schema);
  }
}

Nested Resource Not Working

Hey!

I am trying to get nested serializers to work but it refuses to add the nested attributes.

Here is what im doing:

Fetching the status with its related datacenters:

Status.where('id', 1).fetch({
      withRelated: 'datacenters'
    }).then((status) => {
        res.json(Serializer.serialize(status));
    });

which sends this object

{
    id: 1,
    login: 'normal',
    community: 'delayed',
    matchmaking: 'normal',
    online_servers: 192897,
    online_players: 665583,
    searching: 7280,
    wait_time: 39,
    created_at: SunNov22201510: 38: 01GMT-0800(PST),
    updated_at: SunNov22201510: 38: 01GMT-0800(PST),
    datacenters: [
        {
            id: 1,
            name: 'EUWest',
            capacity: 'full',
            load: 'medium',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 2,
            name: 'EUEast',
            capacity: 'full',
            load: 'medium',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 3,
            name: 'USSouthwest',
            capacity: 'full',
            load: 'low',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 4,
            name: 'USSoutheast',
            capacity: 'full',
            load: 'idle',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 5,
            name: 'India',
            capacity: 'offline',
            load: 'full',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 6,
            name: 'EUNorth',
            capacity: 'full',
            load: 'medium',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 7,
            name: 'Emirates',
            capacity: 'full',
            load: 'idle',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 8,
            name: 'USNorthwest',
            capacity: 'full',
            load: 'idle',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 9,
            name: 'SouthAfrica',
            capacity: 'high',
            load: 'high',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 10,
            name: 'Brazil',
            capacity: 'full',
            load: 'high',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 11,
            name: 'USNortheast',
            capacity: 'full',
            load: 'low',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 12,
            name: 'Japan',
            capacity: 'full',
            load: 'idle',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 13,
            name: 'Singapore',
            capacity: 'full',
            load: 'low',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        },
        {
            id: 14,
            name: 'Australia',
            capacity: 'full',
            load: 'idle',
            steam_status_id: 1,
            created_at: SunNov22201510: 38: 01GMT-0800(PST),
            updated_at: SunNov22201510: 38: 01GMT-0800(PST)
        }
    ]
}

and then is serialized like so:

serialize: (data) => {
    const json = data.toJSON()
    const status = new JSONAPISerializer('status', json, {
      topLevelLinks: { self: `${ENV.host}/steam/status` },
      pluralizeType: false,
      attributes: ['id', 'login', 'community', 'matchmaking', 'online_servers', 'online_players', 'searching', 'wait_time', 'updated_at'],
      datacenters: {
        attributes: ['id', 'name', 'load']
      }
    });
    return status;
  }

That code is returning this json:

{
  "links": {
    "self": "http://localhost:3000/steam/status"
  },
  "data": {
    "type": "status",
    "id": "1",
    "attributes": {
      "id": 1,
      "login": "normal",
      "community": "delayed",
      "matchmaking": "normal",
      "online-servers": 192897,
      "online-players": 665583,
      "searching": 7280,
      "wait-time": 39,
      "updated-at": "2015-11-22T18:38:01.417Z"
    }
  }
}

As you can see the nested stuff isnt attached. Any help would be appreciated.

Thanks.

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.