Search code examples
react-nativeapollo-clientaws-appsyncaws-amplify

How do I configure Amplify to to use multiple AppSync endpoints?


I need to support authenticated and unauthenticated AppSync requests in a React Native app. Since AppSync only allows one authorization type per API, I am setting up two APIs: one for authenticated users (Cognito User Pools), and one for guests (API Key).

I think to make this work I need to have two distinct AWSAppSyncClient configs in the same app.

  // authenticated user    
  const appSyncAuthenticatedClient = new AWSAppSyncClient({
    url: Config.APPSYNC_AUTHENTICATED_ENDPOINT,
    region: Config.APPSYNC_REGION,
    auth: {
      type: 'AMAZON_COGNITO_USER_POOLS',
      jwtToken: async () =>
        (await Auth.currentSession()).getAccessToken().getJwtToken()
    }
  });

  // guest    
  const appSyncUnauthenticatedClient = new AWSAppSyncClient({
    url: Config.APPSYNC_UNAUTHENTICATED_ENDPOINT,
    region: Config.APPSYNC_REGION,
    auth: {
      type: 'API_KEY',
      apiKey: Config.APPSYNC_API_ID
    }
  });

and then determine which to use based on whether or not they are logged in

    Auth.currentAuthenticatedUser()
      .then(user => this.appSyncRunningClient = appSyncAuthenticatedClient)
      .catch(err => this.appSyncRunningClient = appSyncUnauthenticatedClient);

    const App = props => {
      return (
        <ApolloProvider client={this.appSyncRunningClient}>
          <Rehydrated>
              <RootStack/>
            </Root>
          </Rehydrated>
        </ApolloProvider>
      );
    };

    export default App;

This fails because currentAuthenticatedUser returns a promise, and I'm stuck at how to resolve a promise at this top level instantiation of the app. I'll also need to swap out this config during auth events.

In what way can I dynamically select and change the ApolloProvider config at startup and authentication events?


Solution

  • This is currently not possible. Until top-level await is officially supported you should create two Apollo clients one for the API and one for the Cognito.

    for example: in your App.js

    export default function App(props) {
      const [client, setClient] = useState(null);
    
      useEffect(() => {
       checkAuth()
      }, []);
    
      function checkAuth() {
        Auth.currentSession().then(session => {
          const token = session.getIdToken();
          const jwtToken = token.getJwtToken();
          if (typeof jwtToken == "string") {
            const authClientConfig = {
              url: awsmobile.aws_appsync_graphqlEndpoint,
              region: awsmobile.aws_appsync_region,
              disableOffline: true,
              auth: {
                type: AUTH_TYPE.AMAZON_COGNITO_USER_POOLS,
                jwtToken: jwtToken
              }
            }
            const link = ApolloLink.from([createAuthLink(authClientConfig), createSubscriptionHandshakeLink(authClientConfig)]);
            const authClient = new ApolloClient({ link, cache: new InMemoryCache({ addTypename: false }) });
            setClient(authClient);
          } else {
            throw "error";
          }
        }).catch(e => {
          console.log(e);
          const config = {
            url: awsmobile.aws_appsync_graphqlEndpoint,
            region: awsmobile.aws_appsync_region,
            disableOffline: true,
            auth: {
              type: AUTH_TYPE.API_KEY,
              apiKey: awsmobile.aws_appsync_apiKey
            }
          }
          const link = ApolloLink.from([createAuthLink(config), createSubscriptionHandshakeLink(config)]);
          const authClient = new ApolloClient({ link, cache: new InMemoryCache({ addTypename: false }) });
            setClient(authClient);
        })
      }
    
        if (!client) {
          return "Loading..."
        }
    
        return (
          <ApolloProvider client={client}>
              ...
          </ApolloProvider>
        );
    }`