11

I used Amplify to generate a static website and the underlying React app. Initially I also generated an API endpoint but, because my lambda function may run over the API Gateway timeout limit (29 seconds), I need to invoke the lambda function directly from the generated React App, instead of going through API Gateway.

The code looks as follows, for the React page to authenticate using Cognito:

import Auth from '@aws-amplify/auth';
import { withAuthenticator } from 'aws-amplify-react';
import awsconfig from './aws-exports';

Auth.configure(awsconfig);

The above lines wrap the App (root) object and work as advertised. But since I do not want to use the API Gateway, how do I invoke the AWS Lambda function directly from React App?

The answers I could find talk about importing AWS etc, which seems to be in conflict with what we are trying to do here. I need to use the authenticated connection (which already works using the above code) when invoking lambda, so I cannot use generic invocation given in this example.

The Invoke API does not provide any examples as well.

Any advice is appreciated.

0

2 Answers 2

20

Note: if you do not need a response after your long running lambda, then consider API Gateways' Asynchronous Invocation

Amplify calls this approach "working with service objects".

To do this you'll have to ensure that the role Cognito gives your authenticated users includes permissions for lambda:invoke as well as any additional permissions needed within the function. I'll assume you can do that for now, however you can see the Role-Based Access Control documentation, or ask another question if not.

To access these roles within Amplify you need to use the Auth.currentCredentials function, which returns a promise with a credentials object, which can then be used on an aws-sdk client.

For example:

import Auth from '@aws-amplify/auth';
import Lambda from 'aws-sdk/clients/lambda'; // npm install aws-sdk

Auth.currentCredentials()
  .then(credentials => {
    const lambda = new Lambda({
      credentials: Auth.essentialCredentials(credentials)
    });
    return lambda.invoke({
      FunctionName: 'my-function',
      Payload: JSON.stringify({ hello: world }),
    });
  })

You can see the full documentation for invoking lambdas on the AWS-SDK javascript documentation.

However you should be aware that the payload from API Gateway is constructed by AWS and includes much more information than just the body that the endpoint was called with, however when you invoke directly, all you'll get is the payload, so you'll have to build that payload object accordingly.

Sign up to request clarification or add additional context in comments.

5 Comments

This is perfect, thanks! Is there a recommended way to load awsconfig from Amplify and pass to Lambda? I had to make the following minor change to make it work. Auth.currentCredentials() .then(credentials => { const lambda = new Lambda({ credentials: Auth.essentialCredentials(credentials), 'region': 'us-east-1' });
A “long running Lambda” can be terminated before it times out using the time remaining property that is available in the “context” object. That way you can devise a way to always get a response, good or bad.
So how does the Lambda function know the caller is authenticated? The payload doesn't contain any information about authentication (unless manually added). What is setting the "credentials" actually doing to the request?
When you create the lambda client (const lambda = new Lambda) you set the credentials function using Amplify's singleton Auth.essentialCredentials(credentials) (which gets the credentials already set using any of the Auth login functions). These credentials are actually passed in using the header, which AWS IAM then uses to determine if you have the right to invoke that lambda function. See the link to Role-Based control for a discussion on how to link Cognito and IAM.
@thomasmichaelwallace Perfect, I have wrapped this inside an async function, let say I want to populate a dropdown from the values fetched from the Lambda invoked, I call this async function from within the useEffect() hook, however the dropdown is sometimes populated and sometimes not, does that have to do something with the invoked lambda response?
1

In Amplify V6 the credentials must be fetched using the fetchAuthSession API https://docs.amplify.aws/gen1/react/build-a-backend/auth/manage-user-session/#retrieve-a-user-session

import { fetchAuthSession } from "aws-amplify/auth";
fetchAuthSession().then(({ credentials }) => {
    const lambda = new Lambda({
      credentials: credentials,
    });
    return lambda.invoke({
      FunctionName: 'my-function',
      Payload: JSON.stringify({ hello: world }),
    });
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.