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):

enter image description here

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