C.Lee
C.Lee

Reputation: 11279

How to use Apollo Client with AppSync?

AppSync uses MQTT over WebSockets for its subscription, yet Apollo uses WebSockets. Neither Subscription component or subscribeForMore in Query component works for me when using apollo with AppSync.

One AppSync feature that generated a lot of buzz is its emphasis on real-time data. Under the hood, AppSync’s real-time feature is powered by GraphQL subscriptions. While Apollo bases its subscriptions on WebSockets via subscriptions-transport-ws, subscriptions in GraphQL are actually flexible enough for you to base them on another messaging technology. Instead of WebSockets, AppSync’s subscriptions use MQTT as the transport layer.

Is there any way to make use of AppSync while still using Apollo?

Upvotes: 14

Views: 15736

Answers (4)

Sebastian N
Sebastian N

Reputation: 140

There has been an update with the aws-appsync SDK for Apollo V3. From the README.md:

import { createAuthLink } from "aws-appsync-auth-link";
import { createSubscriptionHandshakeLink } from "aws-appsync-subscription-link";

import {
  ApolloProvider,
  ApolloClient,
  InMemoryCache,
  HttpLink,
  ApolloLink,
} from "@apollo/client";

import appSyncConfig from "./aws-exports";

/* The HTTPS endpoint of the AWS AppSync API 
(e.g. *https://aaaaaaaaaaaaaaaaaaaaaaaaaa.appsync-api.us-east-1.amazonaws.com/graphql*). 
[Custom domain names](https://docs.aws.amazon.com/appsync/latest/devguide/custom-domain-name.html) can also be supplied here (e.g. *https://api.yourdomain.com/graphql*). 
Custom domain names can have any format, but must end with `/graphql` 
(see https://graphql.org/learn/serving-over-http/#uris-routes). */
const url = appSyncConfig.aws_appsync_graphqlEndpoint;


const region = appSyncConfig.aws_appsync_region;

const auth = {
  type: appSyncConfig.aws_appsync_authenticationType,
  apiKey: appSyncConfig.aws_appsync_apiKey,
  // jwtToken: async () => token, // Required when you use Cognito UserPools OR OpenID Connect. token object is obtained previously
  // credentials: async () => credentials, // Required when you use IAM-based auth.
};

const httpLink = new HttpLink({ uri: url });

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

const client = new ApolloClient({
  link,
  cache: new InMemoryCache(),
});

const ApolloWrapper = ({ children }) => {
  return <ApolloProvider client={client}>{children}</ApolloProvider>;
};

Upvotes: 0

Neil
Neil

Reputation: 2610

I continued to get errors until I added "as const" to the auth type enum.

import { fromNodeProviderChain } from "@aws-sdk/credential-providers"; // ES6 import
import { ApolloClient, InMemoryCache, gql, ApolloLink, HttpLink } from '@apollo/client/core';
import { createAuthLink, AUTH_TYPE } from "aws-appsync-auth-link";
.....
const auth = {
  type: AUTH_TYPE.AWS_IAM as const,
  region: "us-east-1",
  credentials: fromNodeProviderChain()
};
const link = ApolloLink.from([
  createAuthLink({ url, region: "us-east-1", auth }),
  new HttpLink({ uri: url })
]);
const client = new ApolloClient({
  cache: new InMemoryCache(),
  link
});

With the credentials set to the fromNodeProviderChain(), it should work its way up your IAM credential chain as expected (mine are coming from SSO profile).

Upvotes: 0

C.Lee
C.Lee

Reputation: 11279

Ok, here is how it worked for me. You'll need to use aws-appsync SDK (https://github.com/awslabs/aws-mobile-appsync-sdk-js) to use Apollo with AppSync. Didn't have to make any other change to make subscription work with AppSync.

Configure ApolloProvider and client:

    // App.js
    import React from 'react';
    import { Platform, StatusBar, StyleSheet, View } from 'react-native';
    import { AppLoading, Asset, Font, Icon } from 'expo';
    import AWSAppSyncClient from 'aws-appsync' // <--------- use this instead of Apollo Client
    import {ApolloProvider} from 'react-apollo' 
    import { Rehydrated } from 'aws-appsync-react' // <--------- Rehydrated is required to work with Apollo

    import config from './aws-exports'
    import { SERVER_ENDPOINT, CHAIN_ID } from 'react-native-dotenv'
    import AppNavigator from './navigation/AppNavigator';

    const client = new AWSAppSyncClient({
      url: config.aws_appsync_graphqlEndpoint,
      region: config.aws_appsync_region,
      auth: {
        type: config.aws_appsync_authenticationType,
        apiKey: config.aws_appsync_apiKey,
        // jwtToken: async () => token, // Required when you use Cognito UserPools OR OpenID Connect. token object is obtained previously
      }
    })


    export default class App extends React.Component {
      render() {
        return <ApolloProvider client={client}>
          <Rehydrated>
            <View style={styles.container}>
              <AppNavigator />
            </View>
          </Rehydrated>  
        </ApolloProvider>
    }

Here is how the subscription in a component looks like:

    <Subscription subscription={gql(onCreateBlog)}>
      {({data, loading})=>{
        return <Text>New Item: {JSON.stringify(data)}</Text>
      }}
    </Subscription>

Upvotes: 14

DenisH
DenisH

Reputation: 889

Just to add a note about the authentication as it took me a while to work this out:

If the authenticationType is "API_KEY" then you have to pass the apiKey as shown in @C.Lee's answer.

  auth: {
    type: config.aws_appsync_authenticationType,
    apiKey: config.aws_appsync_apiKey,
  }

If the authenticationType is "AMAZON_COGNITO_USER_POOLS" then you need the jwkToken, and if you're using Amplify you can do this as

  auth: {
    type: config.aws_appsync_authenticationType,
    jwtToken: async () => {
      const session = await Auth.currentSession();
      return session.getIdToken().getJwtToken();
    }
  }

But if your authenticationType is "AWS_IAM" then you need the following:

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

Upvotes: 5

Related Questions