Search code examples
pythonboto3serverlessaws-serverlesschalice

How to directly invoke a Chalice Lambda from another Lambda without going through the API Gateway and Authorizer again?


I have 2 Lambdas using Chalice behind an API Gateway. All endpoints are protected by a Cognito authorizer.

When I call GET /hello, I want the first Lambda to fetch data from the second Lambda:

# Lambda1
@app.route('/hello')
def say_hello():
    name = # fetch data from Lambda2
    return Response({'message': 'hello ' + name})

# Lambda2
@app.route('/name')
def a_name():
    return Response({'name': 'GLaDOS'})

This request doesn't have to go through the API Gateway + authorizer again, as it creates unnecessary overhead.

How to make this call directly?


Solution

  • EDIT: I recommend the below solution only if you can't have an Internal API Gateway for any reason.


    With the code below I could successfully invoke another Chalice Lambda directly (without going through the API Gateway + Authorizer again):

    def invoke_sync(lambda_name: str,
                    http_method: str,
                    path: str,
                    claims: dict,
                    path_parameters: dict = None,
                    http_request_body: str = None,
                    query_string_parameters: dict = None,
                    headers: dict = None):
    
        invoke_payload = {
            'path': path,
            'httpMethod': http_method,
            'headers': headers,
            'multiValueHeaders': {},
            'queryStringParameters': query_string_parameters,
            'multiValueQueryStringParameters': None,
            'pathParameters': path_parameters,
            'stageVariables': None,
            'requestContext': {
                'authorizer': {'claims': claims},
                'path': path,
                'resourcePath': path,
                'httpMethod': http_method,
            },
            'body': http_request_body,
        }
    
        lambda_response = boto3.client('lambda').invoke(FunctionName=lambda_name,
                                                        InvocationType='RequestResponse',
                                                        Payload=json.dumps(invoke_payload))
    
        payload = json.loads(lambda_response['Payload'].read())
        status_code = payload['statusCode']
    
        ...
    

    Usage example:

    invoke_sync(
        lambda_name='Users',
        claims=<claims-in-the-id-token>,
        http_method='GET',
        path='/users/{userId}',
        path_parameters={'userId': 123}
    )
    

    This call is synchronous. To create an asynchronous version of the above, use InvocationType='Event' as documented here.

    Note that the payload used above would be exactly the same if using other languages or frameworks, given this is the format sent from the API Gateway to the Lambda functions.