How can I use normalizr to deal with nested standardised JSON API responses that are key via the { data: ... }
standard?
For example a Book
{
data: {
title: 'Lord of the Rings',
pages: 9250,
publisher: {
data: {
name: 'HarperCollins LLC',
address: 'Big building next to the river',
city: 'Amsterdam'
},
},
author: {
data: {
name: 'J.R.R Tolkien',
country: 'UK',
age: 124,
}
}
}
}
How would I design schemas to deal with the nested data key?
How can I use normalizr to deal with nested standardised JSON API responses that are key via the { data: ... }
standard?
For example a Book
{
data: {
title: 'Lord of the Rings',
pages: 9250,
publisher: {
data: {
name: 'HarperCollins LLC',
address: 'Big building next to the river',
city: 'Amsterdam'
},
},
author: {
data: {
name: 'J.R.R Tolkien',
country: 'UK',
age: 124,
}
}
}
}
How would I design schemas to deal with the nested data key?
Share Improve this question edited Jul 4, 2016 at 14:19 AndrewMcLagan asked Jul 1, 2016 at 13:53 AndrewMcLaganAndrewMcLagan 14k25 gold badges96 silver badges166 bronze badges 2-
The json which you post doesn't follow the
JSONAPI
specifications. Quickly, you can not nest other resources in the main one, but for including other resources in the payload you've to use the specific keyinclude
. In the main resource you can send only a limited set of information about the relationships between itself and other resources under the keyrelationships
. – NickGnd Commented Jul 4, 2016 at 15:55 - Your right, I will update the question – AndrewMcLagan Commented Jul 4, 2016 at 21:02
2 Answers
Reset to default 6For each entity in your response, you should create it's own schema.
In your example, we have three entities - books
, authors
and publishers
:
// schemas.js
import { Schema } from 'normalizr';
const bookSchema = new Schema('book');
const publisherSchema = new Schema('publisher');
const authorSchema = new Schema('author');
If some entity contains nested data which should be normalized, we need to use define
method of it schema.This method accepts an object with nesting rules.
If we need to normalize publisher
and author
props of book
entity, we should pass an object to define
function with same structure as our response:
// schemas.js
bookSchema.define({
data: {
publisher: publisherSchema,
author: authorSchema
}
});
Now we can normalize our response:
import { normalize } from 'normalizr';
import { bookSchema } from './schemas.js';
const response = {
data: {
title: 'Lord of the Rings',
pages: 9250,
publisher: {
data: {
name: 'HarperCollins LLC',
address: 'Big building next to the river',
city: 'Amsterdam'
},
},
author: {
data: {
name: 'J.R.R Tolkien',
country: 'UK',
age: 124,
}
}
}
}
const data = normalize(response, bookSchema);
I believe what you're after is the use of the assignEntity
function which can be passed in the options of normalize
. In this instance it lets us, where appropriate, filter out the redundant data
properties and go straight to the values underneath.
Effectively assignEntity
let's you control how each key of data is normalized. Take a look here for a little more on how it works.
I put this together as a demonstration, take a look: http://requirebin./?gist=b7d89679202a202d72c7eee24f5408b6. Here's a snippet:
book.define({
data: {
publisher: publisher,
author: author,
characters: normalizr.arrayOf(character)
}}
);
publisher.define({
data: {
country: country
}
});
const result = normalizr.normalize(response, book, { assignEntity: function (output, key, value, input) {
if (key === 'data') {
Object.keys(value).forEach(function(d){
output[d] = value[d];
})
} else {
output[key] = value;
}
}});
Also see in particular Ln 29, where the array of characters
has some objects with the information nested within data
and some without. All are normalized correctly.
I also added some parts to show how it works with arrays and deeply nested data, see the country
model within publisher
.
With the data provided you will need a slug due to the absence of id's, which each schema also contains in the example.
Normalizr is fantastic, I hope that helps explain a little more about it :)