Invocations via an API Gateway require extra care when parsing the input event,
and when formatting results. For the most part, lambdr
will handle this
automatically. But for highly custom serialisation/deserialisation or edge cases
it may be helpful to understand how content via API Gateways is structured and
responded to.
When a Lambda runtime, like that implemented by lambdr
, starts listening for
inputs it queries the "next invocation" HTTP endpoint. The response to this
is what's called here an event. The content of this event contains the
arguments (if any) to the function. After the result has been calculate, the
runtime must format the result and submit it to the "result" HTTP endpoint.
With direct invocation the content of the event contains a string which --- when interpreted as JSON --- gives the arguments for the handler function. When a Lambda is invoked via an API Gateway then the content of the event contains significantly more detail and requires careful parsing.
There are two types of API Gateways to consider: REST and HTML.
An example of REST API Gateway event content is given at the bottom of this
section but the two most important sections are body
and
queryStringParameters
.
The queryStringParameters
contain standard JSON. So if the request contains
a parameter ?number=9
in the query then the event will contain
"queryStringParameters": {"number": "9"}
(note the string).
The body
of the event contains the data passed during a POST
operation.
It contains stringified JSON. That is, instead of "body": {"number": 9}
we would see "body": "{\"number\":9}"
.
Stringified JSON can be produced with the as_stringified_json
helper
function which wraps jsonlite
's \code{\link[jsonlite]{toJSON}} function.
There is a slight difference in behaviour, as this function also
automatically unboxes singleton values and also represents NULL
s as JSON
nulls
--- a convention that is used by the event body provided by AWS
Lambda.
When the result is calculated and ready to be sent back to the AWS Lambda
response endpoint it must be formatted in a very specific way in order to be
compatible with the API Gateway. The correct format is as below, with the
body
containing the stringified JSON representation of the result. This
is given as an R list which is then converted to stringified JSON. Compare
this to the representation of the result for a direct invocation:
## Formatting a result for a direct invocation as_stringified_json(result) ## Formatting a result for an invocation via an API Gateway as_stringified_json( list( isBase64Encoded = FALSE, statusCode = 200L, body = as_stringified_json(result) ) )
Here is an example event content from an invocation that is coming via an
API Gateway. The invocation is a call to a parity
function with an argument
number = 9
. Some information has been censored.
{ "resource": "/parity", "path": "/parity", "httpMethod": "POST", "headers": { "accept": "*/*", "Host": "abcdefghijk.execute-api.ap-southeast-2.amazonaws.com", "User-Agent": "curl/7.64.1", "X-Amzn-Trace-Id": "Root=1-615e4711-5f239aad2b046b5609e43b1c", "X-Forwarded-For": "192.168.1.1", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "accept": [ "*/*" ], "Host": [ "abcdefghijk.execute-api.ap-southeast-2.amazonaws.com" ], "User-Agent": [ "curl/7.64.1" ], "X-Amzn-Trace-Id": [ "Root=1-615e4711-5f239aad2b046b5609e43b1c" ], "X-Forwarded-For": [ "192.168.1.1" ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "queryStringParameters": null, "multiValueQueryStringParameters": null, "pathParameters": null, "stageVariables": null, "requestContext": { "resourceId": "abcdef", "resourcePath": "/parity", "httpMethod": "POST", "extendedRequestId": "G0AKsFXISwMFsGA=", "requestTime": "07/Oct/2021:01:02:09 +0000", "path": "/test/parity", "accountId": "1234567890", "protocol": "HTTP/1.1", "stage": "test", "domainPrefix": "abcdefghijk", "requestTimeEpoch": 1633568529038, "requestId": "59bbb4c9-9d24-4cbb-941b-60dd4969e9c5", "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "sourceIp": "192.168.1.1", "principalOrgId": null, "accessKey": null, "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "curl/7.64.1", "user": null }, "domainName": "abcdefghijk.execute-api.ap-southeast-2.amazonaws.com", "apiId": "abcdefghijk" }, "body": "{\"number\":9}", "isBase64Encoded": false }
HTML API Gateway events are similar to REST API Gateway events, except that the
body is more likely to be Base64 encoded, and the query parameters are presented
as string values. That is, we might expect to see a rawQueryString
of "parameter1=value1¶meter1=value2¶meter2=value". In this case however the
queryStringParameters
(if present) will be easier to work with:
"queryStringParameters": { "parameter1": "value1,value2", "parameter2": "value" }
A full example of an event body is shown below. Note that the
queryStringParameters
will not appear if rawQueryString
is an empty string.
{ "version": "2.0", "routeKey": "ANY /parity", "rawPath": "/default/parity", "rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value", "queryStringParameters": { "parameter1": "value1,value2", "parameter2": "value" }, "headers": { "accept": "*/*", "content-length": "12", "content-type": "application/x-www-form-urlencoded", "host": "abcdefghi.execute-api.ap-southeast-2.amazonaws.com", "user-agent": "curl/7.64.1", "x-amzn-trace-id": "Root=1-6167f9fb-1ada874811eaf2bc1464c679", "x-forwarded-for": "192.168.1.1", "x-forwarded-port": "443", "x-forwarded-proto": "https" }, "requestContext": { "accountId": "123456789", "apiId": "abcdefghi", "domainName": "abcdefghi.execute-api.ap-southeast-2.amazonaws.com", "domainPrefix": "abcdefghi", "http": { "method": "POST", "path": "/default/parity", "protocol": "HTTP/1.1", "sourceIp": "192.168.1.1", "userAgent": "curl/7.64.1" }, "requestId": "HMP_QiusywMEPEg=", "routeKey": "ANY /parity", "stage": "default", "time": "14/Oct/2021:09:35:55 +0000", "timeEpoch": 1634204155055 }, "body": "eyJudW1iZXIiOjd9", "isBase64Encoded": true }
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.