Amplify-js: AppSync Subscriptions not working

Created on 30 Oct 2018  路  25Comments  路  Source: aws-amplify/amplify-js

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

What is the current behavior?
Simply cannot get AppSync Subscriptions to work. I've confirmed that the subscription works in the AWS console (by opening the query page in two windows, running the subscription in one, and queries in the other), however I cannot get it to work in my application.

Code subscription:

    const sub = API.graphql(graphqlOperation(subscriptions.onCreateLog)) as Observable<object>;
    sub.subscribe({
      next: (value: object) => {
        console.log(JSON.stringify(value));
      },
      error: (error: any) => {
        console.log(JSON.stringify(error));
      }
    });

The syntax for subscriptions in the docs, which doesn't involve "as Observable," doesn't work for me.

Per issue https://github.com/aws-amplify/amplify-js/issues/660, you must put your AppSync configuration at the root of your configuration, not under the API section. This lines up with the documentation. When I do this, however, I get:

{"data":{},"errors":[{"message":"No graphql endpoint provided."}]}

Per issue https://github.com/aws-amplify/amplify-js/issues/1302, this can be resolved by putting the appsync configuration in the API section of your configuration... In direct contradiction of https://github.com/aws-amplify/amplify-js/issues/660. This resolves the No graphql endpoint error, but still, my subscription doesn't seem to work. I've tried putting the AppSync information both at the root of the config object and in the API section, however this doesn't work either.

I'm at a bit of a loss, here. I've been at this for for quite awhile now, between this and the code in the docs not working.

Here is my network panel, if that helps

image

What is the expected behavior?
Subscriptions should receive data changes from mutations.

Which versions of Amplify, and which browser / OS are affected by this issue? Did this work in previous versions?
Version: [email protected]
Browser: Chrome
OS: Windows 10

API documentation

Most helpful comment

This is ridiculous, every single Amplify product I have tried has been a pain in the a** what a joke costs me so much time. Im about to give up on Amplify all together. Its always something takes me days to figure out out. I could have just built my own login at this point.....

All 25 comments

What JS framework are you using here? Looks like based on zone.js is angular, is that correct? If so, which version?

If Angular, you should put your config in src/main.ts and configure each category independently, OR use the entire lib.

For example:

import API from '@aws-amplify/api';
import PubSub from '@aws-amplify/pubsub'; // this is needed for subscriptions IF using modular imports
import AWSConfig from './aws-exports';
API.configure(AWSConfig);
PubSub.configure(AWSConfig);

In angular, the Observable is from zen-observable so you need to import and cast that when creating a subscription on your own:

in your api.service.ts

import API from '@aws-amplify/api';
import * as Observable from "zen-observable";

OnCreateSomethingListener: Observable<OnCreateCreateSomethingSubscription> = API.graphql(
    graphqlOperation(
      `subscription onCreateSomething {
        onCreateSomething {
          __typename
          id
          name
        }
      }`
    )
  ) as Observable<OnCreateDomainSubscription>;

We are in the process of releasing some code generation for this that will automate this with the aws-amplify/cli very soon. Let me know if this helps/clears things up and thanks for the feedback we clearly need to update/clean up some documentation regarding this.

Sorry about that, didn't mean to leave out vital details. I really appreciate your help.

I am using angular and configuring the whole lib, making use of Cognito auth, an S3 bucket, and (currently) two lambda functions, configured like so:

import Amplify from 'aws-amplify';

let amplifyConfig = {
    Auth: {
      identityPoolId: 'xxx',
      region: 'xxx',
      userPoolId: 'xxx',
      userPoolWebClientId: 'xxx'
    }, 
    Storage: {
      bucket: 'xxx',
      region: 'xxx'
    },
    API: {
      endpoints: [
        {
          name: "xxx",
          endpoint: "xxx",
          service: "lambda",
          region: "xxx"
        },
        {
          name: "xxx",
          endpoint: "xxx",
          service: "lambda",
          region: "xxx"
        }
      ],
      "aws_project_region": "xxx",
      "aws_appsync_graphqlEndpoint": "xxx",
      "aws_appsync_region": "xxx",
      "aws_appsync_authenticationType": "xxx",
      "aws_appsync_apiKey": "xxx"
    }
  };

Amplify.configure(amplifyConfig);

Configured with the graphql information under the Auth object, I don't get any errors, the subscription just never seems to receive any events. If I put it under the root object rather than Auth, I get the no graphql endpoint error.

My subscription, stored in src/app/graphql/subscriptions.ts:

export const onCreateLog = `subscription OnCreateLog(
  $id: ID
  $filename: String
  $message: String
  $success: Boolean
  $failure: Boolean
) {
  onCreateLog(
    id: $id
    filename: $filename
    message: $message
    success: $success
    failure: $failure
  ) {
    id
    filename
    message
    success
    failure
    datetime
  }
}
`;

