aws-amplify / docs

AWS Amplify Framework Documentation
https://docs.amplify.aws
Apache License 2.0
485 stars 1.05k forks source link

Appsync GraphQL from node server - example needed for websocket subscription implementation #5065

Open Jefftopia opened 1 year ago

Jefftopia commented 1 year ago

Describe the content issue: The AWS Amplify docs describe how to connect to amplify from a node server, for example, if you are restricted to using IAM auth. While the nodejs code sample is helpful, there's no example for how to establish a graphql websocket connection for a subscription to appsync.

I have scoured the internet the past few days and cannot find any good documentation or examples of how to do this. I can share what didn't work - I have a couple of attempts based on what amplify does share, but unfortunately my approach always returns a nondescript 400 "network" error. I will include a code snippet for reference, but I am really looking for clear documentation.

Thanks!

URL page where content issue is: https://docs.amplify.aws/lib/graphqlapi/graphql-from-nodejs/q/platform/js/

Erroneous reference code (based off the amplify site):

  function establishWsConnection() {
    const GRAPHQL_HOST = 'pretty-url.domain.com'
    let ws: WebSocket = undefined;
    let signedHeaders: any = undefined;

    const signer = new SignatureV4({
      credentials: provideLocalIdentity,
      region: this.AWS_REGION,
      service: 'appsync',
      sha256: Sha256,
    });

    const requestToBeSigned: HttpRequest = new HttpRequest({
      method: 'POST',
      hostname: GRAPHQL_HOST,
      path: '/graphql/connect',
      protocol: 'https:',
      headers: {
        'content-type': 'application/json; charset=UTF-8',
        'accept': 'application/json, text/javascript',
        'content-encoding': 'amz-1.0',
        host: GRAPHQL_HOST, // compulsory
      },
    });

    const signed = await signer.sign(requestToBeSigned);

    const api_header = signed.headers;
    signedHeaders = api_header;

    const payload = {};

    const base64_api_header = Buffer.from(JSON.stringify(api_header)).toString('base64');
    const base64_payload = Buffer.from(JSON.stringify(payload)).toString('base64');
    const appsync_url = `${this.websocketConnectionString}?header=${base64_api_header}&payload=${base64_payload}`;

    ws = new WebSocket(appsync_url, ['graphql-ws']);

    /**
     * Send request over websocket
     */
    const _send = (obj) => {
      ws.send(JSON.stringify(obj));
    };

    let initializingMode = true;

    ws.onopen = () => {
      _send({ type: 'connection_init' });
    };

    ws.onerror = err => {
      console.error('err', err.message, );
    };

    ws.onmessage = (e) => {
      const data = JSON.parse(e.data.toString());

      if (initializingMode) {
        if (data.type == 'connection_ack') {
          // Acknowledge came, so we can start subscribing

          const query = {
            query: `
            subscription MySubscription {
              onCreate(id: 123) {
                name,
                email,
                id
              }
            }`,
          };

          const queryStr = JSON.stringify(query);

          _send({
            id: '987876988798611113', // should be uuid?
            type: 'start',
            payload: {
              data: queryStr,
              extensions: {
                authorization: {
                  host: GRAPHQL_HOST,
                  ...signedHeaders,
                },
              },
            },
          });
        }

        initializingMode = false;
        return;
      }
    };
  }
70nyIT commented 1 year ago

I'm using Apollo Client on a React App, connecting to AppSync. I think it's the same service used by Amplify.

I was not able to establish a connection with the library graphql-ws, but I managed to do so with subscriptions-transport-ws (that's deprecated!). So I'm following this discussion to see if there's a way to connect using graphql-ws

Jefftopia commented 1 year ago

@70nyIT Sounds like you are connecting from a browser environment, not NodeJs. It is insecure to use IAM auth in the Browser. But it is a requirement for me.

70nyIT commented 1 year ago

Yeah, I realized after you were using IAM. Nope, I'm not going to use IAM roles in the browser 😅