How to create a CRUD API on Amazon Lambda using Micronaut - amazon-web-services

I want to create a CRUD API using Micronaut and deploy it on AWS Lambda, exposing the different methods with Amazon API Gateway. I could create different Kotlin Projects per endpoint (GET, POST...), one including one function, but that's kind of cumbersome so I'd rather have a single project with all the CRUD functions.
My current application contains two functions: one Supplier (GET) and one Consumer (POST).
Application:
object Application {
#JvmStatic
fun main(args: Array<String>) {
Micronaut.build()
.packages("micronaut.aws.poc")
.mainClass(Application.javaClass)
.start()
}
}
Supplier:
#FunctionBean("micronaut-aws-poc")
class MicronautAwsPocFunction : Supplier<String> {
override fun get(): String {
println("GET")
return "micronaut-aws-poc"
}
}
Consumer:
#FunctionBean("micronaut-aws-poc-post")
class MicronautAwsPocPostFunction : Consumer<String> {
override fun accept(t: String) {
println("POST $t")
}
}
Then, I have created a resource in Amazon API Gateway with one GET and one POST method. The problem is, no matter which one I call, the MicronautAwsPocFunction is always invoked.
Is it possible/recommended to embed many functions in a single jar?
How can I make POST invocations call the MicronautAwsPocPostFunction instead of the MicronautAwsPocFunction?
In case I wanted an additional PUT function, how could I model it?

I tried a different approach, this is how I solved it:
Instead of using functions I changed to Lambda Functions Using AWS API Gateway Proxy. Also take into account this specific aws lambda documentation.
I recreated the project with this command mn create-app micronaut-poc --features aws-api-gateway -l kotlin
Now I have a "normal" REST application with two controllers:
#Controller("/")
class PingController {
#Get("/")
fun index(): String {
return "{\"pong\":true}"
}
}
#Controller("/")
class PongController {
#Post("/")
fun post(): String {
println("PONG!!!!!!!")
return "{\"ping\":true}"
}
}
The magic happens in the AWS API Gateway configuration. We have to configure a proxy resource:
Finally we can invoke the lambda from the API Gateway setting the correct HTTP Method. IMPORTANT: Set a host header, otherwise Micronaut will throw a nullpointerexception:
GET:
POST:

Related

How to build request handler class in lambda function for a Springboot CRUD Application?

I am trying to invoke a lambda function by the new feature - function URL"S https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html using which I don't require any trigger events.
I am able to get the output for a simple java application using the above approach written in documentation by using Java as runtimeEnvironment and passing Jar file from my local machine.
Now, I want to test lambda functionality by same concept but for Springboot-Application which has restful endpoints for GET,POST,PUT,DELETE for different crud operations connecting with MySql db. The problem I am facing is how to tell handler class that to route to these endpoints and trigger lambda functions.
Any help/suggestions appreciated here. Thanks in Advance !
It probably won't work with this URL's feature but it allows handling Lambda events similar to standard Spring web requests.
You can check this one: https://github.com/MelonProjectCom/lambda-http-router
Example usage:
#PathPostMapping("/example/test")
public String example(#Body String body) {
return "Hello World! - body: " + body;
}

Spring Cloud function cannot read function definition from Message header

