Search code examples
amazon-web-servicesasynchronousaws-lambdaaws-api-gatewayopenai-api

How do I pass arguments to an async Lambda function through API Gateway?


We have an AWS Lambda function that makes a call to the OpenAI API to generate page_content for a Page based on a transcript. Once a response is received, the respective Page element in a DynamoDB table is updated.

import boto3
import json
import openai
    
    def lambda_handler(event, context):
       dynamodb = boto3.client('dynamodb')
       openai.api_key = "XXXXX"

       d = json.loads(event['body'])
       pageId = d['page_id']
       transcript = d['transcript']
           
       conversation = [
          {"role": "system", "content": "Assistant"},
          {"role": "user", "content": transcript},
          {"role": "system", "content":"Provide a blog post for the content above."}
       ]

       response = openai.ChatCompletion.create(
          model="gpt-3.5-turbo",
          messages=conversation,
          temperature=1,
          max_tokens=2000,
          top_p=1,
          frequency_penalty=0,
          presence_penalty=0
       )
    
       page_content = response['choices'][0]['message']['content']
       state = "COMPLETE"
    
       response = dynamodb.update_item(
          TableName='Pagedb-XXXXXX-dev',
          Key={'id': {'S': pageId}},
          UpdateExpression='SET content = :val1, contentState = :val2',
          ExpressionAttributeValues={
             ':val1': {'S': page_content},
             ':val2': {'S': state}
          },
       )
    
       return {
          'statusCode': 200,
          'headers': {
             'Content-Type': 'application/json', 
             'Access-Control-Allow-Origin': '*',
             'Access-Control-Allow-Headers': 'Content-Type',
             'Access-Control-Allow-Origin': '*',
             'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
           }
        }

This Lambda function is invoked through API Gateway from a React application.

    const response = await axios.post(
          'https://XXXX.execute-api.us-east-2.amazonaws.com/default/generate-content',
          {page_id: page.id, transcript: page.transcript},
        );

The Issue: Most of our calls exceed the 30 second maximum timeout for API Gateway and will cause an error, despite the Lambda function going to successful completion. To work around this, we made the call asynchronous by disabling "Use Lambda Proxy Integration" and adding an HTTP Header X-Amz-Invocation-Type mapped from 'Event'. This allows our Lambda function to run beyond 30 seconds, however we cannot pass details through 'event' of the handler function anymore, as lambda proxy allowed for this.

Question: How can we pass details from our React application to the Lambda function through API Gateway if Lambda Proxy integration is disabled?

Update - Implemented Fix With Mark's feedback, I setup a Mapping Template for the Integration Request of my method in API Gateway doing the following:

  1. Integration Request -> Mapping Templates -> Set "Request body passthrough" to "When there are no templates defined"
  2. "Add mapping template" -> entered "application/json"
  3. Generate template: Method Request passthrough
  4. Modified to the following:
#set($allParams = $input.params())
{
   "method" : "$context.httpMethod", ## API method
   "authcontext" : "$context.authorizer.stringkey", ## Optional output from Lambda Authorizers

   ## passthrough body
   "body" : $input.json('$'),

   ## passthrough headers, querystrings and path parameters
   "params" : {
      #foreach($type in $allParams.keySet())
      #set($params = $allParams.get($type))
      "$type" : {
         #foreach($paramName in $params.keySet())
         "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
         #if($foreach.hasNext),#end
         #end
      }
      #if($foreach.hasNext),#end
      #end
   }
}
  1. Updated my Lambda function to set the variables as such
    pageId = event['body']['page_id']
    transcript = event['body']['transcript']

Solution

  • When you disabled the proxy integration, you also disabled the default proxy request mapping. You need to enable integration passthrough request mapping to get that request data showing up in the Lambda function's event parameter again.