The API Gateway provides an HTTPS endpoint that invokes a Lambda function when a request arrives.

The diagram was created with Cloudcraft - Visualize your cloud architecture like a pro.

As you can see, there is not much infrastructure to set up. To makes things even simpler, you will use the AWS Serverless Application Model (AWS SAM) to reduce the lines of your CloudFormation template to a minimum. All CloudFormation resource types that start with AWS::Serverless:: are transformed by SAM.

AWS Velocity Series Most of our clients use AWS to reduce time-to-market following an agile approach. But AWS is only one part of the solution. In this article series, I show you how we help our clients to improve velocity: the time from idea to production. Discover all posts!

Let’s start to describe the needed infrastructure.

Serverless app infrastructure

The serverless app infrastructure for the factorial app consists of two parts:

API Gateway: Provides a configurable HTTPS REST Endpoint that can trigger integrations such as Lambda when a request arrives.

Lambda function: Lambda provides a fully managed (aka Serverless) runtime for Node.js, Java, Python, and C# code. You upload your code and Lambda runs the code for you.

I will start with the Lambda function.

Lambda function

You can follow step by step or get the full source code here: https://github.com/widdix/aws-velocity

Create a file infrastructure/serverless.yml and describe the Lambda function that is invoked on GET /{number} requests.

infrastructure/serverless.yml GitHub

AWSTemplateFormatVersion: '2010-09-09'

Transform: 'AWS::Serverless-2016-10-31'

Description: 'Serverless'

Parameters:



S3Bucket:

Type: String

S3Key:

Type: String

Resources:

GetFactorialLambda:

Type: 'AWS::Serverless::Function'

Properties:

Handler: 'app/handler.factorial'

Runtime: 'nodejs6.10'

CodeUri:

Bucket: !Ref S3Bucket

Key: !Ref S3Key

Events:

Http:

Type: Api

Properties:

Path: /{n}

Method: get

RestApiId: !Ref ApiGateway



Lambda dictates an interface that you have to follow. So far, the factorial app is based on express and comes with its own web server. This is no longer needed. Instead, we can have a simpler entry point into the application. Create a file app/handler.js with the following content.

app/handler.js GitHub ;



var factorial = require ( './lib/factorial.js' );





exports.factorial = function ( event, context, cb ) {

var n = parseInt (event.pathParameters.n, 10 );

if (n < 0 || n > 14 ) {

cb( null , {

statusCode: 400

});

} else {

cb( null , {

statusCode: 200 ,

headers: {

'Content-Type' : 'text/plain'

},

body: factorial(n).toString()

});

}

};



But how do you get notified if something goes wrong? Let’s add a parameter to the Parameters section to make the receiver configurable:

infrastructure/serverless.yml GitHub AdminEmail:

Description: 'The email address of the admin who receives alerts.'

Type: String



Alerts are triggered by a CloudWatch Alarm which can send an alert to an SNS topic. You can subscribe to this topic via an email address to receive the alerts. Let’s create an SNS topic and two alarms in the Resources section:

infrastructure/serverless.yml GitHub

Alerts:

Type: 'AWS::SNS::Topic'

Properties:

Subscription:

- Endpoint: !Ref AdminEmail

Protocol: email



GetFactorialLambdaLambdaErrorsAlarm:

Type: 'AWS::CloudWatch::Alarm'

Properties:

AlarmDescription: 'GET /{n} lambda errors'

Namespace: 'AWS/Lambda'

MetricName: Errors

Dimensions:

- Name: FunctionName

Value: !Ref GetFactorialLambda

Statistic: Sum

Period: 60

EvaluationPeriods: 1

Threshold: 1

ComparisonOperator: GreaterThanOrEqualToThreshold

AlarmActions:

- !Ref Alerts



GetFactorialLambdaLambdaThrottlesAlarm:

Type: 'AWS::CloudWatch::Alarm'

Properties:

AlarmDescription: 'GET /{n} lambda throttles'

Namespace: 'AWS/Lambda'

MetricName: Throttles

Dimensions:

- Name: FunctionName

Value: !Ref GetFactorialLambda

Statistic: Sum

Period: 60

EvaluationPeriods: 1

Threshold: 1

ComparisonOperator: GreaterThanOrEqualToThreshold

AlarmActions:

- !Ref Alerts



Let’s recap what you implemented: A Lambda function that is connected to an API Gateway for GET /{number} requests. In the case of errors, you will receive an Email. All Lambda functions automatically save their logs in CloudWatch Logs.

Now, you can improve the API Gateway setup and add input validation.

API Gateway

An implicit API Gateway is created and configured automatically when using SAM. But if you want to validate the input on the API Gateway, you have to define the API Gateway explicitly to add the API specification in more details by using the open standard Swagger / OpenAPI Spec. Let’s do this in the Resources section:

infrastructure/serverless.yml GitHub ApiGateway:

Type: 'AWS::Serverless::Api'

Properties:

StageName: Prod

DefinitionBody:

swagger: '2.0'

basePath: '/'

info:

title: Serverless

schemes:

- https



x-amazon-apigateway-request-validators:

basic:

validateRequestBody: true

validateRequestParameters: true

paths:

'/{n}' :

parameters:

- name: 'n'

in: path

description: 'N'

required: true

type: number

get:

produces:

- 'text/plain'

responses:

'200' :

description: 'factorial calculated'

schema:

type: number

x-amazon-apigateway-request-validator: basic

x-amazon-apigateway-integration:

httpMethod: POST

type: 'aws_proxy'

uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetFactorialLambda.Arn}/invocations'

passthroughBehavior: when_no_match



If you are familiar with Swagger / OpenApi Spec you will find nothing special besides the x-* parameters which are API Gateway specific. You can also monitor the API Gateway. To do so, append the following section the Resources section of your template:

infrastructure/serverless.yml GitHub ApiGateway5XXErrorAlarm:

Type: 'AWS::CloudWatch::Alarm'

Properties:

AlarmDescription: 'Api Gateway server-side errors captured'

Namespace: 'AWS/ApiGateway'

MetricName: 5 XXError

Dimensions:

- Name: ApiName

Value: !Ref ApiGateway

- Name: Stage

Value: Prod

Statistic: Sum

Period: 60

EvaluationPeriods: 1

Threshold: 1

ComparisonOperator: GreaterThanOrEqualToThreshold

AlarmActions:

- !Ref Alerts

