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

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. API Gateway responds to the caller with the result of the Lambda function. 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)

/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 CloudFormation to define a REST API.

Defining a RESTful API with CloudFormation

CloudFormation uses the JavaScript Object Notation (JSON) to describe Resources like REST APIs. The JSON file is called a template and is the blueprint of your AWS infrastructure. A REST API in API Gateway composed of three components:

Models: Define the input/output of the data

Resources and Methods: e.g. GET /user to retrieve a list of all users

to retrieve a list of all users Endpoint: e.g. https://api.somename.com/v1

You will now learn how to describe those components in CloudFormation. Remember that a CloudFormation template has the following structure:

{

"AWSTemplateFormatVersion" : "2010-09-09" ,

"Description" : "Lambda and API Gateway" ,

"Resources" : {

[...]

}

}



Everything that you will learn now happens inside the Resources block. The first resource is a REST API container to group all the following stuff.

"RestApi" : {

"Type" : "AWS::ApiGateway::RestApi" ,

"Properties" : {

"Name" : "API Gateway ToDo"

}

}



Describing Models

Models are defined with JSON Schema. The ToDo applications stores users with three required fields:

uid : User id

: User id email

phone

Mapped to a model in CloudFormation, this looks like that:

"UserModel" : {

"Type" : "AWS::ApiGateway::Model" ,

"Properties" : {

"ContentType" : "application/json" ,

"Name" : "User" ,

"RestApiId" : { "Ref" : "RestApi" },

"Schema" : {

" $schema " : "http://json-schema.org/draft-04/schema#" ,

"title" : "User" ,

"type" : "object" ,

"additionalProperties" : false ,

"properties" : {

"uid" : {

"type" : "string"

},

"email" : {

"type" : "string"

},

"phone" : {

"type" : "string"

}

},

"required" : [ "uid" , "email" , "phone" ]

}

}

}



You can also reference a model in a model for example if you want to define a list of users:

"UsersModel" : {

"Type" : "AWS::ApiGateway::Model" ,

"Properties" : {

"ContentType" : "application/json" ,

"Name" : "Users" ,

"RestApiId" : { "Ref" : "RestApi" },

"Schema" : {

" $schema " : "http://json-schema.org/draft-04/schema#" ,

"title" : "Users" ,

"type" : "array" ,

"items" : {

" $ref " : { "Fn::Join" : [ "" , [ "https://apigateway.amazonaws.com/restapis/" , { "Ref" : "RestApi" }, "/models/" , { "Ref" : "UserModel" }]]}

}

}

}

}



Now we need to define HTTP resources and methods.

Describing Resources and Methods

We want our API to expose the list of all users under GET /user . In CloudFormation we need to define the HTTP resource /user and the HTTP Method GET . We also define two optional query parameters limit and next to provide a paging interface.

"UsersResource" : {

"Type" : "AWS::ApiGateway::Resource" ,

"Properties" : {

"RestApiId" : { "Ref" : "RestApi" },

"ParentId" : { "Fn::GetAtt" : [ "RestApi" , "RootResourceId" ]},

"PathPart" : "user"

}

},

"UsersGet" : {

"Type" : "AWS::ApiGateway::Method" ,

"Properties" : {

[...]

"Integration" : {

"Type" : "AWS" ,

"IntegrationHttpMethod" : "POST" ,

"Uri" : { "Fn::Join" : [ "" , [ "arn:aws:apigateway:" , { "Ref" : "AWS::Region" }, ":lambda:path/2015-03-31/functions/" , { "Fn::GetAtt" : [ "Lambda" , "Arn" ]}, "/invocations" ]]},

"IntegrationResponses" : [{

[...]

},

"RequestParameters" : {

"method.request.querystring.limit" : false,

"method.request.querystring.next" : false

},

"MethodResponses" : [{

"ResponseModels" : {

"application/json" : { "Ref" : "UsersModel" }

},

"ResponseParameters" : {

"method.response.header.Link" : true

},

"StatusCode" : 200

}]

}

}



I left the Integration block empty to show you this important aspect now in more detail. The Integration block defines the Lambda function invoked when an HTTP request arrives.

"Integration" : {

"Type" : "AWS" ,

"IntegrationHttpMethod" : "POST" ,

"Uri" : { "Fn::Join" : [ "" , [ "arn:aws:apigateway:" , { "Ref" : "AWS::Region" }, ":lambda:path/2015-03-31/functions/" , { "Fn::GetAtt" : [ "Lambda" , "Arn" ]}, "/invocations" ]]},

"IntegrationResponses" : [{

"ResponseTemplates" : {

"application/json" : "$input.json('$.body')"

},

"ResponseParameters" : {

"method.response.header.Link" : "integration.response.body.headers.next"

},

"StatusCode" : 200

}],

"PassthroughBehavior" : "NEVER" ,

"RequestTemplates" : {

"application/json" : "{\" fun\ ": \" getUsers\ ", \" parameters\ ": {\" limit\ ": \" $input.params( 'limit' )\ ", \" next \ ": \" $input.params( 'next' )\ "}}"

}

}



Now it’s time to describe the endpoint that we need to use the REST API.

Describing the Endpoint

"RestApiDeployment" : {

"Type" : "AWS::ApiGateway::Deployment" ,

"Properties" : {

"RestApiId" : { "Ref" : "RestApi" },

"StageName" : "v1"

},

"DependsOn" : [ "UsersGet" , "UsersPost" , "UserGet" , "UserDelete" , "UserTasksGet" , "UserTasksPost" , "UserTaskPut" , "UserTaskDelete" , "CategoryTasksGet" ]

}



It’s important that you explicitly depend on all the AWS::ApiGateway::Method s.

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