Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

json-api-normalizer

yury-dymov102kMIT1.0.4

JSON API response normalizer

JSON, API, normalize, redux

readme

json-api-normalizer

Utility to normalize JSON API data for redux applications

npm version Downloads Build Status Coverage Status

Description

json-api-normalizer helps awesome JSON API and redux work together. Unlike normalizr json-api-normalizer supports JSON API specification, which means that you don't have to care about schemes. It also converts collections into maps, which is a lot more suitable for redux.

Demo - https://yury-dymov.github.io/json-api-react-redux-example/

Demo sources - https://github.com/yury-dymov/json-api-react-redux-example

Works great together with redux-object, which helps to fetch and denormalize data from the store.

json-api-normalizer was recently featured in SmashingMagazine: https://www.smashingmagazine.com/2017/05/json-api-normalizer-redux/

Install

$ npm install json-api-normalizer

Example

import normalize from 'json-api-normalizer';

const json = {
  data: [{
    "type": "post-block",
    "relationships": {
      "question": {
        "data": {
          "type": "question",
          "id": "295"
        }
      }
    },
    "id": "2620",
    "attributes": {
      "text": "I am great!",
      "id": 2620
    }
  }],
  included: [{
    "type": "question",
    "id": "295",
    "attributes": {
      "text": "How are you?",
      id: 295
    }
  }]
};

console.log(normalize(json));
/* Output:
{
  question: {
    "295": {
      id: 295,
      type: "question"
      attributes: {
        text: "How are you?"
      }
    }
  },
  postBlock: {
    "2620": {
      id: 2620,
      type: "postBlock",
      attributes: {
        text: "I am great!"
      },
      relationships: {
        question: {
          type: "question",
          id: "295"
        }
      }
    }
  }
}
*/

Options

Endpoint And Metadata

While using redux, it is supposed that cache is incrementally updated during the application lifecycle. However, you might face an issue if two different requests are working with the same data objects, and after normalization, it is not clear how to distinguish, which data objects are related to which request. json-api-normalizer can handle such situations by saving the API response structure as metadata, so you can easily get only data corresponding to the certain request.

console.log(normalize(json, { endpoint: '/post-block/2620' }));
/* Output:
{
  question: {
    ...
  },
  postBlock: {
    ...
  },
  meta: {
    "/post-block/2620": {
      data: [{
        type: "postBlock",
        id: 2620,
        relationships: {
          "question": {
            type: "question",
            id: "295"
          }
      }]
    }
  }
}
*/

Endpoint And Query Options

By default request query options are ignored as it is supposed that data is incrementally updated. You can override this behavior by setting filterEndpoint option value to false.

const d1 = normalize(json, { endpoint: '/post-block/2620?page[cursor]=0' });
const d2 = normalize(json, { endpoint: '/post-block/2620?page[cursor]=20' });
console.log(Object.assign({}, d1, d2));
/* Output:
{
  question: {
    ...
  },
  postBlock: {
    ...
  },
  meta: {
    "/post-block/2620": {
      ...
    }
  }
}
*/

const d1 = normalize(json, { endpoint: '/post-block/2620?page[cursor]=0', filterEndpoint: false });
const d2 = normalize(json, { endpoint: '/post-block/2620?page[cursor]=20', filterEndpoint: false });
console.log(someFunctionWhichMergesStuff({}, d1, d2));
/* Output:
{
  question: {
    ...
  },
  postBlock: {
    ...
  },
  meta: {
    "/post-block/2620: {
      "?page[cursor]=0": {
        ...
      },
      "?page[cursor]=20": {
        ...
      }
    }
  }
}
*/

If JSON API returns links section and you define the endpoint, then links are also stored in metadata.

const json = {
  data: [{
    ...
  }],
  included: [{
    ...
  }],
  links: {
    first: "http://example.com/api/v1/post-block/2620?page[cursor]=0",
    next: "http://example.com/api/v1/post-block/2620?page[cursor]=20"
  }
};

console.log(normalize(json, { endpoint: '/post-block/2620?page[cursor]=0'}));
/* Output:
{
  question: {
    ...
  },
  postBlock: {
    ...
  },
  meta: {
    "/post-block/2620": {
      data: [{
        ...
      }],
      links: {
        first: "http://example.com/api/v1/post-block/2620?page[cursor]=0",
        next: "http://example.com/api/v1/post-block/2620?page[cursor]=20"
      }
    }
  }
}
*/

