How to create an API Gateway with lambda integration using AWS CLI?
Hello Everyone
Welcome to CloudAffaire and this is Debjeet.
In today’s blog post, we will discuss how to create an HTTP API Gateway with lambda integration using AWS CLI with example.
We will first create a lambda function and DynamoDB table that will serve as the backend for your REST API and then create an Amazon HTTP API Gateway that routes your REST API methods to the Lambda function which provides a CRUD (GET, POST/PUT, DELETE) functionality using the DynamoDB table.
Note: This is intended for a demo, do not share the API endpoint to anyone as we have not added any authentication to this public API endpoint and anyone who has or can guess the endpoint URL will be able to interact with the API.
How to create an API Gateway with lambda integration using AWS CLI?
Prerequisites:
AWS CLI installed and configured with proper access. You can use below link to install and configure AWS CLI.
https://cloudaffaire.com/how-to-install-aws-cli/
https://cloudaffaire.com/how-to-configure-aws-cli/
Step 1: Create a DynamoDB table that stores the persistent data for your REST API.
1 2 3 4 5 6 |
## Create a dynamodb table aws dynamodb create-table \ --table-name myapp_dynamodb_table \ --attribute-definitions AttributeName=id,AttributeType=S \ --key-schema AttributeName=id,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 |
Step 2: Create a lambda function that will serve as the backend for your HTTP API endpoint.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
## Create assume role policy definition cat <<'EOF'> myapp_lambda_assume_role_policy.json { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF ## Create IAM role aws iam create-role \ --role-name myapp_lambda_iam_role \ --assume-role-policy-document file://myapp_lambda_assume_role_policy.json ## Add AWS managed policy to the role for access to CloudWatch and DynamoDB aws iam attach-role-policy \ --role-name myapp_lambda_iam_role \ --policy-arn arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess && aws iam attach-role-policy \ --role-name myapp_lambda_iam_role \ --policy-arn arn:aws:iam::aws:policy/CloudWatchFullAccess ## Get the role ARN ACCOUNT_ID=$(aws sts get-caller-identity | jq -r .Account) && IAM_ROLE_ARN=arn:aws:iam::$ACCOUNT_ID:role/myapp_lambda_iam_role && echo $IAM_ROLE_ARN ## Create the lambda function code cat << 'EOF' > index.js const AWS = require("aws-sdk"); const dynamo = new AWS.DynamoDB.DocumentClient(); exports.handler = async (event, context) => { let body; let statusCode = 200; const headers = { "Content-Type": "application/json" }; try { switch (event.routeKey) { case "DELETE /items/{id}": await dynamo .delete({ TableName: "myapp_dynamodb_table", Key: { id: event.pathParameters.id } }) .promise(); body = `Deleted item ${event.pathParameters.id}`; break; case "GET /items/{id}": body = await dynamo .get({ TableName: "myapp_dynamodb_table", Key: { id: event.pathParameters.id } }) .promise(); break; case "GET /items": body = await dynamo.scan({ TableName: "myapp_dynamodb_table" }).promise(); break; case "PUT /items": let requestJSON = JSON.parse(event.body); await dynamo .put({ TableName: "myapp_dynamodb_table", Item: { id: requestJSON.id, price: requestJSON.price, name: requestJSON.name } }) .promise(); body = `Put item ${requestJSON.id}`; break; default: throw new Error(`Unsupported route: "${event.routeKey}"`); } } catch (err) { statusCode = 400; body = err.message; } finally { body = JSON.stringify(body, null, 4); } return { statusCode, body, headers }; }; EOF ## Zip the code zip myapp_lambda_function.zip index.js ## Create a lambda function aws lambda create-function \ --function-name myapp_lambda_function \ --runtime nodejs14.x \ --zip-file fileb://myapp_lambda_function.zip \ --handler index.handler \ --role $IAM_ROLE_ARN ## Get lambda function ARN LAMBDA_ARN=$(aws lambda get-function \ --function-name myapp_lambda_function | jq -r .Configuration.FunctionArn) && echo $LAMBDA_ARN |
Step 3: Create a new AWS HTTP API Gateway using AWS CLI.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
## Create a HTTP API API_ID=$(aws apigatewayv2 create-api \ --name myapp_http_api \ --protocol-type HTTP \ --target $LAMBDA_ARN | jq -r .ApiId) ## Get integration id INTEGRATION_ID=$(aws apigatewayv2 get-integrations \ --api-id $API_ID | jq -r .Items[].IntegrationId) ## Create API routes ROUTE_ID1=$(aws apigatewayv2 create-route \ --api-id $API_ID \ --route-key 'GET /items' \ --target integrations/$INTEGRATION_ID | jq -r .RouteId) && ROUTE_ID2=$(aws apigatewayv2 create-route \ --api-id $API_ID \ --route-key 'GET /items/{id}' \ --target integrations/$INTEGRATION_ID | jq -r .RouteId) && ROUTE_ID3=$(aws apigatewayv2 create-route \ --api-id $API_ID \ --route-key 'PUT /items' \ --target integrations/$INTEGRATION_ID | jq -r .RouteId) && ROUTE_ID4=$(aws apigatewayv2 create-route \ --api-id $API_ID \ --route-key 'DELETE /items/{id}' \ --target integrations/$INTEGRATION_ID | jq -r .RouteId) ## Provide access to API gateway to invoke the lambda function AWS_ACCOUNT_ID=$(aws sts get-caller-identity | jq -r .Account) && AWS_REGION=ap-south-1 && aws lambda add-permission \ --statement-id api-invoke-lambda \ --action lambda:InvokeFunction \ --function-name $LAMBDA_ARN \ --principal apigateway.amazonaws.com \ --source-arn "arn:aws:execute-api:ap-south-1:$AWS_ACCOUNT_ID:$API_ID/*" |
If everything goes right, you should have a new API Gateway endpoint ready to serve your REST API.
Next, we will test our API Gateway with some GET/PUT/DELETE REST API call.
Step 4: Make some API call to AWS API Gateway endpoint.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
## Get the api endpoint URL=$(aws apigatewayv2 get-api \ --api-id $API_ID | jq -r .ApiEndpoint) && echo $URL ## Insert couple of items curl -X "PUT" \ -H "Content-Type: application/json" \ -d "{\"id\": \"00001\", \"price\": 10, \"name\": \"mango\"}" \ $URL/items curl -X "PUT" \ -H "Content-Type: application/json" \ -d "{\"id\": \"00002\", \"price\": 20, \"name\": \"banana\"}" \ $URL/items ## Get all items curl $URL/items ## Get specific item curl $URL/items/00002 ## Delete an item curl -X "DELETE" $URL/items/00001 |
Step 5: Clean-up
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
## Delete routes aws apigatewayv2 delete-route \ --api-id $API_ID \ --route-id $ROUTE_ID1 && aws apigatewayv2 delete-route \ --api-id $API_ID \ --route-id $ROUTE_ID2 && aws apigatewayv2 delete-route \ --api-id $API_ID \ --route-id $ROUTE_ID3 && aws apigatewayv2 delete-route \ --api-id $API_ID \ --route-id $ROUTE_ID4 ## Delete the http api aws apigatewayv2 delete-api \ --api-id $API_ID ## Delete the lambda function aws lambda delete-function \ --function-name myapp_lambda_function ## Delete the IAM role aws iam detach-role-policy \ --role-name myapp_lambda_iam_role \ --policy-arn arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess && aws iam detach-role-policy \ --role-name myapp_lambda_iam_role \ --policy-arn arn:aws:iam::aws:policy/CloudWatchFullAccess && aws iam delete-role \ --role-name myapp_lambda_iam_role ## Delete the dynamodb table aws dynamodb delete-table \ --table-name myapp_dynamodb_table |
Hope you have enjoyed this article. We will explore each of these components in upcoming blogs. Please find below links for additional reference
https://docs.aws.amazon.com/apigateway/index.html
https://cloudaffaire.com/category/devops/rest-api/
it is posible to integrate with SQS directly from apigateway?
is it posible to integrate directly from apigateway to sqs or sns?