$ > 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
Configure cloud9
$ > make cloud9
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 […]
// handle.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://talks.superluminar.io"}' \ https://$ENDPOINT > POST / HTTP/1.1 < HTTP/1.1 Created 201 Created short url: https://://$ENDPOINT/$ID
$ > curl -v https://$ENDPOINT/$ID > GET /$ID HTTP/1.1 < HTTP/1.1 302 Found < Location: https://talks.superluminar.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
ResolveFunction: [...] Events: ResolveEvent: Type: Api Properties: Path: /{id} Method: get CreateFunction: [...] Events: CreateEvent: Type: Api Properties: Path: / Method: post
$ > make build package deploy
Resources: DynamoDBTable: Type: AWS::Serverless::SimpleTable Properties: TableName: !Sub ${PREFIX}-${PROJECT}
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
// resolve/handle.go import ( "github.com/aws/aws-sdk-go/aws" "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 ( "encoding/json" "fmt" "hash/fnv" "net/url" "os" "strconv" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-sdk-go/aws" "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 := url.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 = client.PutItem( &dynamodb.PutItemInput{ TableName: aws.String(table), Item: map[string]*dynamodb.AttributeValue{ "id": {S: aws.String(id)}, "url": {S: aws.String(url)}, }, }, )
body := fmt.Sprintf("Created short url: %s/%s/%s", request.Headers["Host"], "Prod", id) return events.APIGatewayProxyResponse{ StatusCode: 201, Body: body, }, nil
$ > make build package deploy $ > # Remember how to get the Endpoint URL ?
$ > curl \ -XPOST -d '{"url":"https://talks.superluminar.io"}' \ https://$ENDPOINT > POST / 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://talks.superluminar.io