react-nativeaws-sdk-js

Getting [TypeError: Cannot read property 'apply' of undefined] - is the AWS V3 SDK compatible with React Native?


Background: I am investigating possible tech stacks for a project, so I am trying a bunch of things I'm not very familiar with. ( rough going, obviously )

I see Amazon recommends Amplify for React Native apps that need to interact with AWS services, but I need to definitively verify if the SDK is an option or not, by either producing a working example as proof, or understanding why a working example isn't possible.

My use case is logging in with a Cognito user. I have little code sample that works fine in Node, but in a React Native App it just gives me:

[TypeError: Cannot read property 'apply' of undefined]

when running in React Native. Maybe I'm just missing something obvious, React Native or JavaScript related.

Here is the code sample that runs in Node:

import {
    AuthFlowType,
    CognitoIdentityProviderClient,
    InitiateAuthCommand,
  } from '@aws-sdk/client-cognito-identity-provider';
  
  const region = 'us-east-1';
  
  const cognitoClient = new CognitoIdentityProviderClient({
    region,
  });
  
  initializeAuthorizationLogin('user', 'aGoodPassword');

  async function initializeAuthorizationLogin(
    username,
    password,
  ) {
    const params = {
      ClientId: 'theclientid',
      AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
      AuthParameters: {
        USERNAME: username,
        PASSWORD: password,
      },
    };
    const initiateAuthCommand = new InitiateAuthCommand(params);
    
    try {
      const response = await cognitoClient.send(initiateAuthCommand);
      console.log(response);
    } catch (e) {
      console.log(e);
    }
  }

In React Native, App.tsx looks like this:

import {
  AuthFlowType,
  CognitoIdentityProviderClient,
  InitiateAuthCommandInput,
  InitiateAuthCommand,
} from '@aws-sdk/client-cognito-identity-provider';

import React from 'react';
import {
  Button,
  SafeAreaView,
  ScrollView,
  StatusBar,
  Text,
  View,
} from 'react-native';

async function initializeAuthorizationLogin(
  username: string,
  password: string,
) {
  const params: InitiateAuthCommandInput = {
    ClientId: 'theclientid',
    AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
    AuthParameters: {
      USERNAME: username,
      PASSWORD: password,
    },
  };
  const initiateAuthCommand = new InitiateAuthCommand(params);

  try {
    const region = 'us-east-1';

    const cognitoClient = new CognitoIdentityProviderClient({
      region,
    });

    const response = await cognitoClient.send(initiateAuthCommand);
    console.log(response);
  } catch (e) {
    console.log(e);
  }
}

function tryLogin() {
  initializeAuthorizationLogin('user', 'aGoodPassword');
}

function App(): React.JSX.Element {
  return (
    <SafeAreaView>
      <StatusBar />
      <ScrollView contentInsetAdjustmentBehavior="automatic">
        <View>
          <Text>A blank starting page</Text>
          <Button onPress={tryLogin} title="Try logging in" />
        </View>
      </ScrollView>
    </SafeAreaView>
  );
}

export default App;

The Node example provides a 200 and the expected IdToken, AccessToken, RefreshToken. The React Native App provides the TypeError. I don't know why.


Solution

  • I'm going to self-answer this one in case it helps someone else out in future.

    TLDR:

    Add

    import '@aws-sdk/util-endpoints';

    and install the appropriate package from NPM. Apparently Metro and the AWS SDK cooks up some horrible concoction ( which would explain why it works great in Node and not at all in React Native )

    It was an adventure in experimentation, but I stumbled upon this: https://github.com/aws/aws-sdk-js-v3/issues/5777 and presto, it worked.

    ( I ran into another issue like this right after, that was also solved by the same 'technique'. struggling to fix: "ReferenceError: Property 'ReadableStream' doesn't exist" helped resolve that. )

    I think there's a different root cause here that can perhaps be resolved by updating the Metro config. Doing this kind of thing for a production app seems squishy.