16

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!

Aaron
  • 2,154
  • 5
  • 29
  • 42
  • I don't know the exact URL, however, if you run Wireshark and then execute your aws command (that you included above), it will show you the network transmission. – Tom Drake Jan 22 '18 at 21:25

2 Answers2

10

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.

Steffen Bach
  • 536
  • 5
  • 7
  • So this saved my ass after quite a few hours. Thank you. However - can you maybe explain the "_user_request_" part? – Byron Coetsee Mar 29 '20 at 16:58
  • @ByronCoetsee `_user_request_` is hard-coded in localstack: https://github.com/atlassian/localstack/blob/master/localstack/constants.py#L81 – thirdender Dec 17 '20 at 20:23
  • Hi Steffen, The example you gave works perfectly when deploying a nodejs lambda module. But as soon as I deploy a python lambda module it looks like localstack starts complaining and it throws `json.decoder.JSONDecodeError` here: `File "/opt/code/localstack/localstack/services/apigateway/apigateway_listener.py", line 344, in invoke_rest_api_integration`. Though invoking my python lambda function from cli using **`lambda invoke`** works perfectly and calls my python handler. Do you have any idea what might be causing this issue? My localstack version: **0.12.10** – Saurav Kumar May 22 '21 at 14:25
  • My mistake.. I was not following the convention of return type from my handler. Python handler function must return a `dict` type, for example: **`return {'statusCode': 200, 'body': ''}`** – Saurav Kumar May 25 '21 at 11:08
  • Very helpful. Thank you. If you see this after 4 years and could provide a serverless example that would be most appreciated. – Amir Jun 17 '22 at 10:06
0
http://localhost:4566/restapis/**{your api gateway rest-api-id }** /**{your api gateway stage name}**/_user_request_/**{your api gateway pathpath i.e /users}**

I used this:

http://localhost:4566/restapis/mcycg8nxqr/local/_user_request_/users
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
dsddet
  • 1
  • 1