commercetools / nodejs

Everything related to the Node.js ecosystem for the commercetools platform.
https://commercetools.github.io/nodejs/
MIT License
75 stars 70 forks source link

api-response-normalizer #383

Closed dferber90 closed 6 years ago

dferber90 commented 6 years ago

Description

The MC currently normalises some data received through the API. This normalisation happens through normalizr.

However, we assume that normalisation may be useful for everybody, so it should be included in this toolkit.

Example implementation

I went ahead and created an example implementation for responses of /products and /products/:id on this branch.

We would need to implement the schemas behind the remaining endpoints. This is quite straightforward, it just takes a bit of time. I didn't implement more yet as I wanted to get feedback first.

Examples

Open them (long) #### Querying a single product When querying `/product/:id` we receive a product of the following shape ```js { "id": "product-id-1", "productType": { "obj": { "id": "product-type-id-1" } }, "masterData": { "current": { "categories": [ { "obj": { "id": "category-id-1" } }, { "obj": { "id": "category-id-2" } } ], "masterVariant": { "prices": [ { "customerGroup": { "obj": { "id": "customer-group-id-1" } }, "channel": { "obj": { "id": "channel-id-2", "custom": { "type": { "obj": { "id": "custom-id-1" } } } } }, "discounted": { "discount": { "obj": { "id": "product-discount-id-1" } } }, "custom": { "type": { "obj": { "id": "custom-id-2" } } } } ], "assets": [ { "custom": { "type": { "obj": { "id": "custom-id-2" } } } } ], "scopedPrice": { "customerGroup": { "obj": { "id": "customer-group-id-2" } }, "channel": { "obj": { "id": "channel-id-1", "custom": { "type": { "obj": { "id": "custom-id-2" } } } } }, "discounted": { "discount": { "obj": { "id": "product-discount-id-1" } } }, "custom": { "type": { "obj": { "id": "custom-id-3" } } } } }, "variants": [ { "prices": [ { "customerGroup": { "obj": { "id": "customer-group-id-1" } }, "channel": { "obj": { "id": "channel-id-1", "custom": { "type": { "obj": { "id": "custom-id-1" } } } } }, "discounted": { "discount": { "obj": { "id": "product-discount-id-1" } } }, "custom": { "type": { "obj": { "id": "custom-id-2" } } } } ], "assets": [ { "custom": { "type": { "obj": { "id": "custom-id-2" } } } } ], "scopedPrice": { "customerGroup": { "obj": { "id": "customer-group-id-2" } }, "channel": { "obj": { "id": "channel-id-2", "custom": { "type": { "obj": { "id": "custom-id-2" } } } } }, "discounted": { "discount": { "obj": { "id": "product-discount-id-1" } } }, "custom": { "type": { "obj": { "id": "custom-id-3" } } } } } ] } } } ``` After normalisation the response will be normalised to ```js { "entities": { "categories": { "category-id-1": { "id": "category-id-1", }, "category-id-2": { "id": "category-id-2", }, }, "channels": { "channel-id-1": { "custom": { "type": { "obj": "custom-id-1", }, }, "id": "channel-id-1", }, "channel-id-2": { "custom": { "type": { "obj": "custom-id-2", }, }, "id": "channel-id-2", }, }, "customerGroups": { "customer-group-id-1": { "id": "customer-group-id-1", }, "customer-group-id-2": { "id": "customer-group-id-2", }, }, "productDiscounts": { "product-discount-id-1": { "id": "product-discount-id-1", }, }, "productTypes": { "product-type-id-1": { "id": "product-type-id-1", }, }, "products": { "product-id-1": { "id": "product-id-1", "masterData": { "current": { "categories": [ { "obj": "category-id-1", }, { "obj": "category-id-2", }, ], "masterVariant": { "assets": [ { "custom": { "type": { "obj": "custom-id-2", }, }, }, ], "prices": [ { "channel": { "obj": "channel-id-2", }, "custom": { "type": { "obj": "custom-id-2", }, }, "customerGroup": { "obj": "customer-group-id-1", }, "discounted": { "discount": { "obj": "product-discount-id-1", }, }, }, ], "scopedPrice": { "channel": { "obj": "channel-id-1", }, "custom": { "type": { "obj": "custom-id-3", }, }, "customerGroup": { "obj": "customer-group-id-2", }, "discounted": { "discount": { "obj": "product-discount-id-1", }, }, }, }, "variants": [ { "assets": [ { "custom": { "type": { "obj": "custom-id-2", }, }, }, ], "prices": [ { "channel": { "obj": "channel-id-1", }, "custom": { "type": { "obj": "custom-id-2", }, }, "customerGroup": { "obj": "customer-group-id-1", }, "discounted": { "discount": { "obj": "product-discount-id-1", }, }, }, ], "scopedPrice": { "channel": { "obj": "channel-id-2", }, "custom": { "type": { "obj": "custom-id-3", }, }, "customerGroup": { "obj": "customer-group-id-2", }, "discounted": { "discount": { "obj": "product-discount-id-1", }, }, }, }, ], }, }, "productType": { "obj": "product-type-id-1", }, }, }, "types": { "custom-id-1": { "id": "custom-id-1", }, "custom-id-2": { "id": "custom-id-2", }, "custom-id-3": { "id": "custom-id-3", }, }, }, "result": "product-id-1", } ``` #### Querying multiple products When querying `/products` Original Response ```js { limit: 5, offset: 0, count: 2, total: 2, results: [{ id: 'product-a' /* ... */ }, { id: 'product-b' /* ... */ }], } ``` After normalising ```js { entities: { products: { 'product-a': {/* ... */}, 'product-b': {/* ... */}, }, // ... }, result: { limit: 5, offset: 0, count: 2, total: 2, results: ['product-a', 'product-b'], } } ```

Future

Pro / Con

Pros of normalised data

Cons of normalised data

Overall I'm not sure whether we should continue with this or not. I'll kickstart a discussion within the MC.

wizzy25 commented 6 years ago

Thanks for this @dferber90. Looks very interesting to me. As this data shape is currently a specific requirement of the MC, I don't know if it's a good idea to add this package to the SDK. Especially because with regards to data shape, any other use for the SDK may then require a different data shape.

dferber90 commented 6 years ago

@wizzy25 Normalisation is not something specific to the MC. It's widely used best practice to normalise data.

For example from the redux documentation: https://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html

wizzy25 commented 6 years ago

@dferber90 My comment was referring to the schema used in your branch/package and not to normalisation in general

dferber90 commented 6 years ago

Oh, sorry I misunderstood that!

The schema normalises based on the reference expansions points our API provides. That seems like the ideal place to normalise data, doesn't it?

We're still discussing on whether we want to move forward with this in the MC internally. I definitely don't want to push us to implement this if we don't even use it ourselves and nobody asks for it.

Anyways, this is more or less on hold until we align within the MC. I can close and come back later in case we still want this.