How do I run existing Go applications on AWS Lambda - Part 1
Learn how to run Go REST APIs as Lambda functions using the AWS Lambda Go API Proxy.
Broadly speaking, you would need to:
- Split the existing code into multiple Lambda functions.
- Refactor each of them to work with AWS Lambda Go runtime APIs.
net/http
package, gorilla
and echo
framework to understand how they work and deploy them using AWS Serverless Application Model.The code is available in this GitHub repository
net/http
(Go standard library) and other frameworks such as gorilla/mux
, echo
, etc., aws-lambda-go-api-proxy
also declares a core
package that contains utility methods and interfaces to translate API Gateway proxy events into Go's default http.Request
and http.ResponseWriter
objects and allows you to adapt any framework to the AWS Lambda Go runtime.- The API Gateway request is accepted by the Lambda function handler.
- The function handler proxies the request to the adapter implementation corresponding to the framework.
- Finally, the API Gateway proxy response is returned to the client.
gorilla/mux
implements a request router and dispatcher for matching incoming requests to their respective handler. Like http.ServeMux
in the Go standard library, mux.Router
matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. Because gorilla/mux
implements the http.Handler
interface, it is compatible with http.ServeMux
.gorilla/mux
package:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var gorillaLambda *gorillamux.GorillaMuxAdapter
func init() {
r := mux.NewRouter()
r.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(Response{From: "gorilla", Message: time.Now().Format(time.UnixDate)})
})
gorillaLambda = gorillamux.New(r)
}
func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
r, err := gorillaLambda.ProxyWithContext(ctx, *core.NewSwitchableAPIGatewayRequestV1(&req))
return *r.Version1(), err
}
func main() {
lambda.Start(Handler)
}
- In the
init
function:gorillamux.New
function takes in amux.Router
(which has theHTTP
GET
route defined) and returns agorillamux.GorillaMuxAdapter
. - In the
Handler
implementation:- The
Proxy
(orProxyWithContext
) method of thegorillamux.GorillaMuxAdapter
object receives theevents.APIGatewayProxyRequest
, converts it into ahttp.Request
object, and sends it to themux.Router
for routing. - It returns a proxy response object (
events.APIGatewayProxyResponse
) generated from the data written to the response writer (http.ResponseWriter
).
echo
framework:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var echoLambda *echoadapter.EchoLambda
func init() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/ping", func(c echo.Context) error {
return c.JSON(http.StatusOK, Response{From: "echo", Message: time.Now().Format(time.UnixDate)})
})
echoLambda = echoadapter.New(e)
}
func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return echoLambda.ProxyWithContext(ctx, req)
}
func main() {
lambda.Start(Handler)
}
- The
init
function sets up the router (echo.Echo
) and passes it into theechoadapter.New
thar returns aechoadapter.EchoLambda
(the adapter implementation). - In the
Handler
function:- The
ProxyWithContext
method of theechoadapter.EchoLambda
object receives theevents.APIGatewayProxyRequest
object and converts it into anhttp.Request
object and sends it toecho.Echo
for routing. - It returns a proxy response object (
events.APIGatewayProxyResponse
) generated from the data written to the response writer (http.ResponseWriter
).
net/http
also works the same way. Here is the code snippet:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var httpLambda *httpadapter.HandlerAdapter
func init() {
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(Response{From: "net/http", Message: time.Now().Format(time.UnixDate)})
})
httpLambda = httpadapter.New(http.DefaultServeMux)
}
func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return httpLambda.ProxyWithContext(ctx, req)
}
func main() {
lambda.Start(Handler)
}
- To use with the standard library, the
httpadapter.New
function takes in ahttp.Handler
(which has the route defined) and returns ahttpadapter.HandlerAdapter
object. - The
ProxyWithContent
method on thehttpadapter.HandlerAdapter
can then be used as a Lambda handler.
1
2
3
git clone https://github.com/build-on-aws/golang-apis-on-aws-lambda
cd golang-apis-on-aws-lambda
CodeUri
in template.yaml
to gorilla/
(which is the local folder where the code is located).1
2
3
4
5
6
7
8
9
sam build
#expected output
Building codeuri: <path>/lambda-go-api-proxy-getting-started/gorilla runtime: go1.x metadata: {} architecture: x86_64 functions: DemoFunction
Running GoModulesBuilder:Build
Build Succeeded
....
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export STACK_NAME=lambda-go-gorilla
sam deploy --stack-name $STACK_NAME --guided
# response to the prompts
Stack Name [lambda-go-gorilla]: <press enter>
AWS Region [us-east-1]: <enter alternate region or press enter>
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]: n
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: y
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]: n
DemoFunction may not have authorization defined, Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]: y
SAM configuration file [samconfig.toml]: <press enter>
SAM configuration environment [default]: <press enter>
CloudFormation
console to check the deployed stack and associated resources. These include the Lambda function, API Gateway (REST API), IAM role, etc.CloudFormation
Outputs section.1
2
3
4
5
6
7
8
9
10
11
-----------------------------------------------------------------------------------------------------
CloudFormation outputs from deployed stack
--------------------------------------------------------------------------------------------------------
Outputs
--------------------------------------------------------------------------------------------------------
Key APIGWEndpoint
Description API Gateway Endpoint
Value https://whrd2yy3ug.execute-api.us-east-1.amazonaws.com/dev/ping
--------------------------------------------------------------------------------------------------------
Successfully created/updated stack - lambda-go-gorilla in us-east-1
1
2
3
export API_ENDPOINT=<enter the API Gateway endpoint here>
curl $API_ENDPOINT
JSON
response similar to the following:1
2
3
4
{
"from": "gorilla",
"message": "Tue Jun 27 18:10:54 UTC 2023"
}
CodeUri
in template.yaml
to refer to the local folder where the code is located:http-stdlib/
in case ofnet/http
package.echo/
in case ofecho
framework.
1
2
3
4
5
6
sam build
# change the stack name to lambda-go-echo in case of "echo" framework
export STACK_NAME=lambda-go-nethttp
sam deploy --stack-name $STACK_NAME --guided
1
2
3
export API_ENDPOINT=<enter your API endpoint here>
curl $API_ENDPOINT
JSON
response similar to the following:1
2
3
4
{
"from": "net/http",
"message": "Tue Jun 27 18:20:42 UTC 2023"
}
echo
framework, you should get a JSON
response similar to the following (notice the different name in the from
field):1
2
3
4
{
"from": "echo",
"message": "Tue Jun 27 18:30:25 UTC 2023"
}
1
2
3
sam delete --stack-name lambda-go-gorilla
sam delete --stack-name lambda-go-nethttp
sam delete --stack-name lambda-go-echo
gorilla/mux
, echo
and net/http
) specific adapter implementations allow you to run existing Go applications as AWS Lambda functions fronted by an API Gateway. You learned the basic concepts with simple code examples, deployed these functions using AWS SAM CLI and verified it by invoking the API Gateway endpoint.Gin
framework based Go applications as AWS Lambda functions with the help of a simple (yet practical) URL shortener service.Any opinions in this post are those of the individual author and may not reflect the opinions of AWS.