Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React-apollo 3.0 with aws-appsync-react #448

Open
Xocix opened this issue Aug 6, 2019 · 73 comments
Open

React-apollo 3.0 with aws-appsync-react #448

Xocix opened this issue Aug 6, 2019 · 73 comments
Labels
Apollo appsync-core Related to core updates within AppSync SDK React Issues in regards to React and AppSync SDK to-be-reproduced We need to reproduce this issue

Comments

@Xocix
Copy link

Xocix commented Aug 6, 2019

Do you want to request a feature or report a bug?
Bug

What is the current behavior?
Installing react-apollo 3.0 makes the Rehydrated component stop working. Going back to react-apollo 2.5.8 makes it work again

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.
Error message recieved: The context client is marked as required in Rehydrated, but its value is undefined.

What is the expected behavior?
Rehydrated should have a client to be able to rehydrate

@thcdevcl
Copy link

thcdevcl commented Aug 6, 2019

I am having the same issue!

@Xocix
Copy link
Author

Xocix commented Aug 7, 2019

My workaround is to create my own Rehydrated component that takes in client as a prop.
Also i needed to add a resolution to the package.json otherwise it was crashing for another error.

"resolutions": { "apollo-client": "2.6.3" }

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import AWSAppSyncClient from 'aws-appsync';

const Rehydrate = props => (
    <div
        className={`awsappsync ${
            props.rehydrated
                ? 'awsappsync--rehydrated'
                : 'awsappsync--rehydrating'
        }`}
    >
        {props.rehydrated ? props.children : <span>Loading...</span>}
    </div>
);

class Rehydrated extends Component {
    static propTypes = {
        render: PropTypes.func,
        children: PropTypes.node,
        loading: PropTypes.node,
        client: PropTypes.instanceOf(AWSAppSyncClient).isRequired,
    };

    constructor(props) {
        super(props);

        this.state = {
            rehydrated: false,
        };
    }

    async componentWillMount() {
        await this.props.client.hydrated();

        this.setState({
            rehydrated: true,
        });
    }

    render() {
        const { render, children, loading } = this.props;
        const { rehydrated } = this.state;

        if (render) return render({ rehydrated });

        if (children) {
            if (loading) return rehydrated ? children : loading;

            return <Rehydrate rehydrated={rehydrated}>{children}</Rehydrate>;
        }
    }
}

export default Rehydrated;

@laurib
Copy link

laurib commented Aug 8, 2019

This is my custom Rehydrated component.

import React, { useContext, useEffect, useState } from 'react';
import { getApolloContext } from 'react-apollo';
import AWSAppSyncClient from 'aws-appsync';

const Rehydrated = ({ children }) => {
  const { client } = useContext(getApolloContext());
  const [rehydrated, setState] = useState(false);

  useEffect(() => {
    if (client instanceof AWSAppSyncClient) {
      (async () => {
        await client.hydrated();
        setState(true);
      })();
    }
  }, [client]);
  return rehydrated ? <>{children}</> : null;
};

export default Rehydrated;

@dai-shi
Copy link

dai-shi commented Aug 9, 2019

Basically the same, but in TypeScript. (might be better to have type guard.)

import { useApolloClient } from '@apollo/react-hooks';

const Rehydrated: React.FC = ({ children }) => {
  const client = useApolloClient();
  const [rehydrated, setRehydrated] = useState(false);
  useEffect(() => {
    (async () => {
      await (client as AWSAppSyncClient<NormalizedCacheObject>).hydrated();
      setRehydrated(true);
    })();
  }, [client]);
  return (
    <div className={`awsappsync ${rehydrated ? 'awsappsync--rehydrated' : 'awsappsync--rehydrating'}`}>
      {rehydrated ? children : <span>Loading...</span>}
    </div>
  );
};

@praisegeek
Copy link

This is only a temporary solution. Mutations hang on the client currently.

@TeoTN
Copy link

TeoTN commented Aug 17, 2019

It won't help as it seems AWSAppSyncClient diverged too far from ApolloClient for the rehydration to work. It'll only work w/react-apollo v3 if you disable offline entirely using disableOffline: true in client's options

@amcdnl
Copy link