Lazy Loading

If you want to lazy load nested objects, json-api-normalizer will store links for that

const json = {
  data: [{
    attributes: {
      ...
    },
    id: "29",
    relationships: {
      "movie": {
        "links": {
          "self": "http://...",
          "related": "http://..."
        }
      },
    },
    type: "question"
  }]
};

console.log(normalize(json));
/* Output:
{
  question: {
    "29": {
      attributes: {
        ...
      },
      relationships: {
        movie: {
          links: {
            "self": "http://...",
            "related": "http://..."
          }
        }
      }
    }
  }
}
*/

Camelize Keys

By default all object keys and type names are camelized, however, you can disable this with camelizeKeys option.

const json = {
  data: [{
    type: "post-block",
    id: "1",
    attributes: {
      "camel-me": 1,
      id: 1
    }
  }]
}

console.log(normalize(json));
/* Output:
{
  postBlock: {
    "1": {
      id: 1,
      type: "postBlock",
      attributes: {
        camelMe: 1
      }
    }
  }
}
*/

console.log(normalize(json, { camelizeKeys: false }));
/* Output:
{
  "post-block": {
    "1": {
      id: 1,
      type: "postBlock",
      attributes: {
        "camel-me": 1
      }
    }
  }
}
*/

Camelize Type Values

By default propagated type values are camelized but original value may be also preserved

const json = {
  data: [{
    type: "post-block",
    id: "1",
    attributes: {
      "camel-me": 1,
      id: 1
    }
  }]
}

console.log(normalize(json, { camelizeTypeValues: false }));
/* Output:
{
  postBlock: {
    "1": {
      id: 1,
      type: "post-block", // <-- this
      attributes: {
        camelMe: 1
      }
    }
  }
}
*/

Copyright

MIT (c) Yury Dymov

changelog

v 1.0.4 (21st Mar 2021)

  • Fixing core-js missing deps

v 1.0.3 (9th Mar 2021)

  • Fixing legacy browser support

v 1.0.1 (2nd Dec 2020)

  • Deps update

v 1.0.0 (08 Jul 2020)

v 0.4.16 (05 Aug 2019)

  • Updated vulnerable deps

v 0.4.15 (17 Jun 2019)

  • Updated vulnerable deps

v. 0.4.14 (16 Nov 2018)

  • updated deps

v. 0.4.12 (08 Oct 2018)

v. 0.4.11 (28 June 2018)

v. 0.4.10 (14 Feb 2018)

v. 0.4.9 (14 Feb 2018)

v. 0.4.8 (01 Feb 2018)

v. 0.4.7 (21 Dec 2017)

v. 0.4.6 (29 Nov 2017)

v. 0.4.5 (23 Oct 2017)

While processing nested objects, we should handle dates accordingly (https://github.com/yury-dymov/json-api-normalizer/issues/23)

v. 0.4.4 (23 Oct 2017)

While processing nested objects, we shouldn't change array attributes to object (https://github.com/yury-dymov/json-api-normalizer/issues/22)

v. 0.4.3 (20 Oct 2017)

Nested attribute keys are also camelized now (https://github.com/yury-dymov/json-api-normalizer/issues/21)

v. 0.4.2 (25 Sep 2017)

Added meta support per spec (https://github.com/yury-dymov/json-api-normalizer/issues/19)

v. 0.4.1 (11 Jun 2017)

Including self links in normalization (https://github.com/yury-dymov/json-api-normalizer/pull/16)

v. 0.4.0 (15 Mar 2017)

Relationshop normalization implementation changed discussion

v. 0.3.0 (09 Mar 2017)

IDs now preserved in entities. discussion

v. 0.2.4 (08 Mar 2017)

Store links for subqueries in meta #7

v. 0.2.3 (06 Mar 2017)

Fixed issue, when data is null for the meta #5

v. 0.2.1 (28 Feb 2017)

Fixed issue, when data is null #4

v. 0.2.0 (09 Feb 2017)

Format changed for filterEndpoint option equals false for metadata.

Was

{
  "meta": {
    "/test?page=1": {...},
    "/test?page=2": {...},
    "/anotherTest": {...}
  }
}

Now

{
  "meta": {
    "/test": {
      "?page=1": {...},
      "?page=2": {...}
    },
    "/anotherTest": {...}
  }
}

v. 0.1.1 (03 Feb 2017)

Added lazy loading support according to #2