aws-mobile-appsync-sdk-js icon indicating copy to clipboard operation
aws-mobile-appsync-sdk-js copied to clipboard

Network error: Can't find <queryName> on object (ROOT_QUERY) undefined

Open Jun711 opened this issue 6 years ago • 29 comments

Error: Network error: Can't find **graphqlQueryName** on object (ROOT_QUERY) undefined.

It works most of the time but fails about 20% of the time.

Similar to these issues https://github.com/apollographql/react-apollo/issues/1932 https://github.com/apollographql/apollo-client/issues/3311 https://github.com/apollographql/apollo-client/issues/1701

this.hc().then(client => {
      return client.query({
        query: queryName
        fetchPolicy: network-only
      });
}).catch(err => {
      console.log('appsyncQuery err:', err);
});
this.hc().then(client => {
      const observable = client.watchQuery({
          query: queryName
          fetchPolicy: network-only
      });

      return observable.result();
}).catch(err => {
      console.log('appsyncQuery err:', err);
});

I set up appsync.service similar to this example https://github.com/aws-samples/aws-mobile-appsync-chat-starter-angular/blob/master/src/app/chat-app/appsync.service.ts

private newClient(session) {
    return new AWSAppSyncClient({
      url: appSyncConfig.graphqlEndpoint,
      region: appSyncConfig.region,
      auth: {
        type: appSyncConfig.authenticationType,
        jwtToken: session.idToken.jwtToken
      }
    }, {
      defaultOptions: {
        watchQuery: {
          fetchPolicy: 'network-only',
        }
      }
    });
  }

using "aws-appsync": "^1.0.15",

Jun711 avatar Apr 20 '18 22:04 Jun711

Hi @Jun711

