├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── doc.go ├── job └── job.go ├── lambdautils ├── aws.go └── env.go ├── service ├── api │ ├── main.go │ └── main_test.go ├── error │ ├── main.go │ └── main_test.go └── worker │ ├── main.go │ ├── main_test.go │ └── testdata │ └── event.json └── template.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /*.zip 2 | /aws-sam-golang-example 3 | /api 4 | /error 5 | /vendor 6 | /worker 7 | /packaged.yaml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - master 5 | 6 | install: 7 | - make install 8 | 9 | script: 10 | - make test 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Chris Pliakas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # These environment variables must be set for deployment to work. 3 | S3_BUCKET := $(S3_BUCKET) 4 | STACK_NAME := $(STACK_NAME) 5 | 6 | # Common values used throughout the Makefile, not intended to be configured. 7 | TEMPLATE = template.yaml 8 | PACKAGED_TEMPLATE = packaged.yaml 9 | 10 | .PHONY: test 11 | test: install 12 | go test -v ./lambdautils 13 | go test -v ./service/api 14 | go test -v ./service/error 15 | go test -v ./service/worker 16 | 17 | .PHONY: clean 18 | clean: 19 | rm -f api error worker $(PACKAGED_TEMPLATE) 20 | 21 | .PHONY: install 22 | install: 23 | go get ./... 24 | 25 | .PHONY: update 26 | update: 27 | go get -u ./... 28 | 29 | api: ./service/api/main.go 30 | go build -o api ./service/api 31 | 32 | error: ./service/error/main.go 33 | go build -o error ./service/error 34 | 35 | worker: ./service/worker/main.go 36 | go build -o worker ./service/worker 37 | 38 | .PHONY: lambda 39 | lambda: 40 | GOOS=linux GOARCH=amd64 $(MAKE) api 41 | GOOS=linux GOARCH=amd64 $(MAKE) error 42 | GOOS=linux GOARCH=amd64 $(MAKE) worker 43 | 44 | .PHONY: build 45 | build: clean lambda 46 | 47 | .PHONY: run 48 | run: build 49 | sam local start-api 50 | 51 | .PHONY: package 52 | package: build 53 | sam package --template-file $(TEMPLATE) --s3-bucket $(S3_BUCKET) --output-template-file $(PACKAGED_TEMPLATE) 54 | 55 | .PHONY: deploy 56 | deploy: package 57 | sam deploy --stack-name $(STACK_NAME) --template-file $(PACKAGED_TEMPLATE) --capabilities CAPABILITY_IAM 58 | 59 | .PHONY: teardown 60 | teardown: 61 | aws cloudformation delete-stack --stack-name $(STACK_NAME) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS SAM Golang Example 2 | 3 | [![Build Status](https://travis-ci.org/cpliakas/aws-sam-golang-example.svg?branch=master)](https://travis-ci.org/cpliakas/aws-sam-golang-example) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/cpliakas/aws-sam-golang-example)](https://goreportcard.com/report/github.com/cpliakas/aws-sam-golang-example) 5 | 6 | An example API and Worker written in Golang using the Amazon Serverless 7 | Application Model (AWS SAM). 8 | 9 | ## Overview 10 | 11 | Go is arguably one of the easiest languages in which to write a RESTful API. 12 | With the addition of [Go support for AWS Lambda](https://aws.amazon.com/blogs/compute/announcing-go-support-for-aws-lambda/) 13 | coupled with the maturity of tooling around the [AWS Serverless Application Model](https://github.com/awslabs/serverless-application-model), 14 | deploying Golang-based APIs to serverless infrastructure is becoming much more 15 | straightforward, too. Thanks to the [APEX Gateway](https://github.com/apex/gateway), 16 | you can even write APIs in a familiar manner without changing how the code is 17 | structured. 18 | 19 | The purpose of this project is to give a slightly more complicated example than 20 | the "hello world" ones provided by Amazon with a toolchain that supports both 21 | local development and deployment to AWS as well as design patterns that 22 | facilitate unit testing. 23 | 24 | ## Prerequisites 25 | 26 | * [An AWS account](https://aws.amazon.com/) 27 | * [Golang](https://golang.org/doc/install) 28 | * [Docker](https://docs.docker.com/install) 29 | * [Node.js](https://nodejs.org/en/download/) 30 | * [AWS Command Line Interface](https://docs.aws.amazon.com/cli/latest/userguide/installing.html) 31 | * [SAM CLI](https://aws.amazon.com/serverless/sam/) 32 | * [jq](https://stedolan.github.io/jq/) (optional) 33 | 34 | ## Installation 35 | 36 | With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain: 37 | 38 | ```sh 39 | go get github.com/cpliakas/aws-sam-golang-example/... 40 | cd $GOPATH/src/github.com/cpliakas/aws-sam-golang-example 41 | ``` 42 | 43 | ## Usage 44 | 45 | ### Run the API Locally 46 | 47 | :warning: Make sure to install all the [Prerequisites](#prerequisites). On Mac 48 | OSX and Windows, ensure that the Docker VM is running. 49 | 50 | ```sh 51 | GOARCH=amd64 GOOS=linux go build -o api ./service/api 52 | sam local start-api 53 | ``` 54 | 55 | or ... 56 | 57 | ```sh 58 | make run 59 | ``` 60 | 61 | You can now consume the API using your tool of choice. [HTTPie](https://httpie.org/) 62 | is pretty awesome. 63 | 64 | ```sh 65 | http localhost:3000/ 66 | ``` 67 | 68 | ``` 69 | HTTP/1.1 200 OK 70 | Content-Length: 28 71 | Content-Type: application/json; charset=utf8 72 | Date: Sat, 03 Feb 2018 20:12:07 GMT 73 | 74 | { 75 | "message": "Hello, world!" 76 | } 77 | ``` 78 | 79 | ### Run the Worker Locally 80 | 81 | *TODO* 82 | 83 | ### Deploy to AWS 84 | 85 | First, set the following environment variables replacing `` and 86 | `` as appropriate: 87 | 88 | ```sh 89 | export S3_BUCKET="" 90 | export STACK_NAME="" 91 | ``` 92 | 93 | Now build, package, and deploy the application: 94 | 95 | ```sh 96 | GOOS=linux GOARCH=amd64 go build -o api ./service/api 97 | GOOS=linux GOARCH=amd64 go build -o error ./service/error 98 | GOOS=linux GOARCH=amd64 go build -o worker ./service/worker 99 | 100 | sam package --template-file template.yaml --s3-bucket $S3_BUCKET --output-template-file packaged.yaml 101 | sam deploy --stack-name $STACK_NAME --template-file packaged.yaml --capabilities CAPABILITY_IAM 102 | ``` 103 | 104 | or ... 105 | 106 | ```sh 107 | make deploy 108 | ``` 109 | 110 | ### Consume the Endpoint 111 | 112 | The API endpoint is captured in the CloudFormation stack's `Endpoint` output 113 | key. Either view the output value via the AWS Management Console, or run the 114 | following command assuming the [jq](https://stedolan.github.io/jq/) tool is 115 | installed: 116 | 117 | ```sh 118 | aws cloudformation describe-stacks --stack-name $STACK_NAME | jq -r '.Stacks[0].Outputs[0].OutputValue' 119 | ``` 120 | 121 | Again, [HTTPie](https://httpie.org/) is a pretty awesome tool. 122 | 123 | ### View AWS Logs 124 | 125 | Run the following command to get the CloudWatch logs for the API. 126 | 127 | ```sh 128 | sam logs -n Api --stack-name $STACK_NAME 129 | ``` 130 | 131 | Replace `Api` with `Worker` or `Error` to get logs for the Lambda functions in 132 | those resources as well. 133 | 134 | :warning: The `sam` tool will throw a nasty stack trace if you try to view the 135 | logs before the Lambda function has been invoked. Only run this command after 136 | you have made requests to the corresponding handlers. 137 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | package doc 2 | -------------------------------------------------------------------------------- /job/job.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "log" 9 | 10 | "github.com/aws/aws-lambda-go/events" 11 | ) 12 | 13 | var jobs map[string]NewJob 14 | 15 | // LogJobName is LogJob's unique identifier. 16 | const LogJobName = "log" 17 | 18 | // Job is the interfce implemented by jobs. 19 | type Job interface { 20 | Do(context.Context) (context.Context, error) 21 | } 22 | 23 | // NewJob is a function definition for functions that return initialized Jobs. 24 | type NewJob = func() Job 25 | 26 | // LogJob is a job that simply prints a log message. 27 | type LogJob struct{} 28 | 29 | // NewLogJob returns a LogJob. 30 | func NewLogJob() Job { 31 | return LogJob{} 32 | } 33 | 34 | // Do implements Job.Do by printing a log message. 35 | func (j LogJob) Do(ctx context.Context) (context.Context, error) { 36 | log.Println("job complete") 37 | return ctx, nil 38 | } 39 | 40 | // Message models a job message sent to the queue. 41 | type Message struct { 42 | Job string `json:"job"` 43 | } 44 | 45 | // NewMessage returns an initialized Message. 46 | func NewMessage(job string) Message { 47 | return Message{Job: job} 48 | } 49 | 50 | // String implementes Stringer by marshaling the message to JSON. 51 | func (m Message) String() string { 52 | b, err := json.Marshal(m) 53 | if err != nil { 54 | panic(fmt.Errorf("error encoding message: %v", err)) 55 | } 56 | return string(b) 57 | } 58 | 59 | // Do parses the SQS message and invokes Job.Do. 60 | func Do(ctx context.Context, message events.SQSMessage) (context.Context, error) { 61 | 62 | var m Message 63 | if err := json.Unmarshal([]byte(message.Body), &m); err != nil { 64 | return ctx, fmt.Errorf("error parsing message: %v", err) 65 | } 66 | if m.Job == "" { 67 | return ctx, errors.New("message invalid: 'job' required") 68 | } 69 | 70 | newJob, ok := jobs[m.Job] 71 | if !ok { 72 | return ctx, fmt.Errorf("job invalid: %q", m.Job) 73 | } 74 | 75 | return newJob().Do(ctx) 76 | } 77 | 78 | func init() { 79 | jobs = make(map[string]NewJob, 1) 80 | jobs[LogJobName] = NewLogJob 81 | } 82 | -------------------------------------------------------------------------------- /lambdautils/aws.go: -------------------------------------------------------------------------------- 1 | package lambdautils 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/aws/aws-sdk-go/aws" 7 | "github.com/aws/aws-sdk-go/service/sqs" 8 | "github.com/aws/aws-sdk-go/service/sqs/sqsiface" 9 | ) 10 | 11 | // SendMessage sends a message to SQS and panics on any errors. 12 | func SendMessage(svc sqsiface.SQSAPI, input *sqs.SendMessageInput) (output *sqs.SendMessageOutput) { 13 | var err error 14 | output, err = svc.SendMessage(input) 15 | if err != nil { 16 | panic(fmt.Errorf("error sending sqs message: %v", err)) 17 | } 18 | return 19 | } 20 | 21 | // DeleteMessage deletes a message from SQS and panics on any errors. 22 | func DeleteMessage(svc sqsiface.SQSAPI, receiptHandle string) (output *sqs.DeleteMessageOutput) { 23 | var err error 24 | 25 | input := &sqs.DeleteMessageInput{ 26 | QueueUrl: aws.String(QueueURL()), 27 | ReceiptHandle: aws.String(receiptHandle), 28 | } 29 | 30 | if output, err = svc.DeleteMessage(input); err != nil { 31 | panic(fmt.Errorf("error deleting sqs message: %v", err)) 32 | } 33 | 34 | return 35 | } 36 | -------------------------------------------------------------------------------- /lambdautils/env.go: -------------------------------------------------------------------------------- 1 | package lambdautils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | // EnvQueueURL is the environment variable that contains the SQS queue URL. 9 | // TODO: Can we use Golang build constraints to change this value? 10 | const EnvQueueURL = "QUEUE_URL" 11 | 12 | // Mustenv ensures an environment variable is set and panics if it is not. 13 | func Mustenv(names ...string) { 14 | for _, name := range names { 15 | if os.Getenv(name) == "" { 16 | panic(fmt.Errorf("missing required environment variable: %v", name)) 17 | } 18 | } 19 | } 20 | 21 | // QueueURL returns the SQS Queue URL set in the environment variable. 22 | func QueueURL() string { 23 | return os.Getenv(EnvQueueURL) 24 | } 25 | -------------------------------------------------------------------------------- /service/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/apex/gateway" 9 | "github.com/aws/aws-sdk-go/aws" 10 | "github.com/aws/aws-sdk-go/aws/session" 11 | "github.com/aws/aws-sdk-go/service/sqs" 12 | "github.com/aws/aws-sdk-go/service/sqs/sqsiface" 13 | "github.com/cpliakas/aws-sam-golang-example/job" 14 | "github.com/cpliakas/aws-sam-golang-example/lambdautils" 15 | ) 16 | 17 | // ContentType contains the Content-Type header sent on all responses. 18 | const ContentType = "application/json; charset=utf8" 19 | 20 | // MessageResponse models a simple message responses. 21 | type MessageResponse struct { 22 | Message string `json:"message"` 23 | } 24 | 25 | // JobResponse models the respose returned by the /job endpoint. 26 | type JobResponse struct { 27 | MessageResponse 28 | JobID string `json:"job_id"` 29 | } 30 | 31 | // WelcomeMessageResponse is the response returned by the / endpoint. 32 | var WelcomeMessageResponse = MessageResponse{"Welcome to the example API!"} 33 | 34 | // JobMessage is the message sent in JobResponse responses. 35 | var JobMessage = "Job sent to queue." 36 | 37 | // RootHandler is a http.HandlerFunc for the / endpoint. 38 | func RootHandler(w http.ResponseWriter, r *http.Request) { 39 | json.NewEncoder(w).Encode(WelcomeMessageResponse) 40 | } 41 | 42 | // JobHandler implements http.Handler for the /job endpoint. 43 | type JobHandler struct { 44 | svc sqsiface.SQSAPI 45 | } 46 | 47 | // JobHandlerFunc returns a http.HandlerFunc for the /job endpoint. 48 | func JobHandlerFunc(svc sqsiface.SQSAPI) http.HandlerFunc { 49 | jh := JobHandler{svc: svc} 50 | return jh.ServeHTTP 51 | } 52 | 53 | // JobHandler implements http.Handler for the /job endpoint. 54 | func (h JobHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 55 | lambdautils.Mustenv(lambdautils.EnvQueueURL) 56 | response := sendJobMessage(h.svc, job.LogJobName) 57 | json.NewEncoder(w).Encode(response) 58 | w.WriteHeader(http.StatusCreated) 59 | } 60 | 61 | // sendJobMessage sends an encoded job.Message to SQS. 62 | func sendJobMessage(svc sqsiface.SQSAPI, name string) (response JobResponse) { 63 | 64 | output := lambdautils.SendMessage(svc, &sqs.SendMessageInput{ 65 | QueueUrl: aws.String(lambdautils.QueueURL()), 66 | MessageBody: aws.String(job.NewMessage(name).String()), 67 | }) 68 | 69 | response = JobResponse{JobID: *output.MessageId} 70 | response.Message = JobMessage 71 | return 72 | } 73 | 74 | // RegisterRoutes registers the API's routes. 75 | func RegisterRoutes() { 76 | sess := session.Must(session.NewSession()) 77 | svc := sqs.New(sess) 78 | 79 | http.Handle("/", h(RootHandler)) 80 | http.Handle("/job", h(JobHandlerFunc(svc))) 81 | } 82 | 83 | // h wraps a http.HandlerFunc and adds common headers. 84 | func h(next http.HandlerFunc) http.Handler { 85 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 86 | w.Header().Set("Content-Type", ContentType) 87 | next.ServeHTTP(w, r) 88 | }) 89 | } 90 | 91 | func main() { 92 | RegisterRoutes() 93 | log.Fatal(gateway.ListenAndServe(":3000", nil)) 94 | } 95 | -------------------------------------------------------------------------------- /service/api/main_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import "testing" 4 | 5 | func TestHandlers(t *testing.T) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /service/error/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/aws/aws-lambda-go/events" 8 | "github.com/aws/aws-lambda-go/lambda" 9 | "github.com/aws/aws-sdk-go/aws/session" 10 | "github.com/aws/aws-sdk-go/service/sqs" 11 | "github.com/aws/aws-sdk-go/service/sqs/sqsiface" 12 | "github.com/cpliakas/aws-sam-golang-example/lambdautils" 13 | ) 14 | 15 | // ErrorHandler handles errors in the dead letter queue. 16 | func ErrorHandler(ctx context.Context, event events.SQSEvent, svc sqsiface.SQSAPI) error { 17 | for _, message := range event.Records { 18 | 19 | // Handle the error here. 20 | log.Println("error handled:", message.MessageId) 21 | 22 | // Delete the message from the dead letter queue. 23 | lambdautils.DeleteMessage(svc, message.ReceiptHandle) 24 | } 25 | return nil 26 | } 27 | 28 | func handler(ctx context.Context, event events.SQSEvent) error { 29 | sess := session.Must(session.NewSession()) 30 | svc := sqs.New(sess) 31 | return ErrorHandler(ctx, event, svc) 32 | } 33 | 34 | func main() { 35 | lambda.Start(handler) 36 | } 37 | -------------------------------------------------------------------------------- /service/error/main_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import "testing" 4 | 5 | func TestHandlers(t *testing.T) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /service/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-lambda-go/events" 7 | "github.com/aws/aws-lambda-go/lambda" 8 | "github.com/aws/aws-sdk-go/aws/session" 9 | "github.com/aws/aws-sdk-go/service/sqs" 10 | "github.com/aws/aws-sdk-go/service/sqs/sqsiface" 11 | "github.com/cpliakas/aws-sam-golang-example/job" 12 | "github.com/cpliakas/aws-sam-golang-example/lambdautils" 13 | ) 14 | 15 | // Worker consumes the messages and executes the job. 16 | func Worker(ctx context.Context, event events.SQSEvent, svc sqsiface.SQSAPI) error { 17 | var err error 18 | 19 | for _, message := range event.Records { 20 | 21 | // Do the "work" here. 22 | if ctx, err = job.Do(ctx, message); err != nil { 23 | return err 24 | } 25 | 26 | // Delete the message from SQS so it is not processed again. 27 | lambdautils.DeleteMessage(svc, message.ReceiptHandle) 28 | } 29 | 30 | return nil 31 | } 32 | 33 | func handler(ctx context.Context, event events.SQSEvent) error { 34 | sess := session.Must(session.NewSession()) 35 | svc := sqs.New(sess) 36 | return Worker(ctx, event, svc) 37 | } 38 | 39 | func main() { 40 | lambda.Start(handler) 41 | } 42 | -------------------------------------------------------------------------------- /service/worker/main_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import "testing" 4 | 5 | func TestHandlers(t *testing.T) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /service/worker/testdata/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "Records": [ 3 | { 4 | "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", 5 | "receiptHandle": "MessageReceiptHandle", 6 | "body": "test event", 7 | "attributes": { 8 | "ApproximateReceiveCount": "1", 9 | "SentTimestamp": "1523232000000", 10 | "SenderId": "123456789012", 11 | "ApproximateFirstReceiveTimestamp": "1523232000001" 12 | }, 13 | "messageAttributes": {}, 14 | "md5OfBody": "7b270e59b47ff90a553787216d55d91d", 15 | "eventSource": "aws:sqs", 16 | "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", 17 | "awsRegion": "us-east-1" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion : '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | 4 | Description: An example serverless API and worker written in Golang. 5 | 6 | Resources: 7 | 8 | Queue: 9 | Type: AWS::SQS::Queue 10 | Properties: 11 | VisibilityTimeout: 20 12 | RedrivePolicy: 13 | deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn 14 | maxReceiveCount: 3 15 | 16 | DeadLetterQueue: 17 | Type: AWS::SQS::Queue 18 | Properties: 19 | VisibilityTimeout: 15 20 | 21 | Api: 22 | Type: AWS::Serverless::Function 23 | Properties: 24 | Runtime: go1.x 25 | Handler: api 26 | Timeout: 10 27 | Policies: 28 | - SQSSendMessagePolicy: 29 | QueueName: !GetAtt Queue.QueueName 30 | Environment: 31 | Variables: 32 | QUEUE_URL: !Ref Queue 33 | Events: 34 | RootHandler: 35 | Type: Api 36 | Properties: 37 | Path: '/' 38 | Method: get 39 | HelloHandler: 40 | Type: Api 41 | Properties: 42 | Path: '/job' 43 | Method: post 44 | 45 | Worker: 46 | Type: AWS::Serverless::Function 47 | Properties: 48 | Runtime: go1.x 49 | Handler: worker 50 | Timeout: 15 51 | Policies: 52 | - SQSPollerPolicy: 53 | QueueName: !GetAtt Queue.QueueName 54 | Environment: 55 | Variables: 56 | QUEUE_URL: !Ref Queue 57 | Events: 58 | QueueEvent: 59 | Type: SQS 60 | Properties: 61 | Queue: !GetAtt Queue.Arn 62 | 63 | Error: 64 | Type: AWS::Serverless::Function 65 | Properties: 66 | Runtime: go1.x 67 | Handler: error 68 | Timeout: 15 69 | Policies: 70 | - SQSPollerPolicy: 71 | QueueName: !GetAtt DeadLetterQueue.QueueName 72 | Environment: 73 | Variables: 74 | QUEUE_URL: !Ref DeadLetterQueue 75 | Events: 76 | QueueEvent: 77 | Type: SQS 78 | Properties: 79 | Queue: !GetAtt DeadLetterQueue.Arn 80 | 81 | Outputs: 82 | Endpoint: 83 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" 84 | QueueUrl: 85 | Value: !Ref Queue 86 | DeadLetterQueueUrl: 87 | Value: !Ref DeadLetterQueue --------------------------------------------------------------------------------