Which is subscribed to in a service I've injected into homescreen component for testing.

log.service.ts:

import { Injectable } from '@angular/core';
import Amplify, { API, graphqlOperation } from 'aws-amplify';
import * as subscriptions from '../graphql/subscriptions';
import * as Observable from 'zen-observable';

@Injectable({
  providedIn: 'root'
})
export default class LogService {

  constructor() {
    console.log("Should init");

    const sub = API.graphql(graphqlOperation(subscriptions.onCreateLog)) as Observable<object>;
    sub.subscribe({
      next: (value: object) => {
        console.log(JSON.stringify(value));
      },
      error: (error: any) => {
        console.log(JSON.stringify(error));
      }
    });
  }
}

home.component.ts:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { Subscription } from 'rxjs/Subscription';
import { API } from 'aws-amplify';
import LogService from '../log.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  subscription: Subscription;
  public user;

  constructor(
    public auth: AuthService,
    public log: LogService
  ) { }

  ngOnInit() { 
    this.subscription = this.auth.loggedIn
      .subscribe(result => {
        this.user = result;
        //console.log(result);
      }); 
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

What are you using for Authentication type on your appsync api API key, IAM, or User Pools?

API Key just for development, but ultimately it will be User Pools

Just tried with User Pool and can confirm that isn't working either. Same situation, no errors on subscribe but I never receive the events

Hm, can you try changing your subscription code to:

// make sure you are importing zen-observable above
const sub = API.graphql(graphqlOperation(subscriptions.onCreateLog)) as Observable<object>;
sub.subscribe(
  (data: any) => {
     console.log('data from subscription: ', data);
  }
);

Also, can you add, just before your Amplify.configure statement:
Amplify.Logger.LOG_LEVEL = "DEBUG";

if you add that log statement, it will give you a large amount of logging to perhaps see if something is getting swallowed.

Added the debugging and your subscription code.

Unfortunately no change. There is debugging output for the subscription being created, but nothing after or when events should be fired

untitled

Which version of Angular are you using?

@krazykhris875 are you able to run a mutation and/or a query from the client successfully?

@mlabieniec v6.1.0

Here is my package.json, if that helps at all:

"@angular/animations": "^6.1.0",
"@angular/common": "^6.1.0",
"@angular/compiler": "^6.1.0",
"@angular/core": "^6.1.0",
"@angular/forms": "^6.1.0",
"@angular/http": "^6.1.0",
"@angular/platform-browser": "^6.1.0",
"@angular/platform-browser-dynamic": "^6.1.0",
"@angular/router": "^6.1.0",
"aws-amplify": "^1.1.6",
"zen-observable": "^0.8.0",
"core-js": "^2.5.4",
"rxjs": "^6.2.2",
"rxjs-compat": "6.2.2",
"zone.js": "~0.8.26"

I'll try out the mutation

@undefobj Just tested: mutation works. I can see my inserted data in my data source in the AWS console, and in fact if I keep the subscription running in the AWS AppSync console, it receives the event for the mutation called from my app. May app, however, doesn't.

home.component.html:

<button (click)="testMutationClicked()" class="button">Test Create Mutation</button>

home.component.ts:

testMutationClicked() {
   console.log("Clicked.");

  this.log.testMutation();
}

log.service.ts:

async testMutation() {
    console.log("Will test");

    const log = {
      xxx: "xxx"
    };

    const newLog = await API.graphql(graphqlOperation(mutations.createLog, {input: log}));
    console.log(newLog);
  }

~Just tested, turns out it works in Microsoft Edge, but not Chrome, and only when the mutation comes from my app, not from the AppSync console.~

~Working in both Edge and Chrome but only when the mutation comes from my app and not the AppSync console. Perhaps that's intended?~

Sorry, ignore all that, I'm an absolute moron and thought it was working but it was just the output from the mutation... Still not working

So if the mutation isn't working that probably means you're not connected properly, hence why subs aren't working either. What does the HTTP response say when you run a mutation?

Sorry to be unclear, the mutation is working perfectly, just for a moment I thought the subscription was working, too, but it's not.

What I'm trying to do is log output from lambda functions to the user in a web browser. The way I'm trying to do that is with AppSync, using a subscription on the create event for a DynamoDB table, so that I can log from my Lambda's using the mutation, and receive the result with the subscription.

Since the subscription looks like it isn't going to work, do you have any better suggestions? From what I can tell, PubSub is more for IoT, SNS can only be done through the browser with service workers, which aren't supported in IE, and I haven't been able to find any reference to using DynamoDB Streams with Amplify.

Your use case is fine, honestly not sure what the issue is based on what you've explained. If you create a mutation in the AppSync console and it's triggering a subscription you're saying the client can run a query to see that data but subscription messages are never received?

@krazykhris875 just to be crystal clear, when your Lambda function runs you are calling a GraphQL mutation in AppSync that is connected to a Subscription correct? Meaning you're not just making changes in DynamoDB or somewhere else and expecting them to trigger a subscription. You have to actually call a Mutation in AppSync that is hooked up to a Subscription with the @aws_subscribe directive in your schema: https://docs.aws.amazon.com/appsync/latest/devguide/real-time-data.html

@krazykhris875 just to be crystal clear, when your Lambda function runs you are calling a GraphQL mutation in AppSync that is connected to a Subscription correct?

Correct, from the queries section in the AppSync AWS console.

I can open the queries window twice, run my subscription in one, and run my create queries in another, and the subscription works perfectly there in the console. The subscription in the console also receives events when I run a create mutation from my angular app.

I appreciate all of your guys' help but I'm going to implement an alternative that doesn't use AppSync, for now.

I hit this thread with a similar problem. I do not see an error in the browser console. My subscription code:

const sub = API.graphql(
    graphqlOperation(subscriptions.addBattlestar)
)
sub.subscribe({
    next: (data) => console.log('^^alldata=', data),
    complete: console.log,
    error: console.log
});

I have enabled amplify debug (Amplify.Logger.LOG_LEVEL = "DEBUG"), but it does not show any errors either

Ignore my comment above. I created a new project, fresh graphql schema, amplify project and I got subscription working
Though I have to say, debugging subscription is a pain

My subscription, stored in src/app/graphql/subscriptions.ts:

export const onCreateLog = `subscription OnCreateLog(
  $id: ID
  $filename: String
  $message: String
  $success: Boolean
  $failure: Boolean
) {
  onCreateLog(
    id: $id
    filename: $filename
    message: $message
    success: $success
    failure: $failure
  ) {
    id
    filename
    message
    success
    failure
    datetime
  }
}
`;

Which is subscribed to in a service I've injected into homescreen component for testing.

log.service.ts:

import { Injectable } from '@angular/core';
import Amplify, { API, graphqlOperation } from 'aws-amplify';
import * as subscriptions from '../graphql/subscriptions';
import * as Observable from 'zen-observable';

@Injectable({
  providedIn: 'root'
})
export default class LogService {

  constructor() {
    console.log("Should init");

    const sub = API.graphql(graphqlOperation(subscriptions.onCreateLog)) as Observable<object>;
    sub.subscribe({
      next: (value: object) => {
        console.log(JSON.stringify(value));
      },
      error: (error: any) => {
        console.log(JSON.stringify(error));
      }
    });
  }
}

home.component.ts:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { Subscription } from 'rxjs/Subscription';
import { API } from 'aws-amplify';
import LogService from '../log.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  subscription: Subscription;
  public user;

  constructor(
    public auth: AuthService,
    public log: LogService
  ) { }

  ngOnInit() { 
    this.subscription = this.auth.loggedIn
      .subscribe(result => {
        this.user = result;
        //console.log(result);
      }); 
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

This example makes no sense

This is ridiculous, every single Amplify product I have tried has been a pain in the a** what a joke costs me so much time. Im about to give up on Amplify all together. Its always something takes me days to figure out out. I could have just built my own login at this point.....

This is ridiculous, every single Amplify product I have tried has been a pain in the a** what a joke costs me so much time. Im about to give up on Amplify all together. Its always something takes me days to figure out out. I could have just built my own login at this point.....

Inline with the above thread:

One of my API graphqlOperation issues was with the Lambda function serving the GraphQL dat set operations was silently failing/hanging. The confusion I had was with the Lambda and API dashboard that was showing 100% success rate, however I suspect something in the pipeline between the Lambda function and GraphQL fails or hangs w/o any obvious errors.

To trouble shoot I:
1) Turned on Cloud watch logs for the API to verify that the Lambda was in fact fetching data and pushing it to GraphQL ( saw a ton of {"duration":617325162,"logType":"Tracing"} entries
2) Reduced Data Set size ( in my case I had a query interacting with MSSQL database, reduced result to the bare minimum fields)
and all issues went away.

I was able to reproduce this by adding back all of the columns/data

Have to look into a pagination solution.
Hope this helps some one.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shinnapatthesix picture shinnapatthesix  路  3Comments

cosmosof picture cosmosof  路  3Comments

karlmosenbacher picture karlmosenbacher  路  3Comments

TheRealRed7 picture TheRealRed7  路  3Comments

callmekatootie picture callmekatootie  路  3Comments