How to build a Serverless API in AWS without using a single lambda

Learn what is needed to build a Lambdaless API using AWS API Gateway, DynamoDB, OpenAPI and CloudFormation.

  • Gather the information from the input body.
  • Map the input from one service to another.
  • Map the service’s output to what we want to return to the client.

Let’s get started

Link to example

File Structure

The solution only requires two files:

  • template.yaml — Defines the DynamoDB table, API Gateway and the role that API Gateway will use to execute. CloudFormation uses this file to deploy all our resources.
  • products-openapi.yaml — this is where all the magic happens. This file has the API definition and the mappings needed for the input and output by using VTL.

Define resources using SAM template

I am using the Serverless Appliction Model(SAM). SAM is a tool that lets you define your Infrastructure as Code. SAM reads from the template.yaml file and runs it through CloudFormation. CloudFormation then creates all the resources in the AWS Cloud.

  • DynamoDB table — Defines a single table with a Partition Key named pk. The value of this attribute will be a GUID. (For more complex data models I would recommend watching this video by Rick Houlihan)
  • API Gateway — Loads the API definition from the OAS file. It is using a Transform to be able to use intrinsic functions to get things like ARNs, Parameters, etc.
  • Role — Role that gives API Gateway the permissions needed to be able to run DynamoDB actions.

Create API Gateway endpoints using Open API

A simple way to define your API endpoints is by using the Open API Specification (OAS). OAS has become the industry standard for API definitions. There are a lot of tools that read the file and generate documentation. Postman and SwaggerHub are some of the applications that do this.

/products

This defines two endpoints:

  • GET — Get list of all the products.
  • POST — Create new Product.

/products/{productId}

This path takes in a productId to work on a specific product. The endpoints that we use this path for are:

  • GET — Get products details.
  • PUT — Update product item with the input information.
  • DELETE — Delete product.
  • requestBody — this section only applies to endpoints that take in an input (POST and PUT in the example). It defines the expected input model. API Gateway validates that the input has expected model. If the validations fail it will return a 400 Bad Request.
  • responses — list of the expected responses when calling the endpoint.
  • x-amazon-apigateway-integration — This is a custom AWS extension. It defines the mappings for the inputs and outputs for the call to DynamoDB. We will go deeper into this section below.

Breaking down x-amazon-apigateway-integration extension

The example below shows how the endpoint that adds products implements this custom extension.

  • uri — Endpoint that API Gateway will be calling to execute our action. In the example it’s calling the DynamoDB Put Item endpoint.
  • credentials — It uses the role that was defined in the template.yaml. It’s using the GetAtt intrinsic function to get the Roles ARN.
  • requestTemplates — This takes the body of the request and maps it to the DynamoDB input using VTL. The first item of the array is using the requestId to set the ProductId. To see a list of all the variables available in the $context go here. This also makes use of the $input variable to get the values from the input, the example is using it to get the name attribute. In the second item we use the Fn::Sub intrinsic function to get the Table Name defined in the template.yaml.
  • responses — Contains the responseTemplates. This is using the same $input and $context variables to build the response mapping.
    In the next example you can see how to use a foreach expression using VTL. This mapping is creating a products array as the response of the API endpoint.

Deploy the Service to AWS

As I mentioned before this is all done without touching the AWS console. To get the application deployed to AWS there are a few things that need to happen:

  1. SAM needs to build the artifacts that are going to be deployed. This is done by running the following SAM command:
sam build
sam deploy --guided

How to test the API

To test this we need the deployed APIs URL.

Outputs:
ProductsURL:
Description: Products URL
Value: !Join [ '', [ 'https://', !Ref ProductsAPI, '.execute-api.', !Sub '${AWS::Region}', '.amazonaws.com/Lambdaless' ] ]

Bonus

To simplify deployments I included a package.json file. The file has an npm script that will take care of executing the deployment commands.

“deploy”: “aws s3api create-bucket — bucket [BUCKET-NAME] && aws s3 cp ./products-api-open-api-definition.yaml s3://[BUCKET-NAME]/ && sam build && sam deploy”
npm run deploy

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store