This behaviour might be caused by trying to access the cache before it is rehydrated from offline storage. Can you try this change in the file you linked (https://github.com/aws-samples/aws-mobile-appsync-chat-starter-angular/blob/master/src/app/chat-app/appsync.service.ts)?

  hydratedClient() {
    return Auth.currentSession().then(session => {
      return new Promise((resolve, reject) => {
        if (session) {
          this._client = this._client || this.newClient(session);
-          resolve(this._client.hydrated());
+ 
+          this._client.hydrated().then(resolve);
        } else {
          reject('No session');
        }
      });
    });
  }

manueliglesias avatar May 01 '18 16:05 manueliglesias

I doesn't work even after hydrated. The only time this works for me is if I set the disableOffline option to true

ghost avatar May 10 '18 16:05 ghost

I'm having the exact same issue as @eightbits-us in Safari Mobile. Setting disableOffline was the only thing that worked for me too.

smakman avatar May 11 '18 10:05 smakman

@smakman thanks - this fixed it for me as well. However I would like to be able to use the offline capabilities in Safari Mobile, since this was one of the deciding factors in choosing AWS AppSync.

mellson avatar Jun 11 '18 06:06 mellson

it worked for me using disableOffline option. Not sure about @manueliglesias suggestion as I didn't try. We decided not to use appsync and go for API Gateway for now. It seems that appsync doesn't work on older browsers. Is that right?

Jun711 avatar Jun 14 '18 21:06 Jun711

Any update on that issue @manueliglesias? Seems to happen only on Safari ...

johnatCB avatar Oct 03 '18 18:10 johnatCB

I'm having the same issue in Chrome, Can someone solve it without disableOffline option.?

tavo1987 avatar Oct 15 '18 22:10 tavo1987

@johnatCB @tavo1987 did you try the latest version (1.4.0) ?

@tavo1987 which Chrome version are you using? Is your app the first thing is opened on the browser?

elorzafe avatar Oct 17 '18 00:10 elorzafe

I'm having the same issue here. Happens to me when I first open my app in a private tab on desktop and mobile Safari and also on mobile Chrome.

luccamordente avatar Nov 26 '18 17:11 luccamordente

@luccamordente how did you configuring and using the client?

elorzafe avatar Jan 03 '19 22:01 elorzafe

Glad that i'm not the only one.. :-) Any ideas yet?

stephankaag avatar Jan 29 '19 19:01 stephankaag

We are

@smakman thanks - this fixed it for me as well. However I would like to be able to use the offline capabilities in Safari Mobile, since this was one of the deciding factors in choosing AWS AppSync.

Same here, key capability for our app. Is there any current activity on this issue?

ericlink avatar Mar 07 '19 22:03 ericlink

This issue seems to be related to inconsistencies with state.offline.online in redux-offline. The following code from /src/link/offline-link.ts sometimes incorrectly reports offline when my application is initially loaded:

const { offline: { online } } = this.store.getState();

Unfortunately, waiting for the store to be hydrated does not solve the issue. We will need to disable offline in our application until this bug has been resolved.

brandomorrill avatar Mar 11 '19 19:03 brandomorrill

@manueliglesias is there anything we can do to help with this issue?

ericlink avatar Apr 03 '19 20:04 ericlink

Confirmed ~20% failure cases after clearing offline localstorage on latest stable Chrome desktop. 0% failure with disableOffline: true.

shrugs avatar May 23 '19 22:05 shrugs

Is there a solution to this problem without having to forego offline functionality?

danielbayerlein avatar Aug 02 '19 05:08 danielbayerlein

I updated all of the dependencies to the versions least likely to break the build and that seems to have fixed this specific problem: https://github.com/awslabs/aws-mobile-appsync-sdk-js/compare/master...XLNT:master lmk if that works for you as well, @danielbayerlein

shrugs avatar Aug 04 '19 08:08 shrugs

@shrugs does above solution work on iOS device? I am using angular 6 with Ionic 4. Below is the code. This works on Chrome desktop browser. But when I run Ionic app on iOS device then I get below error intermittently. Sometimes it works but, sometimes I get this error.

ERROR: Unhandled error Network error: Can't find field listDecks on object undefined. ApolloError@ionic://localhost/vendor-es2015.js:136963:32
ionic://localhost/vendor-es2015.js:138087:60
step@ionic://localhost/vendor-es2015.js:184175:27
ionic://localhost/vendor-es2015.js:184149:75
ZoneAwarePromise@ionic://localhost/polyfills-es2015.js:3882:37
__awaiter@ionic://localhost/vendor-es2015.js:184145:36
ionic://localhost/vendor-es2015.js:138530:25
forEach@[native code]
ionic://localhost/vendor-es2015.js:138529:25
forEach@[native code]
broadcastQueries@ionic://localhost/vendor-es2015.js:138524:29
ionic://localhost/vendor-es2015.js:138030:63
onInvoke@ionic://localhost/vendor-es2015.js:71868:39
run@ionic://localhost/polyfills-es2015.js:3130:49
ionic://localhost/polyfills-es2015.js:3861:39
onInvokeTask@ionic://localhost/vendor-es2015.js:71849:43
runTask@ionic://localhost/polyfills-es2015.js:3174:57
drainMicroTaskQueue@ionic://localhost/polyfills-es2015.js:3565:42
invokeTask@ionic://localhost/polyfills-es2015.js:3475:40
timer@ionic://localhost/polyfills-es2015.js:5656:34

Service that will initialize and setup AWSAppSyncClient.

import { Injectable } from '@angular/core';
import AWSAppSyncClient from '@xlnt/aws-appsync';
import aws_exports from '../aws-exports';
import { resolve } from 'q';
import { Storage } from '@ionic/storage';
import * as localForage from "localforage";
@Injectable()
export class AppsyncService {
   _hc;

   constructor(public storage: Storage) {
    this.initializeClient();
   }

   initializeClient(){
    const client = new AWSAppSyncClient({
      url: aws_exports.aws_appsync_graphqlEndpoint,
      region: aws_exports.aws_project_region,
      auth: {
        type: aws_exports.aws_appsync_authenticationType,
        apiKey: aws_exports.aws_appsync_apiKey,
      },
      offlineConfig: {
       storage: localForage,
       callback: (err, succ) => {
         if(err) {
           const { mutation, variables } = err;
           alert(err);
           console.log(`ERROR for ${mutation}`, err);
         } else {
           const { mutation, variables } = succ;
           alert("SUCCESS");
           alert(succ);
           console.log(`SUCCESS for ${mutation}`, succ);
         }
       },
     },
     disableOffline: false
    });
    this._hc = client;
   }

   hc() {
    let promise: Promise<any> = new Promise((resolve: any) => {
        this._hc.hydrated().then(resolve);
    });
    return promise;
   }
}

Component that uses above service


import { Component,OnInit } from '@angular/core';
import { AppsyncService } from '../appsync.service';

import gql from 'graphql-tag';

const listDeckNames = gql`  
query listDeckNames {  
  listDecks {
    items {
      id
      name
    }
  }
}
`;

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
  decks ;
  questions;
  constructor(private appsync: AppsyncService) {
  }

  ngOnInit() {
    this.getDecks();
  }

  getDecks() {
    this.appsync.hc().then(client => {
      try {
      client.watchQuery({
        query: listDeckNames,
        fetchPolicy:'cache-and-network',
      }).subscribe(({data}) => {
        if (data) { 
          console.log("data ",data);
          this.decks = data.listDecks.items;
          console.log("deckNames : ", data.listDecks.items);
        }
      })
      ,(err) => {
        alert(err);
      };
    } catch (error) {
      alert(error);
    }
    });
  }
}

