Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Create a Serverless RESTful API on AWS

DZone's Guide to

Create a Serverless RESTful API on AWS

Working with AWS? Implementing in REST? Read on for a detailed roadmap on how to do it right.

· Cloud Zone
Free Resource

Site24x7 - Full stack It Infrastructure Monitoring from the cloud. Sign up for free trial.

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.

If you are interested in defining the API in OpenAPI Specification or SwaggerSpecification read Create a serverless RESTful API with API Gateway, Swagger, 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 POSTGETDELETE, etc.) 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.

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 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
  • 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
  • email
  • phone

Mapped to a model in CloudFormation, this looks like:


"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 limitand 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::Methods.

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 Amazon Web Services in Action. You can find the code for the original example in the book's code repository.

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 an 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 a CloudFormation stack:


$ aws cloudformation create-stack --stack-name apigateway --template-body file://template_with_api.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


Get the $ApiId:


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


Set the $ApiGatewayEndpoint environment variable (replace $ApiId):


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


Use the RESTful API

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


Teardown

Delete the CloudFormation stack:


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


Delete the 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 HTTP request invokes a 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 CloudFormation.

Site24x7 - Full stack It Infrastructure Monitoring from the cloud. Sign up for free trial.

Topics:
aws ,rest ,serverless

Published at DZone with permission of Michael Wittig, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}