Example: Lambda and API Gateway
Relevant source files
The following files were used as context for generating this wiki page:The Terraform AWS provider enables the full lifecycle management of AWS resources, including powerful serverless components like AWS Lambda and Amazon API Gateway. These services can be combined to build scalable, event-driven applications. The provider includes several examples that demonstrate how to provision and configure these services.
This document analyzes the api-gateway-websocket-chat-app and cognito-user-pool examples to illustrate how to build serverless applications. The primary focus is on a real-time chat application using a WebSocket API, backed by Lambda functions and DynamoDB for state management. An additional example shows how Lambda can be integrated with Cognito for user management workflows.
Sources: README.md, examples/api-gateway-websocket-chat-app/main.tf, examples/cognito-user-pool/main.tf
WebSocket Chat Application Architecture
The api-gateway-websocket-chat-app example deploys a simple, serverless chat application. It uses an API Gateway WebSocket API to handle client connections, a DynamoDB table to store connection IDs, and three Lambda functions to manage the connection lifecycle and message broadcasting.
Sources: examples/api-gateway-websocket-chat-app/main.tf:1-5
The following diagram illustrates the high-level architecture of the components and their interactions.
API Gateway v2 (WebSocket)
The core of the application is the aws_apigatewayv2_api resource, configured for the WEBSOCKET protocol. It defines the entry point for clients. The API uses a route_selection_expression of $request.body.message to determine which route to invoke based on the content of the message from the client.
Sources: examples/api-gateway-websocket-chat-app/main.tf:89-93, internal/service/apigatewayv2/README.md
Routes and Integrations
The API defines three key routes to handle the WebSocket lifecycle and custom actions. Each route is connected to a Lambda function via an aws_apigatewayv2_integration of type AWS_PROXY.
| Route Key | Target Lambda Function | Purpose |
|---|---|---|
$connect | OnConnectFunction | Handles new client connections and stores the connectionId in DynamoDB. |
$disconnect | OnDisconnectFunction | Handles client disconnections and removes the connectionId from DynamoDB. |
sendmessage | SendMessageFunction | A custom route that receives messages from a client and broadcasts them to all other connected clients. |
Sources: examples/api-gateway-websocket-chat-app/main.tf:104-160
The relationship between routes, integrations, and Lambda functions is defined as follows:
# examples/api-gateway-websocket-chat-app/main.tf
# OnConnect Route
resource "aws_apigatewayv2_integration" "ConnectIntegrat" {
api_id = aws_apigatewayv2_api.SimpleChatWebSocket.id
integration_type = "AWS_PROXY"
integration_uri = aws_lambda_function.OnConnectFunction.invoke_arn
# ...
}
resource "aws_apigatewayv2_route" "ConnectRoute" {
api_id = aws_apigatewayv2_api.SimpleChatWebSocket.id
route_key = "$connect"
target = "integrations/${aws_apigatewayv2_integration.ConnectIntegrat.id}"
}
Sources: examples/api-gateway-websocket-chat-app/main.tf:104-118
Lambda Functions and Permissions
Three distinct aws_lambda_function resources are defined, one for each route. Each function is configured with a nodejs12.x runtime and is passed the name of the DynamoDB table via an environment variable.
Sources: examples/api-gateway-websocket-chat-app/main.tf:166-180, examples/api-gateway-websocket-chat-app/main.tf:234-248, examples/api-gateway-websocket-chat-app/main.tf:302-316
IAM Roles and Policies
Each Lambda function assumes an IAM Role that grants it the necessary permissions to execute and interact with other AWS services. A common aws_iam_policy named DynamoDBCrudPolicy is created and attached to all three Lambda execution roles, allowing them to perform CRUD operations on the simplechat_connections table.
The SendMessageFunction has an additional policy, SendMessageManageConnectionsPolicy, which grants it the execute-api:ManageConnections permission. This is required to post messages back to connected WebSocket clients.
Sources: examples/api-gateway-websocket-chat-app/main.tf:55-79, examples/api-gateway-websocket-chat-app/main.tf:362-372
This diagram shows the IAM configuration for the SendMessageFunction.
Lambda Permissions
To allow API Gateway to invoke the Lambda functions, an aws_lambda_permission resource is created for each function. This grants the apigateway.amazonaws.com service principal the lambda:InvokeFunction action.
# examples/api-gateway-websocket-chat-app/main.tf:182-186
resource "aws_lambda_permission" "OnConnectPermission" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.OnConnectFunction.function_name
principal = "apigateway.amazonaws.com"
}
Sources: examples/api-gateway-websocket-chat-app/main.tf:182-186
Data Persistence
A single aws_dynamodb_table is used to maintain the state of active WebSocket connections.
| Resource | Attribute | Type | Key Type | Purpose |
|---|---|---|---|---|
aws_dynamodb_table.ConnectionsTable | connectionId | S (String) | HASH | Stores the unique ID for each connected client. |
Sources: examples/api-gateway-websocket-chat-app/main.tf:40-52
Advanced Integration: Cognito User Pool Triggers
The cognito-user-pool example demonstrates another common use case for Lambda: extending the functionality of other AWS services. In this case, aws_lambda_function is used to implement custom logic at various points in a Cognito User Pool's lifecycle.
The lambda_config block within the aws_cognito_user_pool resource allows you to associate Lambda functions with specific triggers.
In the example, a single Lambda function (aws_lambda_function.main) is configured to handle all available triggers, showcasing the flexibility of this integration.
# examples/cognito-user-pool/main.tf:101-113
lambda_config {
create_auth_challenge = aws_lambda_function.main.arn
custom_message = aws_lambda_function.main.arn
define_auth_challenge = aws_lambda_function.main.arn
post_authentication = aws_lambda_function.main.arn
post_confirmation = aws_lambda_function.main.arn
pre_authentication = aws_lambda_function.main.arn
pre_sign_up = aws_lambda_function.main.arn
pre_token_generation = aws_lambda_function.main.arn
user_migration = aws_lambda_function.main.arn
verify_auth_challenge_response = aws_lambda_function.main.arn
}
Sources: examples/cognito-user-pool/main.tf:101-113
Summary
The Terraform AWS provider offers comprehensive resources for building serverless applications. The aws_lambda_function resource is a versatile component that can be integrated with services like API Gateway to handle HTTP requests and WebSocket connections, or with services like Cognito to customize user authentication flows. The provided examples demonstrate how to declare these resources, configure their interactions, and manage the necessary IAM permissions to create a secure and functional serverless architecture.
Sources: internal/service/lambda/README.md, internal/service/apigatewayv2/README.md, examples/api-gateway-websocket-chat-app/main.tf, examples/cognito-user-pool/main.tf