devuplabs.cloud
PreviewLab7 hours

HTTP APIs

Build HTTP APIs with API Gateway, routes, integrations, auth, throttling, stages, and custom domains.

Mental model

API Gateway is a thin, configurable layer between the internet and your backend. It handles TLS, auth, routing, throttling, and request validation, so your Lambda doesn't have to. Every error code API Gateway returns (502, 504, 403) has a specific root cause. Knowing what causes each one is the skill this lab builds.

Prerequisites

0 of 5 checked

IAM and the Two Permission Boundaries

Goal

Establish the two IAM concepts before touching any API. Mixing them up is the #1 source of silent failures in API Gateway.

Estimated time: 30 min


The two IAM concepts

What's happening here

API Gateway involves two completely separate IAM concepts that are easy to confuse:

  • Execution role: the IAM role your Lambda function assumes when it runs. Controls what AWS services Lambda can call (DynamoDB, S3, SQS, etc.). Trust principal: lambda.amazonaws.com.
  • Resource policy: a policy attached directly to your Lambda function that controls *who can invoke it*. When API Gateway calls Lambda, it needs permission here. Without it, Lambda silently rejects the invocation and API Gateway returns a 500 or 502 with no useful detail.

The two-question model:

  1. What can Lambda call? → execution role
  2. Who can call Lambda? → resource policy

When you wire a route to Lambda via the AWS Console, AWS adds the resource policy automatically. When you do it via CLI (as in this lab), you must add it manually. Forgetting this is the single most common source of silent 502 errors in API Gateway.

bash
export AWS_REGION=ap-south-1
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# Create Lambda execution role
cat > /tmp/lambda-trust.json << 'EOF'
{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}
EOF

aws iam create-role \
  --role-name apigw-lab-lambda-role \
  --assume-role-policy-document file:///tmp/lambda-trust.json

aws iam attach-role-policy \
  --role-name apigw-lab-lambda-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

export LAMBDA_ROLE_ARN=$(aws iam get-role \
  --role-name apigw-lab-lambda-role \
  --query 'Role.Arn' --output text)

echo "Lambda execution role: $LAMBDA_ROLE_ARN"
sleep 10  # IAM propagation

Create a base Lambda function

bash
# Lambda that echoes the full event it receives
mkdir -p /tmp/apigw-lab

cat > /tmp/apigw-lab/index.mjs << 'EOF'
export const handler = async (event) => {
  console.log('Event:', JSON.stringify(event, null, 2));
  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      message: 'Hello from Lambda',
      method: event.requestContext?.http?.method,
      path: event.rawPath,
      pathParams: event.pathParameters,
      queryParams: event.queryStringParameters,
      body: event.body
    })
  };
};
EOF

cd /tmp/apigw-lab && zip function.zip index.mjs

export LAMBDA_ARN=$(aws lambda create-function \
  --function-name apigw-lab-handler \
  --runtime nodejs20.x \
  --handler index.handler \
  --role $LAMBDA_ROLE_ARN \
  --zip-file fileb://function.zip \
  --region $AWS_REGION \
  --query 'FunctionArn' --output text)

echo "Lambda ARN: $LAMBDA_ARN"

Checkpoint: aws lambda get-function --function-name apigw-lab-handler --region $AWS_REGION --query 'Configuration.State' returns Active.


Break It

Break 1: Resource policy missing, silent 502

bash
# Create a minimal HTTP API wired to Lambda -- WITHOUT adding the resource policy
export API_ID=$(aws apigatewayv2 create-api \
  --name apigw-lab-test \
  --protocol-type HTTP \
  --region $AWS_REGION \
  --query 'ApiId' --output text)

# Create integration (Lambda proxy)
export INTEGRATION_ID=$(aws apigatewayv2 create-integration \
  --api-id $API_ID \
  --integration-type AWS_PROXY \
  --integration-uri $LAMBDA_ARN \
  --payload-format-version 2.0 \
  --region $AWS_REGION \
  --query 'IntegrationId' --output text)

# Create route
aws apigatewayv2 create-route \
  --api-id $API_ID \
  --route-key 'GET /test' \
  --target "integrations/$INTEGRATION_ID" \
  --region $AWS_REGION

# Create stage with auto-deploy
aws apigatewayv2 create-stage \
  --api-id $API_ID \
  --stage-name '$default' \
  --auto-deploy \
  --region $AWS_REGION

export API_URL="https://$API_ID.execute-api.$AWS_REGION.amazonaws.com"
echo "API URL: $API_URL"

# Call the endpoint WITHOUT resource policy
curl -s $API_URL/test

{"message":"Internal Server Error"} (a 500. Lambda was never invoked. Check CloudWatch logs for apigw-lab-handler) no new log stream. The error is in API Gateway's internal logs, not Lambda's. No helpful message tells you the resource policy is missing.

bash
# Now add the resource policy and retry
aws lambda add-permission \
  --function-name apigw-lab-handler \
  --statement-id apigw-lab-invoke \
  --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-arn "arn:aws:execute-api:$AWS_REGION:$ACCOUNT_ID:$API_ID/*/*/test" \
  --region $AWS_REGION

sleep 3
curl -s $API_URL/test | jq

now returns a 200 with the Lambda response. The only change was adding the resource policy. Same API, same Lambda, same code, different result.

bash
# Clean up the test API
aws apigatewayv2 delete-api --api-id $API_ID --region $AWS_REGION

Break 2: Execution role missing CloudWatch Logs

bash
# Detach the basic execution role
aws iam detach-role-policy \
  --role-name apigw-lab-lambda-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

# Invoke Lambda directly
aws lambda invoke \
  --function-name apigw-lab-handler \
  --payload '{}' \
  --cli-binary-format raw-in-base64-out \
  --region $AWS_REGION /tmp/out.json && cat /tmp/out.json

# Check CloudWatch -- no log group, no streams
aws logs describe-log-groups \
  --log-group-name-prefix /aws/lambda/apigw-lab-handler \
  --region $AWS_REGION

Lambda returns a 200 (it ran), but CloudWatch has no log group. Debugging a Lambda without logs is extremely difficult. Re-attach the policy before continuing.

bash
aws iam attach-role-policy \
  --role-name apigw-lab-lambda-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
sleep 5

Your First HTTP API

Goal

Create a production-shaped HTTP API via CLI. Understand routes, integrations, auto-deploy stages, and the Lambda event/response contract. Ship a working endpoint.

Unlock all 24 AWS services & 291+ lab sessions (~180 hours)

Pricing