Package: downtoearth
downtoearthA tool for generating APIs in AWS, powered by Lambda and API Gateway, backed by terraform.We’re just hooking up http verbs to python functions… shouldn’t be that tough.Makes a terraform file for deployment.DocumentationFull documentation can be found athttps://downtoearth.readthedocs.io/en/latest/.The api.json fileA json file is used to define your api. From this, downtoearth will generate a terraform document.{"Name":"DownToEarthApi","Description":"test API for the downtoearth tool","AccountNumber":"123456789012","LambdaZip":"dist/api-lambda.zip","LambdaHandler":"lambda_handler.lambda_handler","LambdaRuntime":"python2.7","Roles":{"MyRole":{"PolicyDoc":"GoesHere"}},"Api":{"/api/X/{1}":["GET"],"/api/X":["GET","POST"],"/api/Y":["GET","POST"],"/api/Y/{1}":["GET"]}}Writing your lambdaThe event dictionary gets filled with a “route” element that contains a
string representing the verb and endpoint hit.VERB:/api/my/endpoint/{my_variable}This simple example will show you how to map that name to a python
function.defget_y(event,context):returndict(oh="yaaaaa!")function_mapping={"GET:/api/Y":get_y}defroute_request(event,context):if"route"notinevent:raiseValueError("must have 'route' in event dictionary")ifevent["route"]notinfunction_mapping:raiseValueError("cannot find{0}in function mapping".format(event["route"]))func=function_mapping[event["route"]]returnfunc(event,context)deflambda_handler(event,context=None):print("event:%s"%event)returnroute_request(event,context)TODO: it’d be awesome if this worked with decorators like flask or
chalice.RouterIf your API is straightforward there is no reason to write your own router.
We provide one. Your lambda code could be as simple as:fromdowntoearth.routerimportRouterROUTE_MAP={"GET:/v1/book":get_all,"POST:/v1/book":post_book,"GET:/v1/book/{isbn}":get_book,"PUT:/v1/book/{isbn}":update_book,"DELETE:/v1/book/{isbn}":remove_book}defhandle_event(event,context):"""Route and handle incoming event."""router=Router(ROUTE_MAP)returnrouter.route_request(event,context)Returning different status codesThe generated API gateway includes a number of common response codes
along with their official descriptions. To return a non-200 OK HTTP
code, raise an exception with an official description bracketed at the
beginning. For example, to return a 404:ifnotfound:raiseValueError('[Not Found] Could not find%s'%item_id)Or you can nicely handle responses from DynamoDB:try:db.put_item(Item=item,ConditionExpression='attribute_not_exists(item_id)')exceptClientError:if'ConditionalCheckFailedException'ine.args[0]:raiseValueError('[Conflict]%salready exists'%item['id'])else:raiseException('[Internal Server Error] An unknown error occurred.  Info:%s'%e.args[0])The currently supported status codes are defined in rfc7231codes, in
api_endpoints.hcl. To add support for a new status code, extend that
tuple with a (code, description) pair.Currently, there is no way to return additional headers or a custom
body. All non-200 integration responses just contain the lambda output
errorMessage field.ExceptionsWe also provide exceptions helpers for you. If you are using the provided
router you won’t need this. If you write your own router, use them like this.fromdowntoearth.exceptionsimportNotFoundExceptionifnotfound:raiseNotFoundException('Could not find%s'%item_id)Creating the Terraformcli.pyINPUT_API_DEFITION_PATHOUTPUT_TERRAFORM_PATH# or if you have it installeddowntoearthINPUT_API_DEFITION_PATHOUTPUT_TERRAFORM_PATHStages, Deployment, and YouBy default, downtoearth with create a single “production” stage. Create
multiple stages by providing an array of names to the Stages key of the
config"Stages":["production","develop"]Applying the terraform created by downtoearth will create an alias in
your lambda for each stage you defined.Now here’s the tricky part: because stages and lambda versions and
aliases are so weird, we have to update the lambda that powers a
specific stage outside of terraform. This is just easier, I promise. And
hopefully, the shape of your API will change much less often than the
code that powers it, so you won’t have to constantly churn terraform
applies just because you fixed a bug in your code.Your stage aliases are initially set up to point to the $LATEST version.
When you wanna push fresh code to a stage, publish a version of your
code, update the alias to point to that version.
You can use this downtoearth cli command to help you deploy a zip to a stage
.. code-block:: shelldowntoearth deploy INPUT_API_DEFITION_PATH STAGEhere’s a little./deploy.sh STAGEscript that does roughly the same thing
.. code-block:: shell#!/usr/bin/env bash
STAGE=$1
aws lambda update-function-code –function-name MY_FUNCTION_ROOT –zip-file fileb://MY_ZIP.zip
VERSION=”$(aws lambda  –region=us-east-1 publish-version –function-name MY_FUNCTION_ROOT | jq -r .Version)”
echo “Created version #$VERSION”
aws lambda update-alias –function-name MY_FUNCTION_ROOT –name $STAGE –function-version $VERSIONNow lets say you want to update you api with a new route. You have already used one of the above methods to update you lambda code, and now you want to apply your terraform to update your routes. In order to apply your newly generated terraform without changing the versions of the lambda code your aliases are pointing to, generate a tfvar file for the -var-file option of terraform apply. To do this simply use this downtoearth cli command
.. code-block:: shelldowntoearth tfvar INPUT_API_DEFITION_PATH TFVAR_FILE_PATHThen when you can
.. code-block:: shellterraform plan -var-file=TFVAR_FILE_PATH
terraform apply -var-file=TFVAR_FILE_PATHwhile holding your lambda aliases steady.