Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a graphql mutation automatically refresh a query being watched by Apollo Client?

recently started playing around with graphQL and Apollo - apollo-client.

I built a web service on top of graphQL and it's working just fine. The only issue I'm facing is on the client side of the project. For example (please refer to code below), after running createVideo() the data property of my component which is an observable which is watching the query doesn't refresh automatically and calling apollo.query manually on the callback doesn't seem to take any effect because the query returns the cached results, not the ones from the server.

Am I missing something?


app.component.ts
  import {Component, OnInit} from '@angular/core';
  import {Apollo, ApolloQueryObservable} from 'apollo-angular';
  import 'rxjs/Rx';
  import gql from 'graphql-tag';

  // http://dev.apollodata.com/angular2/mutations.html
  const NewVideoQuery = gql`
    mutation AddVideoQuery($title: String!,$duration: Int!, $watched: Boolean!){
      createVideo(video: { title: $title, duration: $duration, watched: $watched } ){
        id,
        title
      }
    }
  `;
  const VideoQuery = gql`
      {
          videos {
              id,
              title
          }
      }
  `;
  @Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
  })
  export class AppComponent implements OnInit {
    data: ApolloQueryObservable<any>;
    video: any = {};

    constructor(private apollo: Apollo) {
    }

    ngOnInit() {
      this.data = this.apollo.watchQuery({query: VideoQuery});
    }

    createVideo() {
      /**
       * This will send a mutate query to the server.
       */
      // @todo After running the mutate, the watch query doesn't refresh
      this.apollo.mutate({
        mutation: NewVideoQuery,
        variables: {
          'title': this.video.title || 'Some Video' + Math.floor(Math.random() * 10),
          'duration': 123213,
          'watched': true
        }
      }).subscribe((afterMutation) => {
        console.log(afterMutation);
        // This fires but query doesn't hit the server since it's coming from cache.

        // @todo Not even by re-running it here
        this.apollo.query({query: VideoQuery})
          .subscribe((data) => {
            console.log(data);
          });
      }, (err) => alert(err));
    }
  }

//app.component.html

  <div *ngFor="let x of data | async | select: 'videos'">
   <div><b>{{x.id}}</b>{{x.title}}</div>
  </div>

  <label>
    Title
    <input type="text" [(ngModel)]="video.title">
  </label>

  <button (click)="createVideo()">{{title}}</button>
like image 982
Lothre1 Avatar asked Jun 24 '17 19:06

Lothre1


People also ask

How does GraphQL React to Apollo?

We create the httpLink that will connect our ApolloClient instance with the GraphQL API. The GraphQL server will be running on http://localhost:4000 . We instantiate ApolloClient by passing in the httpLink and a new instance of an InMemoryCache . Finally, we render the root component of our React app.

How do GraphQL mutations work?

In GraphQL, you insert, update or delete data with mutations. A Mutation is a GraphQL Operation that allows you to insert new data or modify the existing data on the server-side. You can think of GraphQL Mutations as the equivalent of POST , PUT , PATCH and DELETE requests in REST.

How do you update the Apollo cache after a mutation?

Updating a single existing entity If a mutation updates a single existing entity, Apollo Client can automatically update that entity's value in its cache when the mutation returns. To do so, the mutation must return the id of the modified entity, along with the values of the fields that were modified.

How do you use update mutations in GraphQL?

Update mutations take filter as an input to select specific objects. You can specify set and remove operations on fields belonging to the filtered objects. It returns the state of the objects after updating. Note Executing an empty remove {} or an empty set{} doesn't have any effect on the update mutation.


1 Answers

I figured it out. Apollo-client by default caches our queries and re-runing the same query will of course return the results from cache not from the server.

Naturally, because I did a mutation to create a new record I would expect the server to refresh the dataset automatically which was not the case.

To solve this issue I came up with the idea of re-running my query from the success callback of our createVideo mutation but this time I add a special option called fetchPolicy which supports the following values:

'cache-first' | 'cache-and-network' | 'network-only' | 'cache-only' | 'standby'

Finally my fetch query looks like this:

this.apollo.query({query: VideoQuery, fetchPolicy: 'network-only'})
.subscribe(()=>{ console.log('refresh done, our watchQuery will update') })

Bonus tip:

Another intersting feature about Apollo is that you can set a pooling interval just like this so your data is always in sync with the server

this.data = this.apollo.watchQuery({query: VideoQuery, pollInterval: 10000});
like image 122
Lothre1 Avatar answered Oct 21 '22 02:10

Lothre1