This adventure started when I decided to develop a lambda in Go with SAM just to go through the development process and workflow, especially compared to the experience with Python.

I don’t know if you are like me, but I really dislike hitting the up arrow in my IDE terminal over and over when coding to rebuild a project or to launch something repeatedly.

Here is my experience navigating through this annoyance to avoid this rebuild/refresh cycle and the solution that I implemented.


Initiating the Project

I ended up initiating my SAM project with no options to just to see what options were available:

1
sam init

After a few choices, I ended up with the following project configuration/options:

Details
-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: go (provided.al2023)
Architectures: x86_64
Dependency Manager: mod
Application Template: hello-world
Output Directory: .
Configuration file: sam-app/samconfig.toml

Next steps can be found in the README file at sam-app/README.md

Updating the Architecture

I wanted to change the architecture to arm64 so I replaced x86_64 with arm64 in the template.yaml file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Metadata:
      BuildMethod: go1.x
    Properties:
      CodeUri: hello-world/
      Handler: bootstrap
      Runtime: provided.al2023
      Architectures:
        - arm64 #updated from x86_64

Local Development

All right, I am ready to get going on developing, so to speak..

1
sam local start-api

After SAM starts the API, I needed to rebuild my binary before invoking my lambda locally or I have to restart the SAM API server.

Details

Here is the rebuild workflow after making any code changes..

CTRL + C
sam build
sam local start-api

Solution with Nodemon

I abandoned working on the lambda at this point and wanted to improve my developer experience while working on the lambda.

This is where nodemon watching for changes in the workspace comes in.

{{ highlight bash “linenos=table” >}} npm install -g nodemon {{ }}

With nodeman, any changes where the application is watching will execute a command with the following:

1
nodemon --watch hello-world/ --ext go --delay 500ms --exec "COMMAND_HERE"


Adding a Bash Script

I made a basic shell script that nodemon will launch on file changes that kills the process for SAM API, rebuilds the binary file, and restart the SAM API server. I added an option for builds as well, incase I even want to run sam local invoke HelloWorldFunction

#!/usr/bin/env bash
if [[ "$1" == "api" ]]; then
    echo -e "Restarting API..."

    # Kill any existing sam local start-api processes
    pkill -f "sam local start-api"

    # Wait for all sam local start-api processes to terminate
    while pgrep -f "sam local start-api" > /dev/null; do
        echo "Waiting for sam local start-api to fully stop..."
        sleep 0.5
    done

    echo "sam local start-api stopped."

    # Build project that is in the hello-world directory
    GOARCH=arm64 GOOS=linux go -C hello-world build -o bootstrap

    # Start the local API
    sam local start-api

elif [[ "$1" == "build" ]]; then
    echo -e "Building bootstrap.."
    # create new build
    make build

else
    echo "Usage: $0 {api|build}"
    exit 1
fi

Putting it all together

1
nodemon --watch hello-world/ --ext go --delay 500ms --exec "./watch.sh api"

At this point, I was really happy with the progress and solution.

The only other thing I wanted to add was adding commands in the projects Makefile for not having to find this command in the history when working on this project.

Here is my Makefile after adding deploy, delete, and nodemon options

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
.PHONY: build

build:
	sam build

deploy:
	sam deploy

delete:
	sam delete

watch-api:
	echo "Watching API..."; \
	nodemon --watch hello-world/ --ext go --delay 500ms --exec "./watch.sh api"

watch-build:
	echo "Watching Build..."; \
	nodemon --watch hello-world/ --ext go --delay 500ms --exec "./watch.sh build"

I am now able to run make watch-api and my lambda local dev environment experience has improved greatly with auto refreshing builds.


Template on GitHub

I have also added the example here into a template repository that anyone can clone or deploy to your own repo if you are interested - https://github.com/mjgard/go-lambda-arm64-template