Create a serverless RESTful API with API Gateway, Swagger, Lambda, and DynamoDB

Michael Wittig – 05 Nov 2015

This article teaches you how to create a serverless RESTful API on AWS. You will use OpenAPI Specification formerly known as Swagger Specification to define the API and API Gateway in combination with Lambda to implement the API. DynamoDB is used to store the data. The example’s source code is available on GitHub and can be used to speed up your own project.

If you are interested in describing the API in CloudFormation read Create a serverless RESTful API with API Gateway, CloudFormation, Lambda, and DynamoDB instead!
If you are interested in deploying the API with the Serverless Framework read Create a serverless RESTful API with the Serverless Framework powered by API Gateway, Lambda, and DynamoDB instead!

Implementing a RESTful API with API Gateway, Lambda, and DynamoDB

API Gateway provides an HTTP API endpoint that is fully configurable. You define the HTTP resources (like /user), the HTTP methods on that resources (like POST, GET, DELETE, …) and the integration (e.g. Lambda function) that should be called to process the request. The Lambda function can then run whatever logic is needed to answer the request. The result of the Lambda function is returned by the API Gateway to the caller. The following figure demonstrates this flow.

API Gateway flow from client request to Lambda and back

If we zoom into the API Gateway component of the previous figure we see what happens inside the API Gateway.

API Gateway internals

If you want to define a REST API you need to specify:

  • Resources (e.g. GET /user)
  • Methods on each resource (e.g. GET /user)
  • Input
  • Body Model
  • Headers
  • Path parameters (e.g. GET /user/:userId)
  • Query parameters (e.g. GET /user?limit=10)
  • Mapping HTTP input to integration input
  • Integrations (e.g. Lambda functions)
  • Mapping integration output to HTTP output
  • Output
  • Body Model
  • Headers

You can use a popular framework called Swagger to define a REST API. You will learn how to use Swagger next.

Defining a RESTful API with Swagger

Swagger defines a way to describe your RESTful API in a format like JSON. The cool thing about Swagger is that the API definition can be used by:

  • the server that implements the API
  • the clients that use the API

Swagger offers a large ecosystem offering powerful tools: you are able to generate client SDKs, visually edit your Swagger definition and use many other helpful tools. A simple Swagger definition that defines what happens if a GET request is sent to resource /user follows:

{
"swagger": "2.0",
"info": {
[...]
},
"schemes": ["https"],
"consumes": ["application/json"],
"produces": ["application/json"],
"paths": {
"/user": {
"get": {
"responses": {
"200": {
"description": "users retrieved",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
}
}
}
}
}
},
"definitions": {
"User": {
"type": "object",
"properties": {
"uid": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
},
"required": ["uid", "email", "phone"]
}
}
}

The Swagger definition also contains definitions to describe the expected input or output in JSON schema notation. With that information, you can define a RESTful API. But how does Swagger work together with API Gateway?

Connecting Swagger with API Gateway

AWS offers a tool to import a Swagger definition into API Gateway. The AWS CLI (version >= 1.10.18) supports importing a Swagger definition into API Gateway. You can also add API Gateway specific information to the Swagger definition by using extensions. The following example makes the connection between the API and the Lambda function.

[...]
"paths": {
"/user": {
"get": {
"responses": {
},
"x-amazon-apigateway-integration": {
"type": "aws",
"uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/XXX/invocations",
"responses": {
"default": {
"statusCode": "200"
}
}
}
}
}
}
[...]

Have a look at the following example to see the API in action.

Example

The example in this article reuses the multi-user ToDo application from chapter 10 in Amanzon Web Services in Action. You can find the code for the original example in the book’s code repository.

Use Swagger UI to have a look at the REST API definition of this example.

Setting up

Clone the repository:

$ git clone git@github.com:AWSinAction/apigateway.git
$ cd apigateway/

Create the lambda code file (lambda.zip):

$ npm install --production
$ ./bundle.sh

Create an S3 bucket in the US East (N. Virginia, us-east-1) region and upload the lambda.zip file (replace $S3Bucket with a S3 bucket name):

export AWS_DEFAULT_REGION=us-east-1
export S3Bucket=$(whoami)-apigateway
$ aws s3 mb s3://$S3Bucket
$ aws s3 cp lambda.zip s3://$S3Bucket/lambda.zip

