Graphql in apollo client: local data is not normalized
I want to normalize (i.e. flatten nested elements) my graphql’s local cache for efficiency reasons (otherwise a change in a nested item would redraw all components). My understanding was that Apollo would automatically normalize the cache… but apparently it seems like I misunderstood. Indeed, if I do:
const IS_LOGGED_IN = gql`
query IsUserLoggedIn {
isLoggedIn @client
items @client {
name @client
quantity @client
}
}
`;
client.writeQuery({
query: IS_LOGGED_IN,
data: {
isLoggedIn: false,
items: [
{name: "Oranges", quantity: "5"},
{name: "Pears", quantity: "10"},
]
},
});
then the dev tools shows non-normalized items (the structure is nested instead of flat) which are nested (for comparison, the Location
items are normalized elements, coming from a query to an external database):
I realized that I could normalize my data if I manually specify two things inside the data
:
- the ID
- the type of the object (basically its table) via
__typename
which gives:
const IS_LOGGED_IN = gql`
query IsUserLoggedIn {
isLoggedIn @client
cake @client {
name @client
ingredients @client {
name @client,
quantity @client
}
}
}
`;
client.writeQuery({
query: IS_LOGGED_IN,
data: {
isLoggedIn: false,
cake: [
{id: "1", __typename:"Cake", name: "Cookies", ingredients: [
{id: "4", __typename:"Ingredients", name: "Sugar", quantity: "100g"},
{id: "5", __typename:"Ingredients", name: "Chocolate", quantity: "250g"}
]},
{id: "2", __typename:"Cake", name: "Choux", ingredients: [
{id: "6", __typename:"Ingredients", name: "Eggs", quantity: "2"},
{id: "8", __typename:"Ingredients", name: "Flour", quantity: "250g"}
]},
]
},
});
(it seems like I don’t need to specify id
or __typename
in the query
type, seems like it is automatically forwarded). However, it is a bit annoying to manually specify the ID and the typename… It seems like I managed to generate random ID using this code, but I’m affraid that this might break cache.identify
:
const client = new ApolloClient({
cache: new InMemoryCache({
typePolicies: {
Cake: {
fields: {
ingredients: {
merge(existing, incoming) {
return incoming;
},
},
},
},
Ingredients: {
// If the ID can be deduced from the fields
//keyFields: ['name'],
// If the ID must be different for each elements
// I hope that using randomness here is not an issue…
// in particular I am not sure if it does not break
// functions to identify the ID of an element…
keyFields: () => "Ingredients:" + uuidv4(),
},
},
}),
});
and I also can’t find a way to avoid typing __typename:"…"
everywhere.
So here is my question: Is it possible to automatically infer the typename from the query, and generate id automatically to save typing and reduce errors, when dealing with local data?
Read more here: Source link