amcdnl commented Aug 26, 2019

@TeoTN - I disabled offline and still got the issue. I bumped to 3.0.1 wonder if that has anything to do w/ it?

@TeoTN
Copy link

TeoTN commented Aug 28, 2019

@amcdnl well, I forgot to mention that I'm using the Rehydrate from @dai-shi AND disabling offline. I'm pretty sure this is a dumb solution (i.e. combining both, the Rehydrate is not needed probably when offline's off) but it temporarily does the thing.

@amcdnl
Copy link

amcdnl commented Aug 28, 2019

@TeoTN - Hmm, still didn't work for me. I'll just hold out for the official update (fingers crossed).

@ConnectivityChris
Copy link

@TeoTN - Hmm, still didn't work for me. I'll just hold out for the official update (fingers crossed).

I'm using @dai-shi 's Rehydrate and adding implicit any to my client and seems to work

<ApolloProvider client={client as any}>
  <Rehydrated>
    <Router />
  </Rehydrated>
</ApolloProvider>

@doldyn
Copy link

doldyn commented Sep 1, 2019

I tried this as well last weekend. I got past the rehydrate by writing a separate one but the mutations don't process. They get added to the redux-offline queue but do not execute. AppSync has an offline-link implemented which is defining the effect. I suspect there are things that needs to be sorted at that level. For mutations, I was unable to make it work without fundamentally changing AppSync core. I hope we will see an official adaptation of this soon.

@SebSchwartz
Copy link

Can AWS tell us what is the plan for supporting Apollo 3.0? Is it in the roadmap? Is there an ETA?
@elorzafe @manueliglesias @dabit3

@amcdnl
Copy link

amcdnl commented Sep 5, 2019

Heres the complete solution I ended up with:

In my package.json, I have:

{
  "dependencies": {
    "apollo-client": "^2.6.4",
    "apollo-link": "^1.2.12",
    "apollo-link-error": "^1.1.11",
    "aws-amplify": "^1.1.36",
    "aws-amplify-react": "^2.3.12",
    "aws-appsync": "^1.8.1",
    "react-apollo": "^3.0.1",
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "aws-appsync-react": "^1.2.9",
  },
  "resolutions": {
    "apollo-client": "2.6.3"
  }
}

then I used one of the Rehydrates above which looks like:

import React, { useEffect, useState } from 'react';
import { useApolloClient } from 'react-apollo';
import AWSAppSyncClient from 'aws-appsync';

export const Rehydrated = ({ children }) => {
  const client = useApolloClient();
  const [rehydrated, setState] = useState(false);

  useEffect(() => {
    if (client instanceof AWSAppSyncClient) {
      (async () => {
        await client.hydrated();
        setState(true);
      })();
    }
  }, [client]);

  return rehydrated ? children : null;
};

then my AWS config looks like:

import Amplify from 'aws-amplify';
import { Auth } from 'core/Auth';
import AWSAppSyncClient, { createAppSyncLink } from 'aws-appsync';
import { ApolloLink } from 'apollo-link';
import { onError } from 'apollo-link-error';
import { AWS_CUSTOM_CONFIG } from '../../aws-custom-exports';

Amplify.configure(AWS_CUSTOM_CONFIG);

const onErrorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.map(({ message, locations, path }) =>
      console.error(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
      )
    );

  if (networkError) {
    console.error(`[Network error]: ${networkError}`);
  }
});

const auth = {
  type: AWS_CUSTOM_CONFIG.aws_appsync_authenticationType as 'OPENID_CONNECT',
  jwtToken: () => {
    const session = Auth.getStoredSession();
    if (session) {
      return session.idToken;
    }
  }
};

export const appSyncClient = new AWSAppSyncClient(
  {
    url: AWS_CUSTOM_CONFIG.aws_appsync_graphqlEndpoint,
    disableOffline: true,
    region: AWS_CUSTOM_CONFIG.aws_appsync_region,
    auth
  },
  {
    link: ApolloLink.from([
      onErrorLink,
      createAppSyncLink({
        url: AWS_CUSTOM_CONFIG.aws_appsync_graphqlEndpoint,
        region: AWS_CUSTOM_CONFIG.aws_appsync_region,
        auth,
        complexObjectsCredentials: () => Auth.getStoredSession()
      })
    ])
  }
);

