Search code examples
angulargraphqlapollo

Dynamically creating Apollo GraphQL mutation call in Angular


I have a working mutation call in my Angular application for mutating some fields for an object with Apollo GraphQL. Part of the mutation call includes some return data that Apollo will associate with some object already in its cache and update it with the new value returned.

I would like to only return fields that are actually being mutated to avoid big packets being sent across the network.

I've managed to dynamically generate a mutation document that only includes fields being mutated and I'm passing that into apollo.mutate({mutation: newMutation, ...}) which returns an observable. The mutation only fires once that observable is subscribed to. I've verified that the component using that subscription is unsubscribing and being destroyed before the new mutation is called.

The problem is that Apollo is caching the mutation document and sending out the first mutation (with only fields mutated the first time) for all calls. I've verified this by checking my web browser's Network tab.

I've tried getting Apollo to stop caching it by using unique mutation names by appending datetime. I've checked that variables to the mutation are unique. I've tried using fragments, yet the fragments would also need to be dynamically generated and it's the same issue.

Anyone know what I'm doing wrong?


Solution

  • Okay, I figured it out. Apollo Client is adding mutation documents to its ROOT_MUTATION. I've found that using Apollo Client's writeData/writeQuery functions only let you add/modify the ROOT_QUERY.

    Originally I was trying to dynamically add fields to my GraphQL AST. The code looked something like this:

    import gql from 'graphql-tag';
    ...
    const myMutation = gql`
      mutation myMutation($id: ID!, ...) {
        mutateFields(id: $id, ...) {
          id  # You need to return at least one field or it will complain
          # Add mutated fields to return here
        }
      }
    `;
    
    # dynamically call the following lines based on fields to be added
    myMutation.definitions[0].selectionSet.selections[0].selectionSet.selections = [
      {...}  # Added my own fields and their subfields here
    ];
    

    The problem was that it worked the first time, so I knew I was correctly modifying the GraphQL AST, yet subsequent calls (with new fields to return) were correctly created but Apollo was sending out the first mutation of the same name (I checked my Browser's Network tab to verify).

    THE SOLUTION:

    Don't modify the GraphQL AST, instead modify it as a string literal similar to the comment by Alireza Hariri on this post: GraphQL dynamic query building.

    Sample code:

    const mainMutation = `
       mutation myMutation($id: ID!, ...) {
        mutateFields(id: $id, ...) {
          id  # You need to return at least one field or it will complain
          # REPLACE_THIS_WITH_FIELDS
        }
      }
    `;
    
    const mutationWithFields = mainMutation.replace(
      `# REPLACE_THIS_WITH_FIELDS`,
      "myField { mySubfield1 mySubfield2 }"
    );
    
    const finalMutation = gql`${mutationWithFields}`;
    this.apollo.mutate({
      mutation: finalMutation,
      variables: {
        id: myId,
        ...
      }
    });