I've created an AWS Lambda based Spring Cloud Function with multiple functions and it works fine when I define exactly one "spring_cloud_function_definition" environment variable in the AWS Lambda Configuration panel. These functions' input and output is a Message<?>, so the payload and http headers can be accessed runtime as well.
I'd like to use this AWS Lambda function in different API Gateway methods, so for example:
/item [POST] -> saveItem should be called
/item [DELETE] -> deleteItem should be called
... and so on...
https://cloud.spring.io/spring-cloud-static/spring-cloud-function/3.0.0.RC1/reference/html/spring-cloud-function.html#_function_catalog_and_flexible_function_signatures
I found this documentation on Spring.io which says it's feasible:
If the input argument is of type Message<?>, you can communicate routing instruction by setting one of spring.cloud.function.definition or spring.cloud.function.routing-expression Message headers. For more static cases you can use spring.cloud.function.definition header which allows you to provide the name of a single function (e.g., …​definition=foo) or a composition instruction (e.g., …​definition=foo|bar|baz)
So the following test event should run in AWS Lambda without any failure:
{
"method": "POST",
"body": "{ "key" : "value" }",
"headers": {
"spring.cloud.function.definition": "saveItem"
}
}
but of course it throws an exception:
No function defined: java.lang.IllegalStateException
java.lang.IllegalStateException: No function defined
at org.springframework.cloud.function.context.AbstractSpringFunctionAdapterInitializer.apply(AbstractSpringFunctionAdapterInitializer.java:187)
at org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler.handleRequest(SpringBootRequestHandler.java:51)
at org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler.handleRequest(SpringBootApiGatewayRequestHandler.java:140)
at org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler.handleRequest(SpringBootApiGatewayRequestHandler.java:43)
I've tried many configurations (application.properties, AWS Lambda env property, header, etc.) but nothing happened:
spring.cloud.function.routing-expression=headers.function_name
setting spring.cloud.function.definition=saveItem
What could be the problem? What is the best configuration in this case? Any help would be appreciated!
Used technologies:
Cloud: AWS
Cloud components: Lambda + API Gateway
AWS Lambda handler class: org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler
AWS Lambda input and output events: APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent
Java: 8
Spring Cloud Function version: 3.0.6.RELEASE
I've updated to v3.1.3 and switched to FunctionInvoker and I've added the function definition to the request template mapping header, and it worked fine.
Anyways, thanks for the help. :)

Can a lambda that uses vendia serverless-express be a step in a state machine?

I have a lambda setup that uses vendia serveless-express as a handler. It has been working well to serve REST APIs from the single lambda function with multiple routes.
I now have a new requirement, where the same lambda needs to be part of a step function's state machine. But that doesn't seem to be allowed by vendia app, as it always throws the error: "Unable to determine event source based on event" as it expects the event to be api gateway / alb only.
So, based on this, it looks like I will need a separate lambda for step, which makes me have duplicate code in multiple lambdas.
Is it possible for the lambda to handle the inputs from step and still be a vendia express app? Please let me know if I am trying something that doesn't make sense at all.
If I were you I would just implement my own converter that transforms a StepFunction event into an API Gateway event and then calling the express-serverless in your lambda.
The package aws-lambda contains definitions in TypeScript of many AWS events including those ones, then try to generate a mock API Gateway event from your own step function event value. From the sources (4.3.9) we have the function:
function getEventSourceNameBasedOnEvent ({
event
}) {
if (event.requestContext && event.requestContext.elb) return 'AWS_ALB'
if (event.Records) return 'AWS_LAMBDA_EDGE'
if (event.requestContext) {
return event.version === '2.0' ? 'AWS_API_GATEWAY_V2' : 'AWS_API_GATEWAY_V1'
}
throw new Error('Unable to determine event source based on event.')
}
So probably to make it work correctly, you have to define a RequestContext mock value in your mock event and it should be enough.

How can I create a mapping template to map path parameter to lambda function in apigateway?

I am using serverless to deploy API gateway and lambda using lambda integration. Below is the infra code:
myLambda:
...
events:
- http:
path: customer/{id}
method: delete
integration: lambda
request:
parameters:
paths:
id: true
template:
text/plain: "$input.params('id')"
It creates a delete API to delete customer based on the id specified in the path parameter. I'd like to map the path id to myLambda's event parameter in its handler. But the above code doesn't work the lambda receives the whole http request from API gateway. How can I map the id parameter to the event parameter in lambda?
When you are using API Gateway and Lambda functions, you have 2 options:
Lambda proxy
Lambda integration
By default, the Serverless framework is using Lambda proxy. This way of connecting to a Lambda function is a lot quicker. It passes the whole HTTP request to a Lambda function and it lets you handle the request completely on the Lambda function side.
There is another way, as I mentioned, it's Lambda integration, without using Lambda proxy. If you want to use this method, you need to set up mappings on your request and your response. This way is "cleaner" on the LAmbda code side, because Lambda received mapped parameters in the event, compared to the whole HTTP request as in proxy. On the other hand, this kind of integration is more complicated to set up, as you need to make all the mappings, and it uses VTL (Apache Velocity Template Language) to do that.
In order to set this up using Serverless, take a look at the "Lambda Integration" section in Serverless framework API Gateway documentation:
I believe that ApiGateway event itself should contain that query / path parameter key value pair and you can directly query your parameters from the event, if you'd go with Lambda Proxy method which #Caldazar mentioned above.
For eg let's say if you have a URL
https://example.execute-api.us-west-2.amazonaws.com/prod/confirmReg?token=12345&uid=5
you could simply retrieve the values as below from your handler:
token = event['queryStringParameters']['token']
uid = event["queryStringParameters"]['uid']
EDIT:
Please follow the below steps for Lambda Integration:
Go to your respective API resource method and click on Integration Request section
Find the Mapping Templates area of the Integration request and open it up. Add a new mapping template for the application/json Content-Type. You'll need to change the type in the combo box from "Input passthrough" to "Mapping Template".
Then paste the following piece of JSON inside the template:
{
"id" : "$input.params('id')"
}
This way you'll not end up receiving the entire event object inside your lambda but only your path param which is sent in your request.
Some references: 1 & 2