then I bring it all together in the index.tsx like:

import React, { Suspense, lazy } from 'react';
import ReactDOM from 'react-dom';
import { appSyncClient, Rehydrated } from 'core/Aws';
import { ApolloProvider } from 'react-apollo';

const Root = () => (
      <ApolloProvider client={appSyncClient as any}>
        <Rehydrated>
          Hello
        </Rehydrated>
      </ApolloProvider>
);

ReactDOM.render(<Root />, document.getElementById('root'));

@djkmiles
Copy link

djkmiles commented Sep 13, 2019

The current version of ApolloClient works nicely with this, even the apollo-boost one, with Cognito auth. Just want to get subscriptions on there instead "self-host" MQTT over WS.

let token = await Auth.currentSession().then(session => session.getIdToken().getJwtToken()) // Cognito   
let client = new ApolloClient({ uri: awsconfig.aws_appsync_graphqlEndpoint, headers: { 'Authorization': token } }) // Same endpoint

I agree with consensus, latest Apollo Client [v3] now leads web development, and React Native.

@doldyn
Copy link

doldyn commented Oct 5, 2019

We have now explored this issue further. The reason to use AppSync over just the ApolloClient is if you need the offline capability. We wrote our own Rehydrated and got around the issues there but then when posting a mutation, it got enqueued in redux-offline but never processed. We debugged the flow and found the issues to be in the offline-link created redux-offline effect.

AppSync is using a method called client.queryManager.buildOperationForLink that no longer exists to create the observable and hence nothing happens when redux-offline calls the effect.

