Thursday, August 4, 2022

Serverless React, AWS Lambda and API Gateway example

Here is small terraform example how you can create serverless React app that will use AWS API Gateway which will call AWS Lambda

API Gateway is proxying GET request to lambda. Lambda will simply return "Hello World". 


Actual terraform infrastructure definition. This terraform definition include CORS configuration.

locals {
function_name = "hello_world"
handler = "index.handler"
runtime = "nodejs14.x"
zip_file = "hello_world.zip"
}
data "archive_file" "zip" {
source_dir = "${path.module}/lambdas/hello-world"
type = "zip"
output_path = local.zip_file
}
resource "aws_lambda_function" "this" {
description = "${var.config.team}-lambda-stream-es"
// Function parameters we defined at the beginning
function_name = local.function_name
handler = local.handler
runtime = local.runtime
timeout = 15
// Upload the .zip file Terraform created to AWS
filename = local.zip_file
source_code_hash = data.archive_file.zip.output_base64sha256
// Connect our IAM resource to our lambda function in AWS
role = var.config.es_lambda_role_arn
}
resource "aws_apigatewayv2_api" "this" {
name = "${var.config.team}-${var.config.env}-lambda-gw"
protocol_type = "HTTP"
cors_configuration {
allow_origins = ["https://www.first.com", "https://www.second.com"]
allow_methods = ["GET"]
allow_headers = ["content-type"]
max_age = 300
}
}
resource "aws_apigatewayv2_stage" "this" {
api_id = aws_apigatewayv2_api.this.id
name = "${var.config.team}-${var.config.env}-lambda-gw-stage"
auto_deploy = true
access_log_settings {
destination_arn = aws_cloudwatch_log_group.this.arn
format = jsonencode({
requestId = "$context.requestId"
sourceIp = "$context.identity.sourceIp"
requestTime = "$context.requestTime"
protocol = "$context.protocol"
httpMethod = "$context.httpMethod"
resourcePath = "$context.resourcePath"
routeKey = "$context.routeKey"
status = "$context.status"
responseLength = "$context.responseLength"
integrationErrorMessage = "$context.integrationErrorMessage"
}
)
}
}
resource "aws_apigatewayv2_integration" "hello_world_integration" {
api_id = aws_apigatewayv2_api.this.id
integration_uri = aws_lambda_function.this.invoke_arn
integration_type = "AWS_PROXY"
integration_method = "POST"
}
resource "aws_apigatewayv2_route" "hello_world_route" {
api_id = aws_apigatewayv2_api.this.id
route_key = "GET /hello-world"
target = "integrations/${aws_apigatewayv2_integration.hello_world_integration.id}"
}
resource "aws_cloudwatch_log_group" "this" {
name = "/aws/api_gw/${aws_apigatewayv2_api.this.name}"
retention_in_days = 30
}
resource "aws_lambda_permission" "api_gw" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.this.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/*"
}
view raw serverless.tf hosted with ❤ by GitHub


Lambda which that will return "Hello World".

// AWS lambda that call ECS service to get similar products
module.exports.handler = async (event) => {
console.log('Event: ', event);
let responseMessage = 'Hello, World!';
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: responseMessage,
}),
}
}
view raw hello.js hosted with ❤ by GitHub