Api gateway get output results from step function?

I followed tutorial on creating and invoking step functions
I'm getting output in my GET request of api as
{
"executionArn": "arn:aws:states:ap-northeast-1:123456789012:execution:HelloWorld:MyExecution",
"startDate": 1.486772644911E9
}
But, instead of above response I want my step functions output, which is given by end state as below.
{
"name":"Hellow World"
}
How to achieve this?
Update: You can now use Express Step Functions for synchronous requests.
AWS Step Functions are asynchronous and do not immediately return their results. API Gateway methods are synchronous and have a maximum timeout of 29 seconds.
To get the function output from a Step Function, you have to add a second method in API Gateway which will call the Step Function with the DescribeExecution action. The API Gateway client will have to call this periodically (poll) until the returned status is no longer "RUNNING".
Here's the DescribeExecution documentation
Use Express Step Functions instead. This type of Step Functions can be called synchronously. Go to your API Gateway and in the Integration Request section make sure you have the StartSyncExecution action:
After that, go a bit lower in the same page to the Mapping Templates:
and include the following template for the application/json Content-Type:
#set($input = $input.json('$'))
{
"input": "$util.escapeJavaScript($input)",
"stateMachineArn": "arn:aws:states:us-east-1:your_aws_account_id:stateMachine:your_step_machine_name"
}
After that, go back to the Method Execution and go to the Integration Response and then to the Mapping Templates section:
And use the following template to have a custom response from your lambda:
#set ($parsedPayload = $util.parseJson($input.json('$.output')))
$parsedPayload
My testing Step Function is like this:
And my Lambda Function code is:
Deploy your API Gateway stage.
Now, if you go to Postman and send a POST request with any json body, now you have a response like this:
New Synchronous Express Workflows for AWS Step Functions is the answer:
https://aws.amazon.com/blogs/compute/new-synchronous-express-workflows-for-aws-step-functions/
Amazon API Gateway now supports integration with Step Functions StartSyncExecution for HTTP APIs:
https://aws.amazon.com/about-aws/whats-new/2020/12/amazon-api-gateway-supports-integration-with-step-functions-startsyncexecution-http-apis/
First of all the step functions executes asynchronously and API Gateway is only capable of invoking the step function (Starting a flow) only.
If you are waiting for the results of a step function invocation from a web application, you can use AWS IOT WebSockets for this. The steps are as follows.
Setup AWS IOT topic with WebSockets.
Configure the API Gateway and Step functions invocation.
From the Web Frontend subscribe to the IOT Topic as a WebSocket listener.
At the last step (And in error steps) in the Step Functions workflow use AWS SDK to trigger the IOT Topic which will broadcast the results to the Web App running in the browser using WebSockets.
For more details on WebSockets with AWS IOT refer the medium article Receiving AWS IoT messages in your browser using websockets.
Expanding on what #MikeD at AWS says, if you're certain that the Step Function won't exceed the 30 second timeout, you could create a lambda that executes the step function and then blocks as it polls for the result. Once it has the result, it can return it.
It is a better idea to have the first call return immediately with the execution id, and then pass that id into a second call to retrieve the result, once it's finished.