'I get different result from Graphql playground and front-end
I am using graphql and Vue.js and apollo
Here is my DateBank
const sensorsdb = [
{
name: "sensor 1",
id: "s-1",
timestamp: 1582021371,
value: 100
},
{
name: "sensor 1",
id: "s-1",
timestamp: 1579451703,
value: 150
},
{
name: "sensor 2-1",
id: "s-2-1",
timestamp: 1582021371,
value: 200
},
{
name: "sensor 2-2",
id: "s-2-2",
timestamp: 1579451703,
value: 350
},
{
name: "sensor 2-2",
id: "s-2-2",
timestamp: 1582021371,
value: 300
},
{
name: "sensor 3",
id: "s-3",
timestamp: 1582021371,
value: 400
},];
I want to get all objects according to object id. sensorId is an array. because I want to get multiple objects with multiple ids.
The following is my API function to get object.
async getDataById({ sensorId }) {
let sensorsData = [];
for (let i = 0; i < sensorId.length; i++) {
let sensorData = this.sensorDataStore.sensors.filter(sensor => sensor.id === sensorId[i]);
sensorsData = sensorsData.concat(sensorData);
}
return sensorsData;
}
In Front-end, gql file is following:
query sensorData($id: [String]){
sensorData(id: $id){
name
id
value
timestamp
}}
and with my apollo query code in vue.js, in this case selectedSensorId is ["s-2-1", "s-2-2"]
<ApolloQuery :query="require('../graphql/sensorDataById.gql')" :variables="{ id: selectedSensorId }">
<template v-slot="{ result: { loading, error, data } }">
<b-loading :is-full-page=true :active.sync=loading :can-cancel="false"/>
<div v-if="error">
<no-data-error />
</div>
<div v-if="data">
{{ data }}
<bar-chart-view :sensorInfo="data.sensorData"/>
</div>
</template>
</ApolloQuery>
But I got the following different result: Graphql playground which has correct result The front-end result with duplicated sensor-s-2
Solution 1:[1]
Apollo Client normalizes results according to the id
and __typename
fields as described in the docs. If an array returns multiple items with the same id
, by default they will share the same cache key, which means what's returned by the client will be the same object.
You should provide a custom dataIdFromObject
function to your InMemoryCache
constructor that accommodates your specific use case. Something like:
const dataIdFromObject = object => {
switch (object.__typename) {
case 'SensorDataPoint': return `SensorDataPoint:${object.id}:{value}`;
default: return defaultDataIdFromObject(object);
}
}
Note that if you use the same type elsewhere, you may experience issues with the cache updated correctly after mutations because we are now keying off both the value
and id
. You might want to consider a different approach to your schema where the ids are actually unique :
type SensorDataPoint {
id: ID!
sensorId: ID!
sensorName: String!
value: Int!
timestamp: Int!
}
or even better
type SensorDataPoint {
id: ID!
value: Int!
timestamp: Int!
sensor: Sensor!
}
type Sensor {
id: ID!
name: String!
}
Solution 2:[2]
I know it its been a while but what Daniel Rearden mentioned above, I included the { addTypename: false }
as options for InMemoryCache
const client = new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, extensions }) => {
console.log(`[GraphQL error]: Message: ${message}, Code: ${extensions?.code}`)
})
if (networkError) {
console.log(`[Network error] ->: ${networkError}`)
Toast.show('Connection Error', {
position: Toast.positions.TOP,
type: 'danger',
duration: 3000,
})
}
}),
authMiddleware,
link,
]),
cache: new InMemoryCache({ addTypename: false }),
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Daniel Rearden |
Solution 2 | Mickey |