Amplify-js: Can you use AWS Amplify with Redux Saga?

Created on 22 Jul 2019  路  3Comments  路  Source: aws-amplify/amplify-js

* Which Category is your question related to? *
API
* What AWS Services are you utilizing? *
AppSync, DynamoDB, Redux Saga
* Provide additional details e.g. code snippets *
I'm trying to make an API call using AWS Amplify's helper methods with Redux Saga.

import { API, graphqlOperation } from 'aws-amplify';
import * as R from 'ramda';
import { call, put } from 'redux-saga/effects';

import { listQuestions } from '../graphql/queries';
import { openSnackbar } from '../snackbar/snackbar-reducer';
import { addQuestions, fetchQuestions as fq } from './question-reducer';

const toAction = R.pipe(
  R.path(['data', 'listQuestions', 'items']),
  addQuestions
);

function* fetchQuestions() {
  try {
    const result = yield call(
      API.graphql,
      graphqlOperation(listQuestions, { limit: 100 })
    );
    yield put(toAction(result));
  } catch (e) {
    console.log(e);
    yield put(openSnackbar(e.message));
  }
}

As you can see, I'm using the API.graphql and graphqlOperation helper methods from the JS framework part of AWS Amplify.

But, this always throws the error:

TypeError: Cannot read property '_graphql' of null
    at APIClass.graphql (API.js:831)

The given code at APIClass.graphql looks like this:

APIClass.prototype.graphql = function (_a) {
    var paramQuery = _a.query, _b = _a.variables, variables = _b === void 0 ? {} : _b, authMode = _a.authMode;
    var query = typeof paramQuery === 'string' ? parser_1.parse(paramQuery) : parser_1.parse(printer_1.print(paramQuery));
    var _c = query.definitions.filter(function (def) { return def.kind === 'OperationDefinition'; })[0], operationDef = _c === void 0 ? {} : _c;
    var operationType = operationDef.operation;
    switch (operationType) {
        case 'query':
        case 'mutation':
            return this._graphql({ query: query, variables: variables, authMode: authMode });
        case 'subscription':
            return this._graphqlSubscribe({ query: query, variables: variables, authMode: authMode });
    }
    throw new Error("invalid operation type: " + operationType);
};

Why would this be null here? Is there a way to use the GraphQL helper methods from AWS Amplify with Redux Saga?

Most helpful comment

Just found the solution. You need to supply a this context to call.

yield call([API, 'graphql'], graphqlOperation(listQuestions, { limit: 100 }))

All 3 comments

Just found the solution. You need to supply a this context to call.

yield call([API, 'graphql'], graphqlOperation(listQuestions, { limit: 100 }))

Was anyone able to get an auth channel/subscription to keep track of the currentAuthenticatedUser? Below is how I am trying to do it, but my AWS auth channel is not working. I'm coming from Firebase, so below is an example of a working saga to keep track of the user. Any idea @janhesters?

Firebase Saga (Working):

// Redux Saga: Firebase Auth Channel
export function* firebaseAuthChannelSaga() {
  try {
    // Auth Channel (Events Emit On Login And Logout)
    const authChannel = yield call(reduxSagaFirebase.auth.channel);

    while (true) {
      const { user } = yield take(authChannel);

      // Check If User Exists
      if (user) {
        // Firebase ID Token
        const firebaseIdToken = yield user.getIdTokenResult();

        // Redux: Login Success
        yield put(loginSuccess(user));
      }
      else {
        // Redux: Logout Success
        yield put(logoutSuccess());

        // authChannel.close();
      }
    }
  }
  catch (error) {
    console.log(error);
  }
};

AWS Saga (Not Working):

// Redux Saga: AWS Auth Channel
export function* awsAuthChannelSaga() {
  try {
    console.log('AUTH CHANNEL');

    // Auth Channel (Events Emit On Login And Logout)
    const currentUser = yield call([Auth, 'currentAuthenticatedUser']);

    while (true) {
      const { attributes } = yield take(currentUser);

      // Check If User Exists
      if (attributes) {
        console.log('AUTH CHANNEL LOGGED IN');

        // Redux: Login Success
        yield put(loginSuccess(attributes));
      }
      else {
        console.log('AUTH CHANNEL LOGGED OUT');

        // Redux: Logout Success
        yield put(logoutSuccess());
      }
    }
  }
  catch (error) {
    console.log(error);
  }
};

Index:

// Root Saga
export default function* rootSaga() {
  // AWS Auth Channel
  yield fork(awsAuthChannelSaga);

  yield all([
    // Sagas: Auth
    fork(watchLoginSaga),
    fork(watchLogoutSaga),
    fork(watchResetPasswordSaga),
    fork(watchSendVerificationCodeSaga),
  ]);
};

@jefelewis I just ran into the same issue. Looking at the context link in the post above, the docs for call indicate the first array index is the context and the second is the _function reference_:

call([context, fn], ...args)

Taking your code here:

// second array index is the function name
const currentUser = yield call([Auth, 'currentAuthenticatedUser']);

and changing it to this worked for me:

// second array index is the function reference
const currentUser = yield call([Auth, Auth.currentAuthenticatedUser]);

Hope that helps.

Was this page helpful?
0 / 5 - 0 ratings