Define Lambda function handler in Go
The Lambda function handler is the method in your function code that processes events. When your function is invoked, Lambda runs the handler method. Your function runs until the handler returns a response, exits, or times out.
Topics
Go handler basics
A Lambda function written in Gomain()
function.
Example Go Lambda function
package main import ( "context" "fmt" "github.com/aws/aws-lambda-go/lambda" ) type MyEvent struct { Name string `json:"name"` } func HandleRequest(ctx context.Context, event *MyEvent) (*string, error) { if event == nil { return nil, fmt.Errorf("received nil event") } message := fmt.Sprintf("Hello %s!", event.Name) return &message, nil } func main() { lambda.Start(HandleRequest) }
Here is a sample input for this function:
{ "name": "Jane" }
Note the following:
-
package main: In Go, the package containing
func main()
must always be namedmain
. -
import: Use this to include the libraries your Lambda function requires. In this instance, it includes:
-
context: Using the Lambda context object to retrieve Go function information.
-
fmt: The Go Formatting
object used to format the return value of your function. -
github.com/aws/aws-lambda-go/lambda: As mentioned previously, implements the Lambda programming model for Go.
-
-
func HandleRequest(ctx context.Context, event *MyEvent) (*string, error): This is the signature of your Lambda handler. It's the entry point for your Lambda function and contains the logic that is executed when your function is invoked. In addition, the parameters included denote the following:
-
ctx context.Context: Provides runtime information for your Lambda function invocation.
ctx
is the variable you declare to leverage the information available via Using the Lambda context object to retrieve Go function information. -
event *MyEvent: This is a parameter named
event
that points toMyEvent
. It represents the input to the Lambda function. -
*string, error: The handler returns two values. The first is a pointer to a string which contains the result of the Lambda function. The second is an error type, which is
nil
if there's no error and contains standard errorinformation if something goes wrong. -
return &message, nil: Returns two values. The first is a pointer to a string message, which is a greeting constructed using the
Name
field from the input event. The second value,nil
, indicates that the function didn't encounter any errors.
-
-
func main(): The entry point that runs your Lambda function code. This is required.
By adding
lambda.Start(HandleRequest)
betweenfunc main(){}
code brackets, your Lambda function will be executed. Per Go language standards, the opening bracket,{
must be placed directly at the end of themain
function signature.
Naming
- provided.al2 and provided.al2023 runtimes
-
For Go functions that use the
provided.al2
orprovided.al2023
runtime in a .zip deployment package, the executable file that contains your function code must be namedbootstrap
. If you're deploying the function with a .zip file, thebootstrap
file must be at the root of the .zip file. For Go functions that use theprovided.al2
orprovided.al2023
runtime in a container image, you can use any name for the executable file.You can use any name for the handler. To reference the handler value in your code, you can use the
_HANDLER
environment variable. - go1.x runtime
-
For Go functions that use the
go1.x
runtime, the executable file and the handler can share any name. For example, if you set the value of the handler toHandler
, Lambda will call themain()
function in theHandler
executable file.
To change the function handler name in the Lambda console, on the Runtime settings pane, choose Edit.
Lambda function handler using structured types
In the example above, the input type was a simple string. But you can also pass in structured events to your function handler:
package main import ( "fmt" "github.com/aws/aws-lambda-go/lambda" ) type MyEvent struct { Name string `json:"What is your name?"` Age int `json:"How old are you?"` } type MyResponse struct { Message string `json:"Answer"` } func HandleLambdaEvent(event *MyEvent) (*MyResponse, error) { if event == nil { return nil, fmt.Errorf("received nil event") } return &MyResponse{Message: fmt.Sprintf("%s is %d years old!", event.Name, event.Age)}, nil } func main() { lambda.Start(HandleLambdaEvent) }
Here is a sample input for this function:
{ "What is your name?": "Jim", "How old are you?": 33 }
The response looks like this:
{ "Answer": "Jim is 33 years old!" }
To be exported, field names in the event struct must be capitalized. For more information on handling events
from AWS event sources, see aws-lambda-go/events
Valid handler signatures
You have several options when building a Lambda function handler in Go, but you must adhere to the following rules:
-
The handler must be a function.
-
The handler may take between 0 and 2 arguments. If there are two arguments, the first argument must implement
context.Context
. -
The handler may return between 0 and 2 arguments. If there is a single return value, it must implement
error
. If there are two return values, the second value must implementerror
.
The following lists valid handler signatures. TIn
and TOut
represent types
compatible with the encoding/json standard library. For more information, see
func Unmarshal
-
func ()
-
func () error
-
func (TIn) error
-
func () (TOut, error)
-
func (context.Context) error
-
func (context.Context, TIn) error
-
func (context.Context) (TOut, error)
-
func (context.Context, TIn) (TOut, error)
Using global state
You can declare and modify global variables that are independent of your Lambda function's handler code. In
addition, your handler may declare an init
function that is executed when your handler is loaded.
This behaves the same in AWS Lambda as it does in standard Go programs. A single instance of your Lambda function
will never handle multiple events simultaneously.
Example Go function with global variables
Note
This code uses the AWS SDK for Go V2. For more information, see Getting Started with the AWS SDK for Go V2
package main import ( "context" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" "log" ) var invokeCount int var myObjects []types.Object func init() { // Load the SDK configuration cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalf("Unable to load SDK config: %v", err) } // Initialize an S3 client svc := s3.NewFromConfig(cfg) // Define the bucket name as a variable so we can take its address bucketName := "amzn-s3-demo-bucket" input := &s3.ListObjectsV2Input{ Bucket: &bucketName, } // List objects in the bucket result, err := svc.ListObjectsV2(context.TODO(), input) if err != nil { log.Fatalf("Failed to list objects: %v", err) } myObjects = result.Contents } func LambdaHandler(ctx context.Context) (int, error) { invokeCount++ for i, obj := range myObjects { log.Printf("object[%d] size: %d key: %s", i, obj.Size, *obj.Key) } return invokeCount, nil } func main() { lambda.Start(LambdaHandler) }
Code best practices for Go Lambda functions
Adhere to the guidelines in the following list to use best coding practices when building your Lambda functions:
-
Separate the Lambda handler from your core logic. This allows you to make a more unit-testable function.
-
Minimize the complexity of your dependencies. Prefer simpler frameworks that load quickly on execution environment startup.
-
Minimize your deployment package size to its runtime necessities. This will reduce the amount of time that it takes for your deployment package to be downloaded and unpacked ahead of invocation.
-
Take advantage of execution environment reuse to improve the performance of your function. Initialize SDK clients and database connections outside of the function handler, and cache static assets locally in the
/tmp
directory. Subsequent invocations processed by the same instance of your function can reuse these resources. This saves cost by reducing function run time.To avoid potential data leaks across invocations, don’t use the execution environment to store user data, events, or other information with security implications. If your function relies on a mutable state that can’t be stored in memory within the handler, consider creating a separate function or separate versions of a function for each user.
-
Use a keep-alive directive to maintain persistent connections. Lambda purges idle connections over time. Attempting to reuse an idle connection when invoking a function will result in a connection error. To maintain your persistent connection, use the keep-alive directive associated with your runtime. For an example, see Reusing Connections with Keep-Alive in Node.js.
-
Use environment variables to pass operational parameters to your function. For example, if you are writing to an Amazon S3 bucket, instead of hard-coding the bucket name you are writing to, configure the bucket name as an environment variable.
-
Avoid using recursive invocations in your Lambda function, where the function invokes itself or initiates a process that may invoke the function again. This could lead to unintended volume of function invocations and escalated costs. If you see an unintended volume of invocations, set the function reserved concurrency to
0
immediately to throttle all invocations to the function, while you update the code. -
Do not use non-documented, non-public APIs in your Lambda function code. For AWS Lambda managed runtimes, Lambda periodically applies security and functional updates to Lambda's internal APIs. These internal API updates may be backwards-incompatible, leading to unintended consequences such as invocation failures if your function has a dependency on these non-public APIs. See the API reference for a list of publicly available APIs.
-
Write idempotent code. Writing idempotent code for your functions ensures that duplicate events are handled the same way. Your code should properly validate events and gracefully handle duplicate events. For more information, see How do I make my Lambda function idempotent?
.