Create CloudFormation stack (replace $S3Bucket with your S3 bucket name)

$ aws cloudformation create-stack --stack-name apigateway --template-body file://template.json --capabilities CAPABILITY_IAM --parameters ParameterKey=S3Bucket,ParameterValue=$S3Bucket

Wait until the stack is created (CREATE_COMPLETE)

$ aws cloudformation wait stack-create-complete --stack-name apigateway

replace all nine occurrences of $AWSRegion in swagger.json with the region that you are creating your API and Lamdba in

$ sed -i '.bak' 's/$AWSRegion/us-east-1/g' swagger.json

get the LambdaArn

$ aws cloudformation describe-stacks --stack-name apigateway --query Stacks[0].Outputs

replace all nine occurrences of $LambdaArn in swagger.json with the ARN from the stack output above (e.g. arn:aws:lambda:us-east-1:YYY:function:apigateway-Lambda-XXX)

$ sed -i '.bak' 's/$LambdaArn/arn:aws:lambda:us-east-1:YYY:function:apigateway-Lambda-XXX/g' swagger.json

deploy the API Gateway

make sure you have an up-to-date version (aws --version) of the AWS CLI >= 1.10.18. Learn more here: http://docs.aws.amazon.com/cli/latest/userguide/installing.html

$ aws apigateway import-rest-api --fail-on-warnings --body file://swagger.json

update the CloudFormation template to set the ApiId parameter (replace $ApiId with the id output from above)

$ aws cloudformation update-stack --stack-name apigateway --template-body file://template.json --capabilities CAPABILITY_IAM --parameters ParameterKey=S3Bucket,UsePreviousValue=true ParameterKey=S3Key,UsePreviousValue=true ParameterKey=ApiId,ParameterValue=$ApiId

deploy to stage v1 (replace $ApiId)

$ aws apigateway create-deployment --rest-api-id $ApiId --stage-name v1

Using the RESTful API

The following examples assume that you replace $ApiGatewayEndpoint with $ApiId.execute-api.us-east-1.amazonaws.com

export ApiGatewayEndpoint="$ApiId.execute-api.us-east-1.amazonaws.com/v1"

Your API is now able to answer requests. Let’s give it a try.

Create a user:

curl -vvv -X POST -d '{"email": "your@mail.com", "phone": "0123456789"}' -H "Content-Type: application/json" https://$ApiGatewayEndpoint/user

List users:

curl -vvv -X GET https://$ApiGatewayEndpoint/user

Create a task:

curl -vvv -X POST -d '{"description": "test task"}' -H "Content-Type: application/json" https://$ApiGatewayEndpoint/user/$UserId/task

List tasks:

curl -vvv -X GET https://$ApiGatewayEndpoint/user/$UserId/task

Mark task as complete:

curl -vvv -X PUT https://$ApiGatewayEndpoint/user/$UserId/task/$TaskId

Delete task:

curl -vvv -X DELETE https://$ApiGatewayEndpoint/user/$UserId/task/$TaskId

Create a task with a category:

curl -vvv -X POST -d '{"description": "test task", "category": "test"}' -H "Content-Type: application/json" https://$ApiGatewayEndpoint/user/$UserId/task

List tasks by category:

curl -vvv -X GET https://$ApiGatewayEndpoint/category/$Category/task

Tear down and clean up

Delete API Gateway (replace $ApiId):

$ aws apigateway delete-rest-api --rest-api-id $ApiId

Delete CloudFormation stack:

$ aws cloudformation delete-stack --stack-name apigateway

Delete S3 bucket (replace $S3Bucket):

$ aws s3 rb --force s3://$S3Bucket

Summary

With API Gateway you can configure a RESTful API. You used a Lambda function to implement the functionality: Each request is answered by the Lambda function. You have very limited overhead to operate your API because you only need to configure your API and implement the functionality. You don’t need to care about servers, scaling, and all the operational overhead.
To automate the configuration of API Gateway you used a tool to import Swagger definitions.

Michael Wittig

Michael Wittig

I’ve been building on AWS since 2012 together with my brother Andreas. We are sharing our insights into all things AWS on cloudonaut and have written the book AWS in Action. Besides that, we’re currently working on bucketAV, HyperEnv for GitHub Actions, and marbot.

Here are the contact options for feedback and questions.