

$ > git clone https://github.com/superluminar-io/check-go.git $ > cd check-go $ > make check 🖖 Go build works 🖖 AWS Session works
$ > git clone \
https://github.com/superluminar-io/boilerplate-go.git
$ > cd boilerplate-go
$ > tree .
.
├── Makefile
├── src
│ └── example
│ ├── handle.go
│ ├── handle_test.go
│ └── main.go
└── template.yml
Create S3 Bucket for artifacts
$ > make configure
Compile Go code
$ > make build
Bundle configuration with binaries
$ > make package
Use CloudFormation to deploy service
$ > make deploy
$ > make configure
aws s3api create-bucket \
--bucket example-url-shortener-artifacts \
--region eu-west-1 \
--create-bucket-configuration LocationConstraint=eu-west-1
$ > make package
aws cloudformation package \
--template-file ./template.yml \
--s3-bucket example-url-shortener-artifacts \
--output-template-file ./dist/stack.yml \
--region eu-west-1
$ > make deploy
aws cloudformation deploy \
--template-file ./dist/stack.yml \
--region eu-west-1 \
--capabilities CAPABILITY_IAM \
--stack-name example-url-shortener-stack \
--force-upload \
--parameter-overrides \
PREFIX=example \
PROJECT=url-shortener
$ > tree . . ├── Makefile ├── src │ └── example │ ├── handle.go │ ├── handle_test.go │ └── main.go └── template.yml
# template.yml
[…]
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
Runtime: go1.x
Handler: dist/handler/example
[…]
// handler.go
type event struct {
ShouldFail bool `json:"should_fail"`
}
func handle(ctx context.Context, e event) (string, error) {
if e.ShouldFail == true {
return '', errors.New("Error")
}
return "Done", nil
}
Change signature for handler
import (
"github.com/aws/aws-lambda-go/events"
)
func handle(
request events.APIGatewayProxyRequest
) (events.APIGatewayProxyResponse, error) {
}
Configure event trigger for handler
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
Runtime: go1.x
Handler: dist/handler/example
Events:
CustomEventName:
Type: Api
Properties:
Path: /example
Method: get
Events:
CustomEventName:
Type: Api
Properties:
Path: /example
Method: get
Events:
CustomEventName:
Type: Api
Properties:
Path: /example/{foo}
Method: post
Update response for HTTP Interface
return events.APIGatewayProxyResponse{
StatusCode: 200,
Body: "Example String",
}, nil
$ > make build package deploy
# template.yml
Outputs:
PREFIX:
Value: !Ref PREFIX
PROJECT:
Value: !Ref PROJECT
# template.yml
Outputs:
Endpoint:
Value: !Sub https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod
$ > make build package deploy
$ > make describe
[
{
"OutputKey": "Endpoint",
"OutputValue": "https://lrrcoo1jn6[…]com/Prod"
}
]
$ > curl https://lrrcoo1jn6[…]com/Prod/example
Example String
$ > curl \
-XPOST -d url=https://zurich.serverlessdays.io \
https://$ENDPOINT
> POST /create-url HTTP/1.1
< HTTP/1.1 Created 201
Created short url: http://$ENDPOINT/$ID
$ > curl -v http://$ENDPOINT/$ID > GET /$ID HTTP/1.1 < HTTP/1.1 302 Found < Location: https://zurich.serverlessdays.io
$ > tree . . ├── Makefile ├── src │ ├── create │ │ ├── handle.go │ │ └── main.go │ ├── example │ │ ├── handle.go │ │ └── main.go │ └── resolve │ ├── handle.go │ └── main.go └── template.yml
Resources:
ResolveFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: go1.x
Handler: dist/handler/resolve
CreateFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: go1.x
Handler: dist/handler/create
[…]
Events:
GetEvent:
Type: Api
Properties:
Path: /
Method: post
[…]
Properties:
Path: /{id}
Method: get
$ > make build package deploy
Resources:
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub ${PREFIX}-url-${PROJECT}
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
AttributeDefinitions:
- AttributeName: id
AttributeType: S
Properties:
Handler: dist/handler/create
Runtime: go1.x
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref DynamoDBTable
Properties:
Handler: dist/handler/resolve
Runtime: go1.x
Policies:
- DynamoDBReadPolicy:
TableName: !Ref DynamoDBTable
[…]
Handler: dist/handler/create
Runtime: go1.x
Environment:
Variables:
DYNAMODB_TABLE_NAME: !Ref DynamoDBTable
body := fmt.Sprintf(
"env variable DYNAMODB_TABLE_NAME is %s",
os.Getenv("DYNAMODB_TABLE_NAME"),
)
return events.APIGatewayProxyResponse{
StatusCode: 200,
Body: body,
}, nil
import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
func handler(request events.APIGatewayProxyRequest) {
table := os.Getenv("DYNAMODB_TABLE_NAME")
id := request.PathParameters["id"]
s := session.Must(session.NewSession())
client := dynamodb.New(s)
[…]
}
result, err := client.GetItem(
&dynamodb.GetItemInput{
TableName: aws.String(table),
Key: map[string]*dynamodb.AttributeValue{
"id": {S: aws.String(id)},
},
},
)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: 500,
Body: "Failed to access data",
}, nil
}
if result.Item == nil {
return events.APIGatewayProxyResponse{
StatusCode: 404,
Body: "Unknown ID",
}, nil
}
return events.APIGatewayProxyResponse{
StatusCode: 302,
Headers: map[string]string{
"Location": *result.Item["url"].S,
},
}, nil
import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
func handler(request events.APIGatewayProxyRequest) {
table := os.Getenv("DYNAMODB_TABLE_NAME")
s := session.Must(session.NewSession())
client := dynamodb.New(s)
[…]
}
var data map[string]string err := json.Unmarshal([]byte(request.Body), &data) // Error Handling url, ok := data["url"] // Error Handling id, err := shorten(url) // Error Handling
func shorten(u string) (string, error) {
if _, err := neturl.ParseRequestURI(u); err != nil {
return "", err
}
hash := fnv.New64a()
if _, err := hash.Write([]byte(u)); err != nil {
return "", err
}
return strconv.FormatUint(hash.Sum64(), 36), nil
}
_, err = h.DynamoDBClient.PutItem(
&dynamodb.PutItemInput{
TableName: aws.String(h.DynamoDBTableName),
Item: map[string]*dynamodb.AttributeValue{
"id": {S: aws.String(id)},
"url": {S: aws.String(url)},
},
},
)
return events.APIGatewayProxyResponse{
StatusCode: 201,
Body: id,
}, nil
$ > make build package deploy $ > # Remember how to get the Endpoint URL ?
$ > curl \
-XPOST -d url=https://zurich.serverlessdays.io \
https://$ENDPOINT
> POST /create-url HTTP/1.1
< HTTP/1.1 Created 201
Created short url: http://$ENDPOINT/$ID
$ > curl -v http://$ENDPOINT/$ID > GET /$ID HTTP/1.1 < HTTP/1.1 302 Found < Location: https://zurich.serverlessdays.io