Skip to content

Instantly share code, notes, and snippets.

@crypticmind
Last active March 25, 2024 06:26
Show Gist options
  • Star 95 You must be signed in to star a gist
  • Fork 30 You must be signed in to fork a gist
  • Save crypticmind/c75db15fd774fe8f53282c3ccbe3d7ad to your computer and use it in GitHub Desktop.
Save crypticmind/c75db15fd774fe8f53282c3ccbe3d7ad to your computer and use it in GitHub Desktop.
Setup lambda + API Gateway using localstack

About

Example on how to run locally an AWS Lambda via API Gateway using localstack.

Based on...

  1. https://stackoverflow.com/questions/44547574/create-api-gateway-in-localstack/48682628
  2. https://ig.nore.me/2016/03/setting-up-lambda-and-a-gateway-through-the-cli/

Usage

  1. Build a zip containing lambda.js, name it api-handler.zip
  2. Launch localstack in whatever way you can. See sample docker-compose.yml.
  3. Run setup.sh

Pending

  1. Catch any URL /api/whatever.
  2. Pass all HTTP verbs. Only GET is configured here. Other verbs require their own integration as ANY is not supported yet.
  3. No query parameters yet.
version: '2.1'
services:
localstack:
image: localstack/localstack
ports:
- "4567-4583:4567-4583"
- "${PORT_WEB_UI-4566}:${PORT_WEB_UI-8080}"
environment:
- SERVICES=${SERVICES-lambda,apigateway }
- DEBUG=${DEBUG- }
- DATA_DIR=${DATA_DIR- }
- PORT_WEB_UI=${PORT_WEB_UI- }
- LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR-docker-reuse }
- KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
- DOCKER_HOST=unix:///var/run/docker.sock
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
'use strict'
const apiHandler = (payload, context, callback) => {
console.log(`Function apiHandler called with payload ${JSON.stringify(payload)}`);
callback(null, {
statusCode: 201,
body: JSON.stringify({
message: 'Hello World'
}),
headers: {
'X-Custom-Header': 'ASDF'
}
});
}
module.exports = {
apiHandler,
}
#!/bin/sh
API_NAME=api
REGION=us-east-1
STAGE=test
function fail() {
echo $2
exit $1
}
awslocal lambda create-function \
--region ${REGION} \
--function-name ${API_NAME} \
--runtime nodejs8.10 \
--handler lambda.apiHandler \
--memory-size 128 \
--zip-file fileb://api-handler.zip \
--role arn:aws:iam::123456:role/irrelevant
[ $? == 0 ] || fail 1 "Failed: AWS / lambda / create-function"
LAMBDA_ARN=$(awslocal lambda list-functions --query "Functions[?FunctionName==\`${API_NAME}\`].FunctionArn" --output text --region ${REGION})
awslocal apigateway create-rest-api \
--region ${REGION} \
--name ${API_NAME}
[ $? == 0 ] || fail 2 "Failed: AWS / apigateway / create-rest-api"
API_ID=$(awslocal apigateway get-rest-apis --query "items[?name==\`${API_NAME}\`].id" --output text --region ${REGION})
PARENT_RESOURCE_ID=$(awslocal apigateway get-resources --rest-api-id ${API_ID} --query 'items[?path==`/`].id' --output text --region ${REGION})
awslocal apigateway create-resource \
--region ${REGION} \
--rest-api-id ${API_ID} \
--parent-id ${PARENT_RESOURCE_ID} \
--path-part "{somethingId}"
[ $? == 0 ] || fail 3 "Failed: AWS / apigateway / create-resource"
RESOURCE_ID=$(awslocal apigateway get-resources --rest-api-id ${API_ID} --query 'items[?path==`/{somethingId}`].id' --output text --region ${REGION})
awslocal apigateway put-method \
--region ${REGION} \
--rest-api-id ${API_ID} \
--resource-id ${RESOURCE_ID} \
--http-method GET \
--request-parameters "method.request.path.somethingId=true" \
--authorization-type "NONE" \
[ $? == 0 ] || fail 4 "Failed: AWS / apigateway / put-method"
awslocal apigateway put-integration \
--region ${REGION} \
--rest-api-id ${API_ID} \
--resource-id ${RESOURCE_ID} \
--http-method GET \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:${REGION}:lambda:path/2015-03-31/functions/${LAMBDA_ARN}/invocations \
--passthrough-behavior WHEN_NO_MATCH \
[ $? == 0 ] || fail 5 "Failed: AWS / apigateway / put-integration"
awslocal apigateway create-deployment \
--region ${REGION} \
--rest-api-id ${API_ID} \
--stage-name ${STAGE} \
[ $? == 0 ] || fail 6 "Failed: AWS / apigateway / create-deployment"
ENDPOINT=http://localhost:4567/restapis/${API_ID}/${STAGE}/_user_request_/HowMuchIsTheFish
echo "API available at: ${ENDPOINT}"
echo "Testing GET:"
curl -i ${ENDPOINT}
echo "Testing POST:"
curl -iX POST ${ENDPOINT}
@gargshubham49
Copy link

Getting error TypeError: callback is not a function

@rpivo
Copy link

rpivo commented Aug 13, 2021

Thanks for sharing this

@dalmosantos
Copy link

##Add 'awslocal'
echo -n -e'

alias awslocal="aws --endpoint-url=http://localhost:4566"' >> ~/.bashrc

@mcnowakGP
Copy link

Getting error: "NoSuchBucket The specified bucket does not exist restapis 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE"

@Arun-AG
Copy link

Arun-AG commented Feb 27, 2022

Getting error "NO such bucket"

@wvargas-ods
Copy link

wvargas-ods commented Apr 8, 2022

I had the same "No such bucket" error and I fixed it creating the URL in this way:
http://localhost:<port>/restapis/<apiId>/<stageId>/_user_request_/<path>

port: In my case was 4566 (http://localhost:4566/health), but it could be different for you.
apiId: Get the value from API_ID
stageId: Get the value from the output of the command awslocal apigateway create-deployment
path: It's what you define in the --path-part of the command awslocal apigateway create-resource

More info: https://docs.localstack.cloud/aws/apigatewayv2/

@danielnegreiros
Copy link

well, amazing, thanks!

@stokito
Copy link

stokito commented Mar 15, 2024

I wasn't able to find why the lambda path lambda:path/2015-03-31/functions/${LAMBDA_ARN}/invocations
contains the date 2015-03-31. But it looks like ignored and I set my own random date and it worked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment