Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exactly which path do I cURL for LocalStack API Gateway Lambda integration?

LocalStack is neat, but it's hard to find documentation for it. I'm having trouble hitting my API Gateway proxy endpoint that's connected to a Lambda function.

The only things I can think of is when I did the put-integration for the gateway, I wasn't sure how to handle the credentials param (given this is 100% local... there's no real AWS account I'm using) so I left it commented for now:

aws --endpoint-url=http://localhost:4567 apigateway \
    put-integration \
    --region us-west-2 \
    --rest-api-id 0091232159 \
    --resource-id 3732709762 \
    --http-method ANY \
    --type AWS_PROXY \
    --integration-http-method POST \
    --uri arn:aws:lambda:us-west-2:000000000000:function:PostProposal \
    #--credentials arn:aws:iam::123456789012:role/apigAwsProxyRole

And my coworker found a thread that make it sound like maybe it's a bug in LocalStack:

https://github.com/atlassian/localstack/issues/129

Anyway, here's what my API looks like when I do aws --endpoint-url=http://localhost:4567 apigateway get-resources --rest-api-id 0091232159:

{
    "items": [
        {
            "id": "3312801A-ZA-Z6",
            "path": "/",
            "resourceMethods": {
                "GET": {}
            }
        },
        {
            "id": "3732709762",
            "parentId": "3312801A-ZA-Z6",
            "pathPart": "{proxy+}",
            "path": "/{proxy+}",
            "resourceMethods": {
                "GET": {},
                "ANY": {
                    "httpMethod": "ANY",
                    "authorizationType": "NONE",
                    "methodIntegration": {
                        "type": "AWS_PROXY",
                        "httpMethod": "ANY",
                        "uri": "arn:aws:lambda:us-west-2:000000000000:function:PostProposal",
                        "integrationResponses": {
                            "200": {
                                "statusCode": 200,
                                "responseTemplates": {
                                    "application/json": null
                                }
                            }
                        }
                    }
                }
            }
        }
    ]
}

My Lambda function is all set up and responds with a 200 when I do:

aws --endpoint-url=http://localhost:4574 lambda invoke --function-name PostProposal outfile.json

Now I just want to cURL an endpoint that will fire off the Lambda.

I found this thread: https://github.com/localstack/localstack/issues/229 which has a cURL example:

curl http://localhost:4567/restapis/35937034A-Z3/test/_user_request_/u-1234

And another one: https://bitbucket.org/atlassian/localstack/issues/4/how-to-create-api-gateway-deployments which has:

curl http://localhost:4567/restapis/$api_id/v1/_user_request_/mypath

I know my API ID and that 'v1' correlates to my 'test' stage name. My endpoint is at the root, so I don't think I need something where he's got bypath.

So I've tried all different variations of paths like this with GET and POST:

curl http://localhost:4567/restapis/0091232159/test/_user_request_/

I get various replies of 404, 500, and {"message": "Unable to find integration for path ....

I'm at least hitting the API Gateway simulated server; when I do curl -vv I see:

* Connected to localhost (127.0.0.1) port 4567 (#0)
> GET /restapis/0091232159/test/_user_request_/ HTTP/1.1
> Host: localhost:4567
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 404 Not Found

Any ideas? I just need the magic path!

like image 555
Aaron Avatar asked Jan 13 '18 01:01

Aaron


People also ask

What does API gateway pass to Lambda?

The backend Lambda function parses the incoming request data to determine the response that it returns. For API Gateway to pass the Lambda output as the API response to the client, the Lambda function must return the result in this format.

Can you invoke a cross account Lambda function from an API gateway integration?

You can use AWS SDKs or AWS CLI to enable cross-account access for a Lambda function to be used as Lambda authorizer and Integrations in Amazon API Gateway.

How do you pass headers to a Lambda function from API gateway?

To pass custom headers from an API Gateway API to a Lambda function, use a body mapping template. The API sends the updated API request to a Lambda function to process the headers. Then, the Lambda function returns one or more header values from the original API request.


1 Answers

Did you deploy your API? I don't see the command for that above. If yes, it should be visibile if you open http://localhost:4567/restapis in your browser.

Just to be sure, here is a complete walkthrough for setting up a lambda with API Gateway in Localstack:

First we create a simple NodeJS Lambda:

const apiTestHandler = (payload, context, callback) => {
console.log(`Function apiTestHandler called with payload ${JSON.stringify(payload)}`);
callback(null, {
    statusCode: 201,
    body: JSON.stringify({
        somethingId: payload.pathParameters.somethingId
    }),
    headers: {
        "X-Click-Header": "abc"
    }
}); 
}
module.exports = {
    apiTestHandler,
}

Put that into a zip File called apiTestHandler.zip and upload it to localstack:

aws lambda create-function \
--region us-east-1 \
--function-name api-test-handler \
--runtime nodejs6.10 \
--handler index.apiTestHandler \
--memory-size 128 \
--zip-file fileb://apiTestHandler.zip \
--role arn:aws:iam::123456:role/role-name --endpoint-url=http://localhost:4574

Now we can create our Rest-Api:

aws apigateway create-rest-api --region us-east-1 --name 'API Test' --endpoint-url=http://localhost:4567

This gives the following response:

{
"name": "API Test",
"id": "487109A-Z548",
"createdDate": 1518081479
}

With the ID we got here, we can ask for its parent-ID:

aws apigateway get-resources --region us-east-1 --rest-api-id 487109A-Z548 --endpoint-url=http://localhost:4567

Response:

{
"items": [
    {
        "path": "/",
        "id": "0270A-Z23550",
        "resourceMethods": {
            "GET": {}
        }
    }
]
}

Now we have everything to create our resource together with its path:

aws apigateway create-resource \
--region us-east-1 \
--rest-api-id 487109A-Z548 \
--parent-id 0270A-Z23550 \
--path-part "{somethingId}" --endpoint-url=http://localhost:4567

Response:

{
"resourceMethods": {
    "GET": {}
},
"pathPart": "{somethingId}",
"parentId": "0270A-Z23550",
"path": "/{somethingId}",
"id": "0662807180"
}

The ID we got here is needed to create our linked GET Method:

aws apigateway put-method \
 --region us-east-1 \
 --rest-api-id 487109A-Z548 \
 --resource-id 0662807180 \
 --http-method GET \
 --request-parameters "method.request.path.somethingId=true" \
 --authorization-type "NONE" \
--endpoint-url=http://localhost:4567

We are almost there - one of the last things to do is to create our integration with the already uploaded lambda:

aws apigateway put-integration \
 --region us-east-1 \
 --rest-api-id 487109A-Z548 \
 --resource-id 0662807180 \
 --http-method GET \
 --type AWS_PROXY \
 --integration-http-method POST \
 --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:000000000000:function:api-test-handler/invocations \
 --passthrough-behavior WHEN_NO_MATCH \
 --endpoint-url=http://localhost:4567

Last but not least: Deploy our API to our desired stage:

aws apigateway create-deployment \
 --region us-east-1 \
 --rest-api-id 487109A-Z548 \
 --stage-name test \
 --endpoint-url=http://localhost:4567

Now we can test it:

curl http://localhost:4567/restapis/487109A-Z548/test/_user_request_/HowMuchIsTheFish

Response:

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                             Dload  Upload   Total   Spent    Left  Speed
100    34  100    34    0     0      9      0  0:00:03  0:00:03 --:--:--     9
{"somethingId":"HowMuchIsTheFish"}

I hope this helps.

Hint 1: For easier use I recommend to install AWSCLI Local ( https://github.com/localstack/awscli-local ) - with this tool you can use the command "awslocal" and don't have to type "--endpoint-url= ..." for each command

Hint 2: If you don't want to define the above steps manually each time in the future I recommend the use of the Serverless Framework ( https://serverless.com/ ) together with the localstack plugin ( https://github.com/temyers/serverless-localstack ). If you need help with that I can provide you with a short guide as well.

like image 66
Steffen Bach Avatar answered Sep 17 '22 09:09

Steffen Bach