Search code examples
amazon-web-serviceslambdaaws-lambda

AWS Api Gw, Sqs, Lambda


Is there a way where I can have a set-up of API Gateway -> SQS -> Lambda? Having Lambda return an API Gateway response to the client-side. I already have API Gateway -> SQS. The Lambda function will be triggered if there is a queue in SQS. The only problem is, how can I have my Lambda return an API Gateway response to the client-side?

SQS

const sqs = new sqs.Queue(this, 'sqs', {
   visibilityTimeout: cdk.Duration.seconds(60),
   queueName: 'sqs',
 });
sqs.grantSendMessages(customRole);

const sqsIntegration = new apigw.AwsIntegration({
   service: 'sqs',
   integrationHttpMethod: "POST",
   path: `${cdk.Aws.ACCOUNT_ID}/${sqs.queueName}`,
   options: {
     credentialsRole: customRole,
     passthroughBehavior: apigw.PassthroughBehavior.NEVER,
     integrationResponses: [def.IntegrationResponse(200)],
     requestParameters: {
       'integration.request.header.Content-Type': `'application/x-www-form-urlencoded'`,
     },
     requestTemplates: {
       "application/json": `Action=SendMessage&MessageBody=\{\"route\"\: \"$input.params('route')\", \"seat\"\: \"$input.params('seat')\", \"body\"\: \"$util.parseJson($input.body)\"\}`
     }
   }
});

Lambda

const lambdaFn = new lambda.Function(this, 'lambdaFn', {
   functionName: 'lambdaFn',
   handler: 'lambdaFn',
   memorySize: 1024,
   role: customRole,
   runtime: lambda.Runtime.GO_1_X,
   timeout: cdk.Duration.seconds(60),
   code: lambda.Code.fromAsset('cmd/lambdaFn')
});

lambdaFn.addEventSource(new SqsEventSource(sqs, {
   enabled: true
}));

API GW Method

apigw.addMethod('POST', sqsIntegration, {
   requestParameters: {
     'method.request.querystring.seat': true,
     'method.request.querystring.route': true
   },
   methodResponses: [
     def.MethodResponse(200, ApiResponseModel),
     def.MethodResponse(400, ApiResponseModel),
     def.MethodResponse(415, ApiResponseModel),
   ],
   requestValidatorOptions: {
     validateRequestBody: false,
     validateRequestParameters: true,
     requestValidatorName: 'api-sqs-validator',
   }
});

Solution

  • No, it is not possible.

    The API Gateway integrates with a ton of services.
    And when it does that it integrates using HTTP.
    HTTP integrations are stateful, in the sense that it sends a request and wait for a response.

    Your current implementation sends a request to API Gateway.
    When API Gtw receives the request, it accepts the connection from the client.
    It keeps the connection open while it creates a new connection between API Gtw and SQS.

    When SQS sends back a response, API Gtw closes the connection with SQS, sends the client the configured response, and closes the connection with the client.

    Later in time, Lambda creates a new connection with SQS and read the available messages.
    At this moment, the connection between the client and the API Gtw does not exist anymore.
    The only way to make it work would be for the Lambda Function to create a new connection between the function and the client.

    But you're missing something here.
    You can not connect the Lambda that reads from SQS to your client because you're using an asynchronous pattern.
    And this pattern was designed to avoid your client have a direct connection to your running code (Lambda Function).

    To solve your issue, you can modify your implementation to use a synchronous pattern (Client -> API Gtw -> Lambda -> API Gtw -> Client) or you can use one of the multiple choices to get the response, like for example, the Asynchronous Request-Reply Pattern.