jsonapi-instance-store
Deserializes JSON API, and stores the instances.
Why?
- 🚀Easy To Use:
- no configuration
- framework agnostic
- 🌟Featured:
- ES6 class based deserialization
- synced relationships
- customizable deserializer
- 😎Reliable:
- written in TypeScript
- tested with CI
Getting Started
// class in TypeScriptclass Dog { id: string; age: string; // Of course, you can define instance method. get numAge(): number { return Number(this.age); }} // or in JavaScript(you do not have to declare instance properties).class Dog { get numAge() { return Number(this.age); }} class Cat { id: string; friend: Dog;} const catResponseWithRelationships = { data: { type: "cats", id: "1", relationships: { friend: { data: { type: "dogs", id: "3" } } } }, included: [ { type: "dogs", id: "3", attributes: { age: "2" } } ]};
import store from 'jsonapi-instance-store'; store.define(Dog);store.define(Cat); const cat = store.read(catResponseWithRelationships); console.log(cat instanceof Cat); // => trueconsole.log(cat.id); // => "1"console.log(cat.friend instanceof Dog); // => trueconsole.log(cat.friend.id); // => "3"console.log(cat.friend.age); // => "2"
note
The class name should correspod to the "type" of JSON API.
Whether type is single or plural, and the case of type do not matter.
note
In TypeScript, your editor's static analysis cannot know the class of the deserialized instances as it is defined dynamically.
So you should use type assertion to use static analysis.
Usage
Deserialize
sync relationships
const catResponseWithNoIncluded = { data: { type: "cats", id: "1", relationships: { friend: { data: { type: "dogs", id: "1" } } } }}; const dogResponse = { data: { type: "dog", id: "1", attributes: { age: "2" } }};
// deserialize record with no "included"const catWithNoIncluded = store.read(catResponseWithNoIncluded); // cat.friend has no attributesconsole.log(catWithNoIncluded.friend.age); // => undefined // read a response corresponding to cat.friendstore.read(dogsResponse) // now, cat.friend has its attributes!console.log(catWithNoIncluded.friend.age); // => "2"
append another instances to an existing result
const dogsResponse = { data: [ { type: "dog", id: "1", attributes: { age: "2" } }, { type: "dog", id: "2", attributes: { age: "3" } } ]}; const anoutherDogsResponse = { data: [ { type: "dog", id: "10", attributes: { age: "4" } }, { type: "dog", id: "11", attributes: { age: "5" } } ]};
const multipleDogs = store.read(dogsResponse); // append another instances to the existing resultstore.append(multipleDogs, anotherDogResponse); console.log(multipleDogs.length); // => 4console.log(multipleDogs[multipleDogs.length - 1].id); // => "11"console.log(multipleDogs[multipleDogs.length - 1].age); // => "5"
note
As the second argument, you can also pass a payload containing single recored.
case of the attributes key does not matter
export class Person { id: string; firstName: string; lastName: string;} const personResponseInSnake = { data: { type: "person", id: "1", attributes: { first_name: "John", last_name: "Doe" } }}; const personResponseInCamel = { data: { type: "person", id: "1", attributes: { firstName: "John", lastName: "Doe" } }};
import store from 'jsonapi-instance-store'; store.define(Person); // deserialize JSON API with snake cased attributesconst person1 = store.read(personResponseInSnake); console.log(person1.firstName); // => "John" // deserialize JSON API with camel cased attributesconst person2 = store.read(personResponseInCamel); console.log(person2.firstName); // => "John"
Store
class Dog { id: string; age: string;} const dogsResponse = { data: [ { type: "dog", id: "1", attributes: { age: "2" } }, { type: "dog", id: "2", attributes: { age: "3" } } ]};
import store from 'jsonapi-instance-store'; store.define(Dog); store.read(dogsResponse); // findconsole.log(store.find("dog", "1").id); => "1"console.log(store.find("dogs", "1").id); => "1"console.log(store.find("dog", 1).id); => "1" // findAllconst dogs = store.findAll("dog"); console.log(dogs instanceof Array); => trueconsole.log(dogs.length) => 2 // destroyconst dog = store.find("dog", "1")store.destroy(dog);console.log(store.find("dog", "1")); => undefinedconsole.log(store.findAll("dog").length); => 1 // resetstore.reset();console.log(store.findAll("dog").length); => 0
Custom Deserializer
You can use your own deserializer. A sample implementation is JsonApiDeserializer.ts
import CustomDeserializer from "path/to/custom/deserializer";import { Store } from "jsonapi-instance-store";const store = new Store();store.deserializer = new CustomDeserializer(store);store.read(payload);
Contributing
License
MIT