SQS and API Gateway can talk directly, bypassing Lambda entirely.
Let’s see how this actually works. Imagine we have a simple API endpoint /send that accepts POST requests. Instead of a Lambda function processing this request, we want API Gateway to take the incoming request body and push it directly onto an SQS queue.
Here’s a sample serverless.yml configuration to achieve this:
service: sqs-direct-integration
provider:
name: aws
runtime: nodejs18.x # Still needed for IAM role creation, even if no function code
region: us-east-1
iam:
role:
statements:
- Effect: "Allow"
Action:
- "sqs:SendMessage"
Resource: !GetAtt MyQueue.Arn
functions:
# No actual function code here, just a placeholder for IAM role generation
# The API Gateway integration will be configured separately.
# If you don't have any functions, you might need a minimal function
# or adjust IAM role creation strategy if your provider doesn't auto-generate.
# For Serverless Framework, this is a common pattern.
dummy:
handler: handler.dummy # This file won't be deployed or executed
resources:
Resources:
MyQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: my-direct-integration-queue
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: SQSDirectIntegrationAPI
ApiGatewayResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref ApiGatewayRestApi
ParentId: !GetAtt ApiGatewayRestApi.RootResourceId
PathPart: send
ApiGatewayMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref ApiGatewayRestApi
ResourceId: !Ref ApiGatewayResource
HttpMethod: POST
AuthorizationType: NONE
Integration:
Type: AWS
IntegrationHttpMethod: POST # The HTTP method for the AWS service integration
Uri: !Join
- ""
- - "arn:aws:apigateway:"
- !Ref AWS::Region
- ":sqs:action/aws-apigateway/send/service/sqs/action/SendMessage" # Custom integration URI
Credentials: !GetAtt ApiGatewayRole.Arn # Role API Gateway assumes to send to SQS
RequestParameters:
integration.request.header.X-Amz-Target: "'SendMessage'" # Needed for SQS action
integration.request.header.X-Amz-Content-Type-X: "'application/x-amz-json-1.0'" # Also needed for SQS action
integration.request.path.QueueUrl: !Ref MyQueue # Dynamically set QueueUrl
integration.request.body.MessageBody: "integration.request.body" # Map request body to MessageBody
ApiGatewayDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref ApiGatewayRestApi
StageName: prod
ApiGatewayRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "apigateway.amazonaws.com"
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: "ApiGatewaySQSSendMessagePolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "sqs:SendMessage"
Resource: !GetAtt MyQueue.Arn
When you deploy this, API Gateway will create a REST API with a /send resource that accepts POST requests. The ApiGatewayMethod resource is where the magic happens.
The Integration block defines how API Gateway connects to other AWS services.
Type: AWStells API Gateway this is an AWS service integration.IntegrationHttpMethod: POSTis the HTTP method API Gateway will use to call the SQS service endpoint.Uriis a special string that tells API Gateway which service action to perform. For SQS SendMessage, it’sarn:aws:apigateway:REGION:sqs:action/aws-apigateway/send/service/sqs/action/SendMessage. This is a built-in API Gateway integration URI.Credentialspoints to an IAM role that API Gateway will assume to perform thesqs:SendMessageaction. This role needssqs:SendMessagepermissions for your specific queue.RequestParametersis crucial. It maps incoming request details to parameters required by the SQSSendMessageAPI.integration.request.header.X-Amz-Target: "'SendMessage'": This header tells SQS which API action to invoke.integration.request.header.X-Amz-Content-Type-X: "'application/x-amz-json-1.0'": Specifies the content type for the JSON payload.integration.request.path.QueueUrl: !Ref MyQueue: This dynamically injects the SQS queue URL into the integration request.integration.request.body.MessageBody: "integration.request.body": This is the core mapping. It takes the entire body of the incoming HTTP POST request and assigns it to theMessageBodyparameter of the SQSSendMessageAPI call.
The ApiGatewayRole is a separate IAM Role that API Gateway assumes. It must have sqs:SendMessage permissions on the target queue. The Provider section in serverless.yml (or explicit IAM resource definition) ensures the necessary IAM policies are attached to the role that API Gateway will use.
After deployment, you can send a POST request to your API Gateway endpoint. For example, using curl:
curl -X POST \
https://<your-api-id>.execute-api.us-east-1.amazonaws.com/prod/send \
-H 'Content-Type: application/json' \
-d '{
"orderId": "12345",
"item": "widget",
"quantity": 10
}'
The serverless.yml above will create the SQS queue (MyQueue), the API Gateway REST API (ApiGatewayRestApi), the /send resource (ApiGatewayResource), the POST method (ApiGatewayMethod), the deployment (ApiGatewayDeployment), and the necessary IAM role for API Gateway (ApiGatewayRole).
The most surprising thing is how the Uri and RequestParameters work together to abstract away the direct SDK call. API Gateway is essentially acting as a proxy that translates HTTP requests into AWS service API calls based on these configurations.
If you inspect the SQS queue (my-direct-integration-queue) in the AWS console, you’ll see a message with the JSON body you sent via curl. There was no Lambda execution involved.
The next thing you’ll likely run into is needing to process these messages. That’s where SQS event source mapping for Lambda or SQS consumers come into play.