Reputation: 6914
Typically, a AWS Lambda event handler code in Go (using Serverless Framework) is coded as:
package main
import (
"fmt"
"context"
"github.com/aws/aws-lambda-go/lambda"
)
type MyEvent struct {
Name string `json:"name"`
}
func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
return fmt.Sprintf("Hello %s!", name.Name ), nil
}
func main() {
lambda.Start(HandleRequest)
}
The serverless.yml
file then contains a section like:
skeleton-go-get:
name: skeleton-go-get
runtime: go1.x
handler: go-handler # <- This specifies a file, not a function.
events:
- http:
path: skeleton/go
method: get
That ^ creates one request handler... but now I want my one Go script / program to contain the event handlers for both HTTP GET and POST requests, and not use one Go program file per serverless function.
Exactly this is possible in languages like Node.js, Ruby, Python, with the serverless.yml
specifying which function in the handler file is to be used for which serverless function. For example (for Python functions):
[...]
functions:
skeleton-python-get:
name: skeleton-python-get
handler: python-handler.handle_get # <- Specifies the HTTP GET handler.
events:
- http:
path: skeleton/python
method: get
skeleton-python-post:
name: skeleton-python-post
handler: python-handler.handle_post # <- Specifies the HTTP POST handler.
events:
- http:
path: skeleton/python
method: post
I cannot get this same trick to work for Go. I tried to include the proper request in main()
but to no avail:
func HandleGetRequest(ctx context.Context, name MyEvent) (string, error) {
return fmt.Sprintf("Hello %s!", name.Name ), nil
}
func HandlePostRequest(ctx context.Context, name MyEvent) (string, error) {
return fmt.Sprintf("Hello %s!", name.Name ), nil
}
func main() {
lambda.Start(HandleGetRequest)
lambda.Start(HandlePostRequest) // <- Attempt to add another handler.
}
And specifying multiple event handler functions in the serverless.yml
file for the Go handlers also doesn't work: the function isn't a valid part of the handler declaration.
skeleton-go-get:
name: skeleton-go-get
runtime: go1.x
handler: go-handler.HandleGet # <- Attempt to specify a function.
events:
- http:
path: skeleton/go
method: get
skeleton-go-post:
name: skeleton-go-post
runtime: go1.x
handler: go-handler.HandlePost # <- Attempt to specify a function.
events:
- http:
path: skeleton/go
method: post
Q: How can I include more than one AWS Lambda event handler in one Go program (using Serverless Framework)?
Upvotes: 4
Views: 4472
Reputation: 29
You shouldn't need to build yourself a Go Server in Lambda as you already have API Gateway served to you from serverless framework...
I used AWS CloudFormation + SAM, and I used HTTP API Gateway (Not REST) but it should function in a similar way...
First of all... you need to make it into 1 Lambda functions for 2 events like this:
skeleton-go-get:
name: skeleton-go-get
runtime: go1.x
handler: go-handler # <- This specifies a file, not a function.
events:
- http:
path: skeleton/go
method: get
- http:
path: skeleton/go
method: post
Inside your lambda, you should have:
package main
import ...
func getSkeleton(event events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error) {
// Return APIGateway Response
}
func postSkeleton(event events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error) {
// Return APIGateway Response
}
func handler(_ context.Context, event events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error) {
// Log Events
eventJson, _ := json.Marshal(event)
log.Printf("EVENT: %s", string(eventJson))
switch event.RouteKey {
case "GET /skeleton/go":
return getSkeleton(event)
case "POST /skeleton/go":
return postSkeleton(event)
default:
return events.APIGatewayV2HTTPResponse{
StatusCode: 400
}, nil
}
}
func main() {
lambda.Start(handler)
}
Upvotes: 0
Reputation: 2085
You can use the same function (and handler) for your get
and your post
:
skeleton-go:
name: skeleton-go
runtime: go1.x
handler: go-handler
events:
- http:
path: skeleton/go
method: get
- http:
path: skeleton/go
method: post
Use Go's built-in HTTP router or use a third-party one, such as Gorilla Mux or Chi as shown in the example code below (because that's what I had handy). In essence, you're building a Go HTTP server, but in Lambda. So, follow the details for setting up a Go web server, and take a look at AWS's API Gateway Proxy.
package main
import (
"context"
"net/http"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/go-chi/chi"
chiproxy "github.com/awslabs/aws-lambda-go-api-proxy/chi"
)
var adapter *chiproxy.ChiLambda
func GetSkeleton(w http.ResponseWriter, r *http.Request) {
...
}
func PostSkeletonToMom(w http.ResponseWriter, r *http.Request) {
...
}
func init() {
r := chi.NewRouter()
r.Get("/skeleton/go", GetSkeleton)
r.Post("/skeleton/go", PostSkeletonToMom)
adapter = chiproxy.New(r)
}
func lambdaHandler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
c, err := adapter.ProxyWithContext(ctx, req)
return c, err
}
func main() {
lambda.Start(lambdaHandler)
}
Upvotes: 4