Lambda and API Gateway with Amazon CDK (Java edition)
by Afanasy Barbarov
The problem: Creating AWS infrastructure with code

One day I realized, that I need to check some ideas using a specific Java library. But from the other side - I don't know Java well. The idea that would help to spend dramatically less time was simple - just to wrap that library in a lambda function and run it when needed.
And in this blog post, I'll tell how to wrap a Java code into an AWS lambda function that runs behind an AWS API Gateway with CORS enabled. So let's try it out.
I will use AWS CDK for the infrastructure provisioning this time. I had some positive experience using it and consider it a good choice for the current problem.
First, you need Java and Maven installed. Homebrew is the best friend for installing software on Mac OS X, so it should be easy to install both. Just a side note - while I was doing it, I had to type brew install twice - mvn command differs from the maven package name, so I did not guess at the first run:).
There is also a great workshop - https://cdkworkshop.com/ to get acquainted with the AWS CDK. I highly recommend spending around an hour and pass it.
So, back to the code:
Create a folder for the lambda and run
cdk init sample-app --language java. AWS CDK sample app creates an AWS CloudFormation template for each stack defined. In this case, it defines an SQS queue, an SNS topic, and subscribes that queue to the topic.Run
cdk synthto generate the CloudFormation template.[Tip] I have different AWS profiles in file
~/.aws/credentials, that looks like
[default]
aws_access_key_id = YOUSHALLNOTPASS
aws_secret_access_key = YOUSHALLNOTPASS
[serverless]
aws_access_key_id = YOUSHALLNOTPASS
aws_secret_access_key = YOUSHALLNOTPASSTo switch to a serverless profile (the one I use to play with AWS infrastructure) I usually run a command export AWS_PROFILE="serverless" in the shell. Maybe there are other 'easy-peasy' approaches, but this works in my case and is fine to me.
The first time you deploy an AWS CDK app into an environment (account/region), you’ll need to install a “bootstrap stack”. This stack includes resources that are needed for the toolkit’s operation. So run
cdk bootstrap.If you check the CloudFormation page in AWS Console, you'll find the new stack.
And we are ready to deploy the stack - first compile the app:
mvn packageand then deploy:
cdk deploy- After that a new stack will be deployed:

The cdkworkshop fits ideally to my case - creating a lambda function behind an API Gateway. I just followed the instructions... I removed all the resources described in the
LambdaTaggerStackclass and tests as well. Don't get me wrong - I like testing, but I don't know Java well and don't want to spend time learning it for this particular problem. So, as I use to say: less code - fewer bugs. After removing the resources - runmvn packageagain. This time we are able to check the difference with the previous CloudFormation template - runcdk diff, to double-check, that resources are removed. Then runcdk deployagain to clean up resources.And here I realized, that something is wrong.. The rest of the workshop is about lambdas written in javascript. I did not check before starting the workshop and believed, that everything is about Java - the CDK, the lambda itself, everything!
But I managed to handle that. As it often is - the tutorials work only when you follow them until the end, which was definitely not my case. So after reading some docs and reviewing code, I've decided to adopt a specific sample from the official AWS documentation, found here - https://github.com/aws-samples/aws-cdk-examples/tree/master/java/api-cors-lambda-crud-dynamodb. The idea of the code is simple - you have one app with two modules -
cdkandlambdaand build them together. and then put the output jar file to the assets and run it as a lambda function. I refactored the code to fit my needs. And of course, won't show, how exactly:) You can check out my github repo https://github.com/abarbarov/lambda-tagger for reference.But that code was not exactly what I wanted. As you remember, I wanted a lambda and an API Gateway. So I cleaned up all the unnecessary logic. I removed usages of DynamoDB and left only two classes - GetAllItems and GetOneItem.
Feel free to grab the code and replace the current one. Run
mvn clean packageandcdk diffafterwards and check the resources. You'll get something like this: (all keys are replaced to AAAAA):
[+] AWS::IAM::Role getOneItemFunction/ServiceRole
getOneItemFunctionServiceRoleAAAAA
[+] AWS::Lambda::Function getOneItemFunction
getOneItemFunctionAAAAA
[+] AWS::IAM::Role getAllItemsFunction/ServiceRole
getAllItemsFunctionServiceRoleAAAAA
[+] AWS::Lambda::Function getAllItemsFunction
getAllItemsFunctionAAAAA
[+] AWS::ApiGateway::RestApi itemsApi
itemsApiAAAAA
[+] AWS::ApiGateway::Deployment
itemsApi/Deployment itemsApiDeploymentAAAAA
[+] AWS::ApiGateway::Stage
itemsApi/DeploymentStage.prod itemsApiDeploymentStageprodAAAAA
[+] AWS::IAM::Role
itemsApi/CloudWatchRole itemsApiCloudWatchRoleAAAAA
[+] AWS::ApiGateway::Account
itemsApi/Account itemsApiAccountAAAAA
[+] AWS::ApiGateway::Resource
itemsApi/Default/items itemsApiitemsAAAAA
[+] AWS::Lambda::Permission
itemsApi/Default/items/GET/ApiPermission.LambdaTaggerStackitems
ApiAAAAA.GET..items itemsApiitemsGETApiPermission
LambdaTaggerStackitemsApiAAAAA
[+] AWS::Lambda::Permission
itemsApi/Default/items/GET/ApiPermission.Test.Lambda
TaggerStackitemsApiAAAAA
.GET..items itemsApiitemsGETApiPermissionTestLambda
TaggerStackitemsApiAAAAAGETitemsAAAAA
[+] AWS::ApiGateway::Method
itemsApi/Default/items/GET itemsApiitemsAAAAA
[+] AWS::ApiGateway::Method
itemsApi/Default/items/OPTIONS itemsApiitemsOPTIONSAAAAA
[+] AWS::ApiGateway::Resource
itemsApi/Default/items/{id} itemsApiitemsidAAAAA
[+] AWS::Lambda::Permission
itemsApi/Default/items/{id}/GET/ApiPermission.
LambdaTaggerStackitemsApiAAAAA.GET..items.{id}
itemsApiitemsidGETApiPermissionLambdaTaggerStackitems
ApiAAAAAGETitemsidAAAAA
[+] AWS::Lambda::Permission
itemsApi/Default/items/{id}/GET/ApiPermission
.Test.LambdaTaggerStackitemsApiAAAAA.GET..items
.{id} itemsApiitemsidGETApiPermissionTestLambdaTagger
StackitemsApiAAAAAGETitemsidAAAAA
[+] AWS::ApiGateway::Method
itemsApi/Default/items/{id}/GET itemsApiitemsidGETAAAAA
[+] AWS::ApiGateway::Method
itemsApi/Default/items/{id}/OPTIONS iwhich is fine and looks like what I wanted. 11. Run cdk deploy to update the stack. After that note the output. There will be a link to the api gateway, something like
LambdaTaggerStack.itemsApiEndpointAAAAA =
https://AAAAA.execute-api.eu-west-1.amazonaws.com/prod/- If you try to GET that URL, you'll get
{"message":"Missing Authentication Token"}That's because AWS returns such message for the wrong URL:) So add items to the end and enjoy.
- If you, like me, tried to oversimplify the code, you might get another message -
{"message": "Internal server error"}. In that case, you'll need to dig into the CloudWatch logs. For me, the error was in using a wrong jar file for the lambda -lambda-tagger-*instead oflambda-*.
Let's summarize: we have a lambda function that returns something, an API Gateway, and CORS configured. Maybe one day I'll add authorization, but now I'll keep it simple.
That's all, folks!