We refactored this to the following construct using the same approach as Apollo does in the mutations:

        client.queryManager.getObservableFromLink(
            mutation,
            extraContext,
            variables,
            false,
        ).subscribe({            
            next: data => {...

There were a few other minor issues that we have been changing to make the framework work for our specific use case but this is the key one the core framework falls over on.

We now have this working nicely with hooks and our adapted version of AppSync for our use case and I just wanted to share in case anyone else are looking to do the same or if this could inspire the core team to update the framework as it is a hinderance for adaptation.

@sakhmedbayev
Copy link

sakhmedbayev commented Oct 10, 2019

We are also stuck here. We are using AppSync with Next.js. The solution found here works only client-side. The server-side cannot go beyond Loading... stage :-(

@undefobj
Copy link
Contributor

undefobj commented Oct 11, 2019

Hello everyone - I want to reply and assure you that we have not abandoned support of this project. Per my earlier comment, this is a complex process and to be completely transparent there are things in the Apollo library that are out of our control and there have also been breaking changes in Apollo versions which we're trying to account for, which is also why we pinned the version. We've been spending many hours trying to account for this but it's not simple to solve. After putting more thought into this we've got some initial solutions that I hope will unblock many of you. What we've done is packaged two of the "Links" which are part of the SDK - the Auth and Subscription Links - and published them to NPM. This will allow you to include them in the newer Apollo versions if you wish to use the newer features. This does not include the "Offline Link" for offline support which is more complex to solve and we're looking into for the future.

Installation of the links:

npm install aws-appsync-auth-link aws-appsync-subscription-link

Usage:
https://github.com/awslabs/aws-mobile-appsync-sdk-js#using-authorization-and-subscription-links-with-apollo-client-no-offline-support

With is I would then give the following recommendations for choosing your client strategy:

  1. If you do not have offline use cases, you can either use the Auth & Subscription Links above with the latest Apollo client or alternatively use the Amplify GraphQL client instead of Apollo:https://aws-amplify.github.io/docs/js/api#amplify-graphql-client
  2. If you do have offline use cases please use the current version as-is with the older Apollo version. We're still working on a future strategy for the Offline Link.

Please let us know if you have any trouble using these links or if there is a scenario which we haven't accounted for here.

@SebSchwartz
Copy link

Thanks for giving us updates on this @undefobj .
For offline support, what about the solution of @doldyn ?

@undefobj
Copy link
Contributor

Thanks for giving us updates on this @undefobj .
For offline support, what about the solution of @doldyn ?

The problem is query manager uses some private methods, and when you factor in the new Local State features of Apollo which can override the cache there are several scenarios which can lead to inconsistent state of the underlying persisted data. Essentially you have a race condition of multiple "writers" which is why we need a deterministic mechanism to update the cache, otherwise we cannot guarantee data integrity.

@tgjorgoski
Copy link

tgjorgoski commented Oct 12, 2019

@undefobj ,
In the aws-appsync code there is also another link added when constructing the client:
new link_1.ComplexObjectLink(complexObjectsCredentials)
When I saw it, I guessed that this is needed for "complex object" support for AppSync where it also involves working with connected files on S3. Is that right?
I'm not using this functionality ATM, but would this functionality still be working with the new proposed solution with new Apollo with included auth and sub. links?

@undefobj
Copy link
Contributor

@undefobj ,
In the aws-appsync code there is also another link added when constructing the client:
new link_1.ComplexObjectLink(complexObjectsCredentials)
When I saw it, I guessed that this is needed for "complex object" support for AppSync where it also involves working with connected files on S3. Is that right?
I'm not using this functionality ATM, but would this functionality still be working with the new proposed solution with new Apollo with included auth and sub. links?

Right now this is a separate link which is not published and still requires the older Apollo version. If there is high demand we can investigate publishing this as well, but I'd rather see if the core issue can be resolved.

@dai-shi
Copy link

dai-shi commented Oct 14, 2019

Thanks to #448 (comment), it's now much cleaner with apollo-client. (without offline use cases)

Some notes:

  1. a minor typo apsync.

  2. it shows an error: You are calling concat on a terminating link, which will have no effect.
    Modifying the code from the doc

const link = ApolloLink.from([
  createAuthLink({ url, region, auth }),
  createSubscriptionHandshakeLink(url),
  createHttpLink({ uri: url }),
]);

to

const link = ApolloLink.from([
  createAuthLink({ url, region, auth }),
  createSubscriptionHandshakeLink(url),
]);

the error message is gone.
I think this is fine because of


and it has resultsFetcherLink = createHttpLink({ uri: url })) as default.

I wish somebody could confirm that.

@elorzafe
Copy link
Contributor

@dai-shi thanks for pointing this out

@sakhmedbayev
Copy link

sakhmedbayev commented Oct 17, 2019

Using Typescript with this example. Here:

  const auth = {
    type: AUTH_TYPE.AWS_IAM,
    credentials: () => Auth.currentCredentials()
  }

  const link = ApolloLink.from([
    createAuthLink({ url, region, auth }),
  ]);

auth gets:

Type '{ type: AUTH_TYPE; credentials: () => Promise<ICredentials>; }' is not assignable to type 'AuthOptions'.
  Type '{ type: AUTH_TYPE; credentials: () => Promise<ICredentials>; }' is not assignable to type 'AuthOptionsIAM'.
    Types of property 'type' are incompatible.
      Type 'AUTH_TYPE' is not assignable to type '"AWS_IAM"'.ts(2322)
index.d.ts(5, 5): The expected type comes from property 'auth' which is declared here on type '{ url: string; region: string; auth: AuthOptions; }'

@dai-shi
Copy link

dai-shi commented Oct 17, 2019

Putting as const after AWS_IAM might solve it.

@sakhmedbayev
Copy link

Thank @dai-shi! Facing another issue using proposed solution with Next.js. I posted a separate issue here.

@elorzafe elorzafe added React Issues in regards to React and AppSync SDK appsync-core Related to core updates within AppSync SDK to-be-reproduced We need to reproduce this issue labels Apr 15, 2020
@willsamu
Copy link

Hello everyone, I have just published an article on how to set up AWS AppSync with react apollo 3.x, react hooks and offline support. Hope it helps!

@amcdnl
Copy link

amcdnl commented May 25, 2020

Another good library I found for this: https://github.com/wolfeidau/appsync-apollo-links

@NTonani
Copy link

NTonani commented Jun 30, 2020

ETA around 3.0 support, including offline?

@BenoitDel
Copy link

Appsync is now two major release behind for its apollo/client dependency. the solution given by @undefobj works but makes the project depends both on apollo 2.4 and apollo 3.0 which has major impact on bundle size.
Now that apollo 3.0 has reach stable version i hope we will see progress. Any updates ?

@zuhair-naqvi
Copy link

Can someone please confirm if this is at least on the Road Map for future releases?

@MasterNeuromancer
Copy link

@dylan-westbury I think the solution you posted above works with react native but what most of us are looking for here is a solution that works with the current version of apollo and maintains the offline functionality which looks like you had to disable in your example. The people want both!

@ablbol
Copy link

ablbol commented Aug 31, 2020

Please add support to complexObjectsCredentials with latest Apollo version 3.0. I really need it for my work.
Thanks

@Jerboas86
Copy link

@ablbol This issue is opened for more than a year now. This library is barely maintained (alive). So good luck

@manueliglesias
Copy link
Contributor

Hi,

Just to let you know, we've published new versions of the links to make them compatible with Apollo 3 (no offline support)

For more details: #561 (comment)

@andrekovac
Copy link

@manueliglesias @undefobj @elorzafe Any timeline to upgrade aws-appsync-react to support @apollo/client version 3 with offline support ?

The offline functionality is the main reason why I go for the AWS/Apollo stack

@andrekovac
Copy link

Another question: Could https://github.com/apollographql/apollo-cache-persist bring offline support together with the packages @manueliglesias published to support Apollo 3?

@andrekovac
Copy link

@tgjorgoski Was your thumbs-up emoji confirmation that it does work with apollo-cache-persist? Or do you have the same question as I do?

@tgjorgoski
Copy link

@andrekovac , just liked the question as an interesting one, I would also like to know.

@aagamdoshi
Copy link

@TeoTN - Hmm, still didn't work for me. I'll just hold out for the official update (fingers crossed).

Did you get any luck on this?

@fab-mindflow
Copy link

@manueliglesias @undefobj @elorzafe Any timeline to upgrade aws-appsync-react to support @apollo/client version 3 with offline support ?

The offline functionality is the main reason why I go for the AWS/Apollo stack

Any update please?

@amcdnl
Copy link

amcdnl commented Jul 19, 2021

@fab-mindflow - I'll let you know I gave up on this - its actually way cleaner just to do just apollo. Also the libraries require AWS-SDK which creates a huge bloat and its not tree shakeable.

@LYevhen
Copy link

LYevhen commented Jul 21, 2021

@undefobj That is very sad that after two years there is no progress, roadmaps and even hope for others to have this important update.

@tabdon
Copy link

tabdon commented Jul 25, 2021

@amcdnl Could you please elaborate on your setup? I'd like to ditch AWS-SDK as well. Thanks

@razor-x
Copy link

razor-x commented Jul 26, 2021

Here is what I have been using in production for a while now:

➡️ https://gist.github.com/razor-x/e19d7d776cdf58d04af1e223b0757064

Versions:

{
  "@apollo/client": "^3.3.12",
  "apollo-cache-persist": "^0.1.1",
  "graphql": "^15.0.0",
  "subscriptions-transport-ws": "^0.9.16"
}

The WebSocket integration is based off of https://gist.github.com/zachboyd/f5630736b0a5a9b627d61bfd25299c90 and the custom domain support is from #517 (comment)

Only issue I've noticed is that I'm not 100% sure the subscriptions are as stable or reliable as I would expect. If anyone uses this and can share their findings, let me know.

@PatrykMilewski
Copy link

How it's possible that it's still not done after 3 years?

@shamseer-ahammed
Copy link

shamseer-ahammed commented Nov 30, 2023

its 2023 nov 31 and still , no official fix for this.

Is this Rehydrate component even needed in

"@apollo/client": "^3.8.7"
"aws-appsync": "^4.1.9",
"aws-appsync-react": "^4.0.15",

? As per their official docs i did'nt find them using it

https://github.com/awslabs/aws-mobile-appsync-sdk-js#using-authorization-and-subscription-links-with-apollo-client-v3-no-offline-support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Apollo appsync-core Related to core updates within AppSync SDK React Issues in regards to React and AppSync SDK to-be-reproduced We need to reproduce this issue
Projects
None yet
Development

No branches or pull requests