patilkun avatar Aug 08 '19 17:08 patilkun

@patilkun not certain, sorry — my only target is browsers. check the diff for what's changed but imo seems like the problem is somewhere in the specific apollo version that I'd updated to

shrugs avatar Aug 08 '19 20:08 shrugs

Thanks @shrugs

patilkun avatar Aug 09 '19 05:08 patilkun

seeing this issue 100% of the time with AppSync in nodeJS, - https://aws-amplify.github.io/docs/js/api#aws-appsync-sdk.

setting disableOffline to true just makes the program hang.

krsnaa avatar Aug 09 '19 17:08 krsnaa

How did you guys solve this issue without disabling offline?

AymericG avatar Mar 11 '20 01:03 AymericG

@Jun711 The sample has been archived, are you still looking for a sample with this?

sammartinez avatar Apr 14 '20 15:04 sammartinez

Can we open another issue for this? With a recent attempt i could not get it working without adding the disableOffline: true config value.

mindnuts avatar Apr 24 '20 11:04 mindnuts

UPDATE This article covers the update. Offline capabilities from appsync are being swept under the rug while they push Datastore as the new option.

Summary: The aws-appsync package has a dependency on [email protected]. This setup won't allow using @apollo/react-hooks which only functions properly with [email protected]. AWS is now pushing DataStore as the new method of offline capabilities.

Link https://dev.to/willsamu/how-to-get-aws-appsync-running-with-offline-support-and-react-hooks-678

mikeRChambers610 avatar Aug 18 '20 17:08 mikeRChambers610

Traced the first issue (of unknown total issues) to be caused by store.ts, which uses @redux-offline/redux-offline's default detectNetwork function, which tries to register windows event (https://github.com/redux-offline/redux-offline/blob/9e84f38be0720a069d47487eb0717b00ada11843/src/defaults/detectNetwork.js) - this causes the link to always use offline strategy (even at initialization)

There's an example in redux-offline's issue log that shows an example for how the detectNetwork function can be modified for Node.js (or web workers) https://github.com/piranna/redux-offline-Node.js-PoC/blob/master/index.js Most likely need to modify store.ts's createStore function that initializes offline with the detectNetwork function described above...

I think there are still more of these similar situation needing to be flushed out, but @mikeRChambers610 's solution looks a lot more flexible and promising than patching on this project (thanks for that!). Just leaving the info here for anyone interested in continuing this path :)

vendexis-stan avatar Aug 21 '20 09:08 vendexis-stan

what resolved for me was this

export const client = new AWSAppSyncClient({
  ....
  auth: {
    type: AppSyncConfig.authenticationType,
    apiKey: AppSyncConfig.apiKey
  },
  // dont use optimize response
  disableOffline: true
})

thadeu avatar Dec 03 '20 13:12 thadeu

The aws-appsync library ultimately uses the following code to set the boolean that's used to determine if the device is online or not (from @redux-offline/redux-offline/lib/defaults/detectNetwork.js):

if (window.requestAnimationFrame) {
	window.requestAnimationFrame(function () {
	  return callback({
	    online: online
	  });
	});
} else {
	setTimeout(function () {
	  return callback({
	    online: online
	  });
	}, 0);
}

The problem this creates is that it typically ends up waiting for the first animation frame before the online boolean gets set. In some devices/environments (notably Cordova/Ionic ios, likely others), the initial batch of appsync queries will trigger before the first animation frame (but after the rehydrated value has been set to true) so the online boolean is still null, causing the query to behave as if it's offline.

To work around this issue, you can wait a couple of animation frames before rendering the component that will make the initial appsync queries. A functional react component might have something like this:

const [isPassedFirstAnimationFrame, setIsPassedFirstAnimationFrame] = useState(false);

useEffect(() => {
	window.requestAnimationFrame(() => {
	  // One requestAnimationFrame call may or may not be enough - it depends on whether the detectNetwork's function fires first or second. We call it again here just to be safe.
	  window.requestAnimationFrame(() => {
	    setIsPassedFirstAnimationFrame(true);
	  });
	});
}, []);

return (
	{isPassedFirstAnimationFrame &&
		<ComponentThatMakesAppsyncQueries/>
	}
	{!isPassedFirstAnimationFrame &&
		<LoadingComponent/>
	}
);

Ideally, a similar hook would be integrated into the Rehydrated component of aws-appsync's library so waiting for rehydration would be sufficient.

rpmadden08 avatar Dec 17 '20 16:12 rpmadden08

any update on this issue.?

Mughees605 avatar Oct 15 '21 14:10 Mughees605