Overview
This lab demonstrates how to build and deploy Lambda-backed HTTP APIs using Amazon API Gateway (HTTP API). Students will create a production-representative Lambda service (Node.js), integrate it with HTTP API routes, configure networking so Lambda can access an EC2-hosted SQL Server in a private subnet, secure credentials with Secrets Manager, and add observability (CloudWatch + X-Ray). The lab also covers IAM roles, CORS, payload format versions, cold-start considerations and deployment examples using AWS CLI / PowerShell.
Prerequisites
Architecture notes
secretsmanager:GetSecretValue).Detailed steps
Example trust policy (lambda assume role):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}
]
}
Attach policies (examples):
AWSLambdaBasicExecutionRole (CloudWatch Logs){
"Version": "2012-10-17",
"Statement": [
{"Effect":"Allow","Action":["secretsmanager:GetSecretValue","secretsmanager:DescribeSecret"],"Resource":"arn:aws:secretsmanager:<region>:<account-id>:secret:<your-secret>"},
{"Effect":"Allow","Action":["ec2:CreateNetworkInterface","ec2:DescribeNetworkInterfaces","ec2:DeleteNetworkInterface"],"Resource":"*"}
]
}
PowerShell / AWS CLI example to create role and attach policy (PowerShell):
# create role
$trust = '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam create-role --role-name LearningHubLambdaRole --assume-role-policy-document $trust
# attach managed policy
aws iam attach-role-policy --role-name LearningHubLambdaRole --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# put inline policy for secrets + ENI
# save the inline JSON to lambda-secrets-policy.json locally then run:
aws iam put-role-policy --role-name LearningHubLambdaRole --policy-name LambdaSecretsPolicy --policy-document file://lambda-secrets-policy.json
Project layout (example):
lambda/ package.json index.js node_modules/
Use a bundler (esbuild/webpack) or npm install --production and zip the folder.
PowerShell example to package:
cd lambda
npm install --production
Compress-Archive -Path * -DestinationPath ..\lambda-package.zip
aws lambda create-function --function-name learninghub-api-handler \
--runtime nodejs18.x --handler index.handler \
--zip-file fileb://lambda-package.zip \
--role arn:aws:iam::<account-id>:role/LearningHubLambdaRole \
--timeout 30 --memory-size 512
If Lambda needs DB access in private subnet, update function configuration to include VPC config (subnet IDs + security group IDs):
aws lambda update-function-configuration --function-name learninghub-api-handler --vpc-config SubnetIds=subnet-aaa,subnet-bbb,SecurityGroupIds=sg-xxxx
Notes on VPC:
Create secret (PowerShell):
aws secretsmanager create-secret --name learninghub/sqlserver --secret-string '{"username":"dbuser","password":"P@ssw0rd","host":"10.0.1.10","port":"1433"}'
Attach IAM permission (see step 1) so Lambda can GetSecretValue for that secret.
Create HTTP API (CLI):
$api=$(aws apigatewayv2 create-api --name learninghub-api --protocol-type HTTP --output json)
$apiId=(ConvertFrom-Json $api).ApiId
# create integration to Lambda
$lambdaArn = "arn:aws:lambda:<region>:<account-id>:function:learninghub-api-handler"
$integration=$(aws apigatewayv2 create-integration --api-id $apiId --integration-type AWS_PROXY --integration-uri $lambdaArn --payload-format-version 2.0 --output json)
$integrationId=(ConvertFrom-Json $integration).IntegrationId
# create a route
aws apigatewayv2 create-route --api-id $apiId --route-key "GET /status" --target "integrations/$integrationId"
# add permission so API Gateway can invoke Lambda
aws lambda add-permission --function-name learninghub-api-handler --statement-id apigw-invoke --action lambda:InvokeFunction --principal apigateway.amazonaws.com --source-arn arn:aws:execute-api:<region>:<account-id>:$apiId/*/*
# deploy
aws apigatewayv2 create-stage --api-id $apiId --stage-name prod --auto-deploy
Notes:
payload-format-version 2.0 for HTTP API; event shape differs from REST API.integration-type AWS_PROXY maps the entire HTTP request to Lambda event.You can enable CORS in console or with CLI using update-api to set CorsConfiguration:
$cors='{"AllowOrigins":["https://learninghub.example.com"],"AllowMethods":["GET","POST","OPTIONS"],"AllowHeaders":["Content-Type","Authorization"],"MaxAge":3600}'
aws apigatewayv2 update-api --api-id $apiId --cors-configuration $cors
PowerShell example:
Invoke-RestMethod -Method Get -Uri "https://$apiId.execute-api.<region>.amazonaws.com/status"
If your route is protected (e.g., by Cognito authorizer) attach token header: -Headers @{ Authorization = "Bearer $token" }.
accessLogSettings in create-stage/update-stage.# delete stage and API
aws apigatewayv2 delete-api --api-id $apiId
# delete lambda
aws lambda delete-function --function-name learninghub-api-handler
# delete role inline policy, detach managed, then delete role
aws iam delete-role-policy --role-name LearningHubLambdaRole --policy-name LambdaSecretsPolicy
aws iam detach-role-policy --role-name LearningHubLambdaRole --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name LearningHubLambdaRole
Appendix: sample minimal Lambda (Node.js)
// index.js
const sql = require('mssql'); // if using mssql package
exports.handler = async (event) => {
// parse request, read secret, connect to SQL Server, run query
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: 'OK', request: event })
};
};
References