Create a serverless RESTful API with API Gateway, Swagger, Lambda, and DynamoDB
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.
If we zoom into the API Gateway component of the previous figure we see what happens inside the API Gateway.
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:
{ |
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.
[ ] |
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 |
Create the lambda code file (lambda.zip
):
$ npm install --production |
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 |
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.
Further reading
- Article Serverless image resizing at any scale
- Article A look at DynamoDB
- Article 5 AWS mistakes you should avoid
- Article Building blocks for highly available systems
- Tag lambda
- Tag apigateway
- Tag dynamodb