I'm trying to setup an API Gateway that forwards requests to a Lambda function. Precisely I struggle to setup CORS properly. When handling the request in my Lambda function the incoming event does not have the RequestContext information set. Hence I don't know when I handle an OPTIONS request.
My debug code: in is the APIGatewayV2HTTPRequest
log.Printf("type: %s", reflect.TypeOf(in))
log.Printf("req: %+v", in)
Output:
type: events.APIGatewayV2HTTPRequest
...
RequestContext:{
RouteKey:
AccountID:xxx
Stage:default
RequestID:dB7h2jEcFiAEMkA=
Authorizer:<nil>
APIID:xxx
DomainName:xxx
DomainPrefix:xxx
Time:
TimeEpoch:0
HTTP:
{Method: Path: Protocol: SourceIP: UserAgent:}
It seems RequestContext.HTTP is not set and I have no idea why.
I had to set the api gateway payload version to v2. For some reason it was set to v1 and therefor the event was not properly populated.
Related
I'd like to call lambda function using a state machine with passing path and method (as in usual HTTP sense). Current serverless template to achieve that is the following:
functions:
myfunction:
handler: bin/myfunction
events:
- http:
path: setup
method: POST
stepFunctions:
validate: true
stateMachines:
myMachine:
name: myMachine
definition:
StartAt: Setup
States:
Setup:
Type: Task
Resource:
Fn::GetAtt: [myfunction, Arn]
Parameters:
InvocationType: Event
Payload:
path: "/setup"
httpMethod: "POST"
body: ""
End: true
However, the actual call that arrives to myfunction is a GET request with path /. Fields that I used as a payload are from lambda:InvokeFunction API where one can set body, path and httpMethod as a json in Payload property of lambda.InvokeInput and get everything called correctly.
How to replicate the same with my example?
path and httpMethod are for invoking an API Gateway route, not a Lambda function.
A Lambda function invocation (mostly) takes a function name, invocation type & a payload.
If you must go via API Gateway, take a look at the official 'Call API Gateway with Step Functions' guide on how to do this otherwise just invoke your Lambda manually.
I'm trying to get my API Gateway api to:
Run an authorizer
Pass authorizer context to a Step Function execution
Respond to client with Step Function output
I already have #1 and #3 done, but passing the response of the attached authorizer lambda to the step function is proving to be impossible.
I found this page and this page with reference sheets on what interpolation values you can use for your parameter mapping (Create Integration -> Step Function: StartSyncExecution -> Advanced Settings -> Input) but any time I try to use anything related to $context like $context.authorizer.email, API Gateway just responds with an HTTP 400 and gives me this CloudWatch output:
"Unable to resolve property Input from source {\"lambdaName\": \"arn:aws:lambda:us-east-1:xxxxxxx\", \"reqBody\": $request.body.Input, \"authContext\": $context.apiId }. Please make sure that the request to API Gateway contains all the necessary fields specified in request parameters."
These are the JSON objects I've tried using for the Input text box and all of them either give me an errors when trying to save or throw an HTTP 400 and log the above errors when I visit the route:
{"lambdaName": "xxx", "reqBody": $request.body.Input, "authContext": $context.authorizer.email }
{"lambdaName": "xxx", "reqBody": $request.body.Input, "authContext": "$context.authorizer.email" }
{"lambdaName": "xxx", "reqBody": $request.body.Input, "authContext": $context.apiId }
{"lambdaName": "xxx", "reqBody": $request.body.Input, "authContext": $context }
{"lambdaName": "xxx", "reqBody": $request.body.Input, "authContext": $event.requestContext.authorizer.email }
It seems like the only way to have authorization code to work with step functions is to wrap my step function called by API Gateway in another step function that authorizes the request and then invokes the endpoint step function. I've researched this for hours and I'm not getting anywhere. Any help at all is appreciated.
I ended up solving this by using API Gateway v1 and a REST API instead of a HTTP API. For some reason v2's input field currently doesn't work for anything other than $request.body.Input. From there, I hooked up all of my endpoints to a step function that runs the authorization lambda on their Authorization header in the request.
I have a step function that allows me to chain together step function and lambda actions so for most requests I just chain together the authorizer lambda and the endpoint's action (can be lambda or another step function).
The main takeaway here is that if you're using API Gateway and Step Functions, it looks like passing custom-formatted input into your step function isn't very easy to do without using the v1 of API Gateway in a REST API, not an HTTP api. Hopefully this will be fixed in the future.
Another solution could be to use the Mapping Template in API Gateway Integration Request.
Example: Consider the response from the Lambda Authorizer as this:
{
principalId: 'myuser',
context: {
customKey: 'CustomValue'
},
policyDocument: {
Version: '2012-10-17',
Statement: [
{
Action: ['execute-api:Invoke'],
Effect: 'Allow',
Resource:
'arn:aws:execute-api:_region_:111111111111:0a0a0a0a0a/default/POST/my-endpoint'
}
]
}
}
In the Mapping Template for application/json, map whatever property you want in the input field (quote escaped). Each property sent from the Lambda Authorizer in the context field will be available as $context.authorizer.property.
One possible Template Mapping could be:
{
"input": "{\"origin\":\"$input.json('$.requestvalue').replaceAll('\"','')\", \"customPropertyFromLambdaAuthorizer\" : \"$context.authorizer.customKey\" }",
"name": "DemoStateMachineRequest",
"stateMachineArn": "arn:aws:states:_region_:11111111111:stateMachine:MyStateMachine"
}
Reference $context.authorizer.property
I started looking into using AWS HTTP API as a single point of entry to some micro services running with ECS.
One micro service has the following route internally on the server:
/sessions/{session_id}/topics
I define exactly the same route in my HTTP API and use CloudMap and a VPC Link to reach my ECS cluster. So far so good, the requests can reach the servers. The path is however not the same when it arrives. As per AWS documentation [1] it will prepend the stage name so that the request looks the following when it arrives:
/{stage_name}/sessions/{session_id}/topics
So I started to look into Parameter mappings so that I can change the path for the integration, but I cannot get it to work.
For requestParameters I want overwrite the path like below, but for some reason the original path with the stage variable is still there. If I just define overwrite:path as $request.path.sessionId I get only the ID as the path or if I write whatever string I want it will arrive as I define it. But when I mix the $request.path.sessionId and the other parts of the string it does not seem to work.
How do I format this correctly?
paths:
/sessions/{sessionId}/topics:
post:
responses:
default:
description: "Default response for POST /sessions/{sessionId}/topics"
x-amazon-apigateway-integration:
requestParameters:
overwrite:path: "/sessions/$request.path.sessionId/topics"
payloadFormatVersion: "1.0"
connectionId: (removed)
type: "http_proxy"
httpMethod: "POST"
uri: (removed)
connectionType: "VPC_LINK"
timeoutInMillis: 30000
[1] https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-private.html
You can try to use parentheses. Formal notation instead of shorthand notation.
overwrite:path: "/sessions/${request.path.sessionId}/topics"
It worked well for me for complex mappings.
mapping template is a script expressed in Velocity Template Language (VTL)
dont remove the uri and the connectionId and it will work for you.
add only requestParameters:
overwrite:path: "/sessions/$request.path.sessionId/topics"
I am using serverless to deploy API gateway and lambda using lambda integration. Below is the infra code:
myLambda:
...
events:
- http:
path: customer/{id}
method: delete
integration: lambda
request:
parameters:
paths:
id: true
template:
text/plain: "$input.params('id')"
It creates a delete API to delete customer based on the id specified in the path parameter. I'd like to map the path id to myLambda's event parameter in its handler. But the above code doesn't work the lambda receives the whole http request from API gateway. How can I map the id parameter to the event parameter in lambda?
When you are using API Gateway and Lambda functions, you have 2 options:
Lambda proxy
Lambda integration
By default, the Serverless framework is using Lambda proxy. This way of connecting to a Lambda function is a lot quicker. It passes the whole HTTP request to a Lambda function and it lets you handle the request completely on the Lambda function side.
There is another way, as I mentioned, it's Lambda integration, without using Lambda proxy. If you want to use this method, you need to set up mappings on your request and your response. This way is "cleaner" on the LAmbda code side, because Lambda received mapped parameters in the event, compared to the whole HTTP request as in proxy. On the other hand, this kind of integration is more complicated to set up, as you need to make all the mappings, and it uses VTL (Apache Velocity Template Language) to do that.
In order to set this up using Serverless, take a look at the "Lambda Integration" section in Serverless framework API Gateway documentation:
I believe that ApiGateway event itself should contain that query / path parameter key value pair and you can directly query your parameters from the event, if you'd go with Lambda Proxy method which #Caldazar mentioned above.
For eg let's say if you have a URL
https://example.execute-api.us-west-2.amazonaws.com/prod/confirmReg?token=12345&uid=5
you could simply retrieve the values as below from your handler:
token = event['queryStringParameters']['token']
uid = event["queryStringParameters"]['uid']
EDIT:
Please follow the below steps for Lambda Integration:
Go to your respective API resource method and click on Integration Request section
Find the Mapping Templates area of the Integration request and open it up. Add a new mapping template for the application/json Content-Type. You'll need to change the type in the combo box from "Input passthrough" to "Mapping Template".
Then paste the following piece of JSON inside the template:
{
"id" : "$input.params('id')"
}
This way you'll not end up receiving the entire event object inside your lambda but only your path param which is sent in your request.
Some references: 1 & 2
I have a multi-endpoint webservice written in Flask and running on API Gateway and Lambda thanks to Zappa.
I have a second, very tiny, lambda, written in Node, that periodically hits one of the webservice endpoints. I do this by configuring the little lambda to have Internet access then use Node's https.request with these options:
const options = {
hostname: 'XXXXXXXXXX.execute-api.us-east-1.amazonaws.com',
port: 443,
path: '/path/to/my/endpoint',
method: 'POST',
headers: {
'Authorization': `Bearer ${s3cretN0tSt0r3d1nTheC0de}`,
}
};
and this works beautifully. But now I am wondering whether I should instead make the little lambda invoke the API endpoint directly using the AWS SDK. I have seen other S.O. questions on invoking lambdas from lambdas but I did not see any examples where the target lambda was a multi-endpoint webservice. All the examples I found used new AWS.Lambda({...}) and then called invokeFunction with params.
Is there a way to pass, say, an event to the target lambda which contained the path of the specific endpoint I want to call? (and the auth headers, etc.) * * * * OR * * * * is this just a really dumb idea, given that I have working code already? My thinking is that a direct SDK lambda invocation might (is this true?) bypass API Gateway and be cheaper, BUT, hitting the endpoint directly via API Gateway is better for logging. And since the periodic lambda runs once a day, it's probably free anyway.
If what I have now is best, that's a fine answer. A lambda invocation answer would be cool too, since I've not been able to find a good example in which the target lambda had multiple https endpoints.
You can invoke the Lambda function directly using the invoke method in AWS SDK.
var params = {
ClientContext: "MyApp",
FunctionName: "MyFunction",
InvocationType: "Event",
LogType: "Tail",
Payload: <Binary String>,
Qualifier: "1"
};
lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
/*
data = {
FunctionError: "",
LogResult: "",
Payload: <Binary String>,
StatusCode: 123
}
*/
});
Refer the AWS JavaScript SDK lambda.invoke method for more details.