github.com/mweagle/Sparta@v1.15.0/docs_source/content/reference/apigateway/echo_event.md (about) 1 --- 2 date: 2016-03-09T19:56:50+01:00 3 title: Echo 4 weight: 10 5 --- 6 7 To start, we'll create a HTTPS accessible lambda function that simply echoes back the contents of incoming API Gateway 8 Lambda event. The source for this is the [SpartaHTML](https://github.com/mweagle/SpartaHTML). 9 10 For reference, the `helloWorld` function is below. 11 12 ```go 13 import ( 14 awsLambdaEvents "github.com/aws/aws-lambda-go/events" 15 spartaAPIGateway "github.com/mweagle/Sparta/aws/apigateway" 16 ) 17 18 func helloWorld(ctx context.Context, 19 gatewayEvent spartaAWSEvents.APIGatewayRequest) (*spartaAPIGateway.Response, error) { 20 logger, loggerOk := ctx.Value(sparta.ContextKeyLogger).(*logrus.Logger) 21 if loggerOk { 22 logger.Info("Hello world structured log message") 23 } 24 // Return a message, together with the incoming input... 25 return spartaAPIGateway.NewResponse(http.StatusOK, &helloWorldResponse{ 26 Message: fmt.Sprintf("Hello world 🌏"), 27 Request: gatewayEvent, 28 }), nil 29 } 30 ``` 31 32 ## API Gateway 33 34 The first requirement is to create a new [API](https://godoc.org/github.com/mweagle/Sparta#API) instance via [sparta.NewAPIGateway()](https://godoc.org/github.com/mweagle/Sparta#NewAPIGateway). 35 36 ```go 37 stage := sparta.NewStage("prod") 38 apiGateway := sparta.NewAPIGateway("MySpartaAPI", stage) 39 ``` 40 41 In the example above, we're also including a [Stage](https://godoc.org/github.com/mweagle/Sparta#Stage) value. 42 A non-`nil` Stage value will cause the registered API to be deployed. If the Stage value is `nil`, a REST API will be created, 43 but it will not be [deployed](http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-deploy-api.html) 44 (and therefore not publicly accessible). 45 46 ## Resource 47 48 The next step is to associate a URL path with the `sparta.LambdaAWSInfo` struct that represents the **go** function: 49 50 ```go 51 func spartaHTMLLambdaFunctions(api *sparta.API) []*sparta.LambdaAWSInfo { 52 var lambdaFunctions []*sparta.LambdaAWSInfo 53 lambdaFn, _ := sparta.NewAWSLambda(sparta.LambdaName(helloWorld), 54 helloWorld, 55 sparta.IAMRoleDefinition{}) 56 57 if nil != api { 58 apiGatewayResource, _ := api.NewResource("/hello", lambdaFn) 59 60 // We only return http.StatusOK 61 apiMethod, apiMethodErr := apiGatewayResource.NewMethod("GET", 62 http.StatusOK, 63 http.StatusInternalServerError) 64 if nil != apiMethodErr { 65 panic("Failed to create /hello resource: " + apiMethodErr.Error()) 66 } 67 // The lambda resource only supports application/json Unmarshallable 68 // requests. 69 apiMethod.SupportedRequestContentTypes = []string{"application/json"} 70 } 71 return append(lambdaFunctions, lambdaFn) 72 } 73 ``` 74 75 Our `helloWorld` only supports `GET`. We'll see how a single lambda function can support multiple HTTP methods shortly. 76 77 ## Provision 78 79 The final step is to to provide the API instance to `Sparta.Main()` 80 81 ```go 82 // Register the function with the API Gateway 83 apiStage := sparta.NewStage("v1") 84 apiGateway := sparta.NewAPIGateway("SpartaHTML", apiStage) 85 ``` 86 87 Once the service is successfully provisioned, the `Outputs` key will include the API Gateway Deployed URL (sample): 88 89 ```text 90 INFO[0096] ──────────────────────────────────────────────── 91 INFO[0096] Stack Outputs 92 INFO[0096] ──────────────────────────────────────────────── 93 INFO[0096] S3SiteURL Description="S3 Website URL" Value="http://spartahtml-mweagle-s3site89c05c24a06599753eb3ae4e-1w6rehqu6x04c.s3-website-us-west-2.amazonaws.com" 94 INFO[0096] APIGatewayURL Description="API Gateway URL" Value="https://w2tefhnt4b.execute-api.us-west-2.amazonaws.com/v1" 95 INFO[0096] ──────────────────────────────────────────────── 96 ``` 97 98 Combining the _API Gateway URL_ `OutputValue` with our resource path (_/hello/world/test_), we get the absolute URL to our lambda function: [https://w2tefhnt4b.execute-api.us-west-2.amazonaws.com/v1/hello](https://w2tefhnt4b.execute-api.us-west-2.amazonaws.com/v1/hello) 99 100 ## Verify 101 102 Let's query the lambda function and see what the `event` data is at execution time. The 103 snippet below is pretty printed by piping the response through [jq](https://stedolan.github.io/jq/). 104 105 ```nohighlight 106 $ curl -vs https://3e7ux226ga.execute-api.us-west-2.amazonaws.com/v1/hello | jq . 107 * Trying 52.84.237.220... 108 * TCP_NODELAY set 109 * Connected to 3e7ux226ga.execute-api.us-west-2.amazonaws.com (52.84.237.220) port 443 (#0) 110 * TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 111 * Server certificate: *.execute-api.us-west-2.amazonaws.com 112 * Server certificate: Amazon 113 * Server certificate: Amazon Root CA 1 114 * Server certificate: Starfield Services Root Certificate Authority - G2 115 > GET /v1/hello HTTP/1.1 116 > Host: 3e7ux226ga.execute-api.us-west-2.amazonaws.com 117 > User-Agent: curl/7.54.0 118 > Accept: */* 119 > 120 < HTTP/1.1 200 OK 121 < Content-Type: application/json 122 < Content-Length: 1137 123 < Connection: keep-alive 124 < Date: Mon, 29 Jan 2018 14:15:28 GMT 125 < x-amzn-RequestId: db7f5734-04fe-11e8-b264-c70ecab3a032 126 < Access-Control-Allow-Origin: http://spartahtml-mweagle-s3site89c05c24a06599753eb3ae4e-419zo4dp8n2d.s3-website-us-west-2.amazonaws.com 127 < Access-Control-Allow-Headers: Content-Type,X-Amz-Date,Authorization,X-Api-Key 128 < Access-Control-Allow-Methods: * 129 < X-Amzn-Trace-Id: sampled=0;root=1-5a6f2c80-efb0f84554384252abca6d15 130 < X-Cache: Miss from cloudfront 131 < Via: 1.1 570a1979c411cb4529fa1e711db52490.cloudfront.net (CloudFront) 132 < X-Amz-Cf-Id: -UsCegiR1K3vJUFyAo9IMrWGdH8rKW6UBrtJLjxZqke19r0cxMl1NA== 133 < 134 { [1137 bytes data] 135 * Connection #0 to host 3e7ux226ga.execute-api.us-west-2.amazonaws.com left intact 136 { 137 "Message": "Hello world 🌏", 138 "Request": { 139 "method": "GET", 140 "body": {}, 141 "headers": { 142 "Accept": "*/*", 143 "CloudFront-Forwarded-Proto": "https", 144 "CloudFront-Is-Desktop-Viewer": "true", 145 "CloudFront-Is-Mobile-Viewer": "false", 146 "CloudFront-Is-SmartTV-Viewer": "false", 147 "CloudFront-Is-Tablet-Viewer": "false", 148 "CloudFront-Viewer-Country": "US", 149 "Host": "3e7ux226ga.execute-api.us-west-2.amazonaws.com", 150 "User-Agent": "curl/7.54.0", 151 "Via": "1.1 570a1979c411cb4529fa1e711db52490.cloudfront.net (CloudFront)", 152 "X-Amz-Cf-Id": "vAFNTV5uAMeTG9JN6IORnA7LYJhZyB3jHV7vh-7lXn2uZQUR6eHQUw==", 153 "X-Amzn-Trace-Id": "Root=1-5a6f2c80-2b48a9c86a30b0162d8ab1f1", 154 "X-Forwarded-For": "73.118.138.121, 205.251.214.60", 155 "X-Forwarded-Port": "443", 156 "X-Forwarded-Proto": "https" 157 }, 158 "queryParams": {}, 159 "pathParams": {}, 160 "context": { 161 "appId": "", 162 "method": "GET", 163 "requestId": "db7f5734-04fe-11e8-b264-c70ecab3a032", 164 "resourceId": "401s9n", 165 "resourcePath": "/hello", 166 "stage": "v1", 167 "identity": { 168 "accountId": "", 169 "apiKey": "", 170 "caller": "", 171 "cognitoAuthenticationProvider": "", 172 "cognitoAuthenticationType": "", 173 "cognitoIdentityId": "", 174 "cognitoIdentityPoolId": "", 175 "sourceIp": "73.118.138.121", 176 "user": "", 177 "userAgent": "curl/7.54.0", 178 "userArn": "" 179 } 180 } 181 } 182 } 183 ``` 184 185 While this demonstrates that our lambda function is publicly accessible, it's not immediately obvious where the `*event` data is being populated. 186 187 ## Mapping Templates 188 189 The event data that's actually supplied to `echoS3Event` is the complete HTTP request body. This content is what the API Gateway sends to our lambda function, which is defined by the integration mapping. This event data also includes the values of any whitelisted parameters. When the API Gateway Method is defined, it optionally includes any whitelisted query params and header values that should be forwarded to the integration target. For this example, we're not whitelisting any params, so those fields (`queryParams`, `pathParams`) are empty. Then for each integration target (which can be AWS Lambda, a mock, or a HTTP Proxy), it's possible to transform the API Gateway request data and whitelisted arguments into a format that's more amenable to the target. 190 191 Sparta uses a pass-through template that passes all valid data, with minor **Body** differences based on the inbound _Content-Type_: 192 193 ### _application/json_ 194 195 {{% import file="./static/source/resources/provision/apigateway/inputmapping_json.vtl" language="nohighlight" %}} 196 197 ### _*_ (Default `Content-Type`) 198 199 {{% import file="./static/source/resources/provision/apigateway/inputmapping_default.vtl" language="nohighlight" %}} 200 201 The default mapping templates forwards all whitelisted data & body to the lambda function. You can see by switching on the `method` field would allow a single function to handle different HTTP methods. 202 203 The next example shows how to unmarshal this data and perform request-specific actions. 204 205 ## Proxying Envelope 206 207 Because the integration request returned a successful response, the API Gateway response body contains only our lambda's output (`$input.json('$.body')`). 208 209 To return an error that API Gateway can properly translate into an HTTP 210 status code, use an [apigateway.NewErrorResponse](https://godoc.org/github.com/mweagle/Sparta/aws/apigateway#NewErrorResponse) type. This 211 custom error type includes fields that trigger integration mappings based on the 212 inline [HTTP StatusCode](https://golang.org/src/net/http/status.go). The proper error 213 code is extracted by lifting the `code` value from the Lambda's response body and 214 using a [template override](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-override-request-response-parameters.html) 215 216 If you look at the **Integration Response** section of the _/hello/world/test_ resource in the Console, you'll see a list of Regular Expression matches: 217 218 ## Cleanup 219 220 Before moving on, remember to decommission the service via: 221 222 ```bash 223 go run application.go delete 224 ``` 225 226 ## Wrapping Up 227 228 Now that we know what data is actually being sent to our API Gateway-connected Lambda function, we'll move on to performing a more complex operation, including returning a custom HTTP response body. 229 230 ### Notes 231 232 * [Mapping Template Reference](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html)