Building serverless apps is awesome! As a developer you don’t have to worry about provisioning or maintaining servers, and you only have to create the code that you need to power your next business idea! Deploying such apps is made super easy by the team at Serverless Framework.With the Serverless Framework, you can configure which events should trigger it, where to deploy it and what kind of resources it is allowed to use without going into the AWS console.
You’ll need to have the Serverless Framework installed. If you haven’t done that yet, now would be a great time to do so!
To finally deploy your apps you’ll need an AWS account. If you haven’t signed up yet, this would be a great time to do that too.
The first step is to create a sample project based on the Flogo template.
serverless create -u https://github.com/tibcosoftware/flogo/tree/master/serverless -p myservice
That command just generated a bunch of files for you
myservice <-- A new directory with the name of your service
├── hello <-- A folder with the sources of your function
│ ├── function.go <-- A Hello World function
│ └── main.go <-- The Lambda trigger code, created by Flogo
├── .gitignore <-- A .gitignore file to ignore the things you don't want in git
├── Makefile <-- A Makefile to build and deploy even faster
├── README.md <-- A quickstart guide
└── serverless.yaml <-- The Serverless Framework template
The content of main.go
comes directly from the Lambda trigger. The function.go
file has three methods that make up the entire app
shimApp()
and ultimately starts the engineRunActivities
To build the executable that you want to deploy to Lambda, you can run make
or make build
. That command, in turn, executes two other commands:
go generate ./...
: In order to run the activities and triggers that you have uses, the Flogo engine needs to have all the metadata available. This command generates the metadata so that it can be compiled into the executableenv GOOS=linux go build -ldflags="-s -w" -o bin/hello hello/*.go
: This command creates the executable (which should run on Linux) from the sources in the hello folder and stores the result (a file called hello) in the bin folder.To deploy the app run make deploy
or, if you don’t want to use make, run sls deploy --verbose
to get the same result. This command will deploy your function to AWS Lambda.
The output on your screen will have something like what is pasted below:
<snip>
Service Information
service: myservice
stage: dev
region: us-east-1
stack: myservice-dev
api keys:
None
endpoints:
GET - https://xxx.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
hello: myservice-dev-hello
<snip>
Using cURL you can test your new function
curl --request GET --url https://xxx.execute-api.us-east-1.amazonaws.com/dev/hello --header 'content-type: application/json'
The above command will call your function and return a result:
{"message": "Go Serverless v1.x! Your function executed successfully!"}
That was pretty easy, and pretty cool, but not really useful. The next step is all about updating the code to not only handle GET
, but also POST
operations and provide a more personalized response.
The first thing is to update the serverless.yml
file with a new event handler. Around line 58 of the file you’ll find the events and the types of events that can trigger your app. It already has an entry for GET
, copy and paste that and change the method to POST
.
functions:
hello:
handler: bin/hello
events:
- http:
path: hello
method: get
- http:
path: hello
method: post
The second part is to update the RunActivities
method. In this step you’ll walk through the entire method.
In order to distinguish the two HTTP methods, you’ll have to look at the httpMethod
element of the incoming message:
// Get the HTTP method from the event
method := inputs["evt"].Value().(map[string]interface{})["httpMethod"].(string)
// Create a variable for the message
var message string
// Decide which way to take
switch method {
case "GET":
case "POST":
}
Creating the response, so after the switch statement is completed, will still be the same:
// Using a Flogo activity to log the message
in := map[string]interface{}{"message": message}
_, err := flogo.EvalActivity(&log.LogActivity{}, in)
if err != nil {
return nil, err
}
// The response from the Lambda function is always in the form of a JSON message.
// In this case we're creating a structure with a single element called
// message
responseData := make(map[string]interface{})
responseData["message"] = message
// Because we're sending the result back to the API Gateway, it will expect to have
// both an HTTP result code (called code) and some response data. In this case we're
// sending back the data object we created earlier
response := make(map[string]*data.Attribute)
response["code"], _ = data.NewAttribute("code", data.TypeInteger, 200)
response["data"], _ = data.NewAttribute("data", data.TypeAny, responseData)
return response, nil
In case of a GET operation, the message should still be the same. You can update the GET part by pasting in message = "Go Serverless v1.x! Your function executed successfully!"
In case of a POST operation, the message will be a little different. In that case you want to reply with the name of the person that called your function.
// API Gateway passes in the body as a string, so the first step
// is to parse the body to a JON object, or map[string]interface{} in Go
var eventBody map[string]interface{}
if err := json.Unmarshal([]byte(inputs["evt"].Value().(map[string]interface{})["body"].(string)), &eventBody); err != nil {
return nil, err
}
// The message you want to log
message = fmt.Sprintf("%v is going all in on Serverless v1.x!", eventBody["name"])
Putting everything together, the new method will look like
// Get the HTTP method from the event
method := inputs["evt"].Value().(map[string]interface{})["httpMethod"].(string)
// Create a variable for the message
var message string
// Decide which way to take
switch method {
case "GET":
message = "Go Serverless v1.x! Your function executed successfully!"
case "POST":
// API Gateway passes in the body as a string, so the first step
// is to parse the body to a JON object, or map[string]interface{} in Go
var eventBody map[string]interface{}
if err := json.Unmarshal([]byte(inputs["evt"].Value().(map[string]interface{})["body"].(string)), &eventBody); err != nil {
return nil, err
}
// The message you want to log
message = fmt.Sprintf("%v is going all in on Serverless v1.x!", eventBody["name"])
}
// Using a Flogo activity to log the message
in := map[string]interface{}{"message": message}
_, err := flogo.EvalActivity(&log.LogActivity{}, in)
if err != nil {
return nil, err
}
// The response from the Lambda function is always in the form of a JSON message.
// In this case we're creating a structure with a single element called
// message
responseData := make(map[string]interface{})
responseData["message"] = message
// Because we're sending the result back to the API Gateway, it will expect to have
// both an HTTP result code (called code) and some response data. In this case we're
// sending back the data object we created earlier
response := make(map[string]*data.Attribute)
response["code"], _ = data.NewAttribute("code", data.TypeInteger, 200)
response["data"], _ = data.NewAttribute("data", data.TypeAny, responseData)
return response, nil
To build and deploy the updates you can use one command: make deploy
. This command will build the new executable and deploy your function to AWS Lambda.
The output on your screen will have something like what is pasted below:
<snip>
Service Information
service: myservice
stage: dev
region: us-east-1
stack: myservice-dev
api keys:
None
endpoints:
GET - https://xxx.execute-api.us-east-1.amazonaws.com/dev/hello
POST - https://xxx.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
hello: myservice-dev-hello
<snip>
Using cURL you can test that your function still works for a GET operation. The command
curl --request GET --url https://xxx.execute-api.us-east-1.amazonaws.com/dev/hello --header 'content-type: application/json'
Should still respond with:
{"message": "Go Serverless v1.x! Your function executed successfully!"}
When you POST a message, though
curl --request POST --url https://xxx.execute-api.us-east-1.amazonaws.com/dev/hello --header 'content-type: application/json' --data '{"name": "Flogo"}'
The result should be different
{"message": "Flogo is going all in on Serverless v1.x!"}