Skip to content

Sample options for implementing REST services using AWS Serverless Application Model.

License

Notifications You must be signed in to change notification settings

mindit-io/aws-sam-serverless-services

Repository files navigation

mindit.io

AWS SAM sample templates for serverless REST deployments

AWS Serverless Application Model it's a great framework to start building serverless applications. With just a few lines of configuration, you can define the application you want and deploy it.

AWS SAM hides all the boilerplate configurations needed if building directly using AWS CloudFormation resources and properties.

While this is great for a quick start, it can become very quickly frustrating when you want to implement slightly more complex functionalities, such as validation, authorization, transformation, etc.

This repository is intended as a storage for various AWS SAM templates, covering different deployments scenarios.

Currently it holds sample templates for:

Deployment Scenario SAM AWS template
Lambda Proxy Integration template-lpi.yaml
Lambda Proxy Integration with HTTP catch all template-lpi-catch-all.yaml
Lambda Proxy Integration with body content validation template-lpi-validate.yaml
Lambda Custom Integration template-li.yaml
Lambda Custom Integration with body content validation template-li-validate.yaml

In the future the list of sample templates will be extended with new deployment scenarios (incl transformation, authorization, etc).

Please refer to the following AWS documentation articles in order to understand the referenced concepts:

Prerequisites

In order to run those examples you need to have SAM CLI installed and configured.

Please follow your platform specific installation instructions.

Following those installations instructions you will end-up with:

  • an AWS account
  • AWS / SAM CLI installed and configured locally using the above account
  • an AWS S3 bucket which will be used during the deployment process

Deployment

Any of the examples can be deployed using the following commands:

sam build -t template.yaml  
sam package --template-file template.yaml --output-template-file packaged.yaml     --s3-bucket your_bucket_name 
sam deploy     --template-file packaged.yaml     --stack-name api-echo     --capabilities CAPABILITY_IAM

where:

  • template.yaml can be replaced with any of the sample templates file names
  • api-echo can be replaced with any name that you want to be assigned to the deployed API
  • your_bucket_name with the bucket name created when you've installed SAM CLI

API Functionality

All RESTs created by the sample templates are simple echo services. In general, they return the same information that is passeed in the body request. Particular implementation details are mentioned for each scenario.

APIs are powered by lambda functions. By default the runtime of lambda functions is nodejs10.x.

If you want to run the same examples using Java, just replace in the template the following configuration:

        FunctionName: echo-lpi
        CodeUri: EchoFunctionJS
        Handler: app.lambdaHandler
        Runtime: nodejs10.x

with:

        CodeUri: EchoFunction
        Handler: io.mindit.aws.handler.EchoHandler::handleRequest
        Runtime: java8
        MemorySize: 256

The simplest way to test the output of of the REST endpoint is by accessing the service in the API Gateway in the AWS console:

AWS api gateway

Lambda Proxy Integration

Template file: template-lpi.yaml

In a Lambda proxy integration, the entire client request is sent to the backend Lambda function as is. API Gateway maps the entire client request to the input event parameter of the backend Lambda function. The Lambda function's output, including status code, headers, and body, is returned to the client as is. For many use cases, this is the preferred integration type.

With a Lambda proxy integration, API Gateway requires the backend Lambda function to return output according to the following JSON format:

{
    "statusCode": "...", 
    "headers": {
        "custom-header": "..." 
    },
    "body": "...", 
    "isBase64Encoded": "..."
}

where:

  • statusCode = a valid HTTP status code
  • custom-header = custom headers that you want to be included in the response
  • body = body of the response as a JSON string
  • isBase64Encoded = true/false whether binary support should be included

Image below shows the behaivour of a serverless endpoint implemented using Lambda Proxy Integration. Entire client request (including headers, body and parameters) is mapped to the event object send to the function. Our lambda function returns the object that it receives in the request:

mindit.io

Template format for this kind of integration is very simple. The entire configuration can be done by using just a single resource:

Resources:
    EchoLpiApi:
      Type: AWS::Serverless::Function 
      Properties:
        FunctionName: echo-lpi
        CodeUri: EchoFunctionJS
        Handler: app.lambdaHandler
        Runtime: nodejs10.x
        Events:
          Request:
            Type: Api
            Properties:
              Path: /echo
              Method: post

Lambda Proxy Integration with HTTP catch all

Template file: template-lpi-catch-all.yaml

This method is applicable when you wish to use an API Gateway as a pure proxy, with little to no intervention on the incoming request. The business logic of the exposed API endpoint is comprised entirely within the Lambda function, which is wholly responsible for handling and responding to the request When the backend web server opens more resources for public access, the client can use these new resources with the same API setup. But this requiers that the service developer to communicate clearly to the client developer which are the new accesible resources, and the supported operations.

API Gateway is passing the "httpMethod" action to the lambda function: mindit.io

The sam AWS template configuration has to mention to the resource path {proxy+} and to include the catch-all ANY verb for the HTTP method.

Resources:

    EchoLpiApi:
      Type: AWS::Serverless::Function 
      Properties:
        FunctionName: echo-lpi-catch-all
        CodeUri: EchoFunctionJS
        Handler: app.lambdaHandler
        Runtime: nodejs10.x
        Events:
          ProxyApiGreedy:
            Type: Api
            Properties:
              Path: /echo/{proxy+}
              Method: ANY

Lambda Proxy Integration with body content validation

Template file: template-lpi-validate.yaml

Very often, instead of writing your validation code into the lambda function it is preferable to let the API Gateway manages that for you. You can configure API Gateway to perform basic validation of an API request before proceeding with the integration request. When the validation fails, API Gateway immediately fails the request, returns a 400 error response to the caller.

Figure below shows the results of the validation performed on a incomplete body request: mindit.io

API Gateway can validate both:

  • request parameters
  • request payload

SAM AWS template offers support to enable validation for lambda proxy integration by using "Request Model" or "Request Parameter" properties of the object describing an event source with type Api. Request model has to be configured in "Models" property of the "AWS::Serverless::API" resource:

EchoWithValidationAPI:
      Type: AWS::Serverless::Api
      Properties:
       ...
        Models: 
          Inventor:
            type: object
            required:
              - name
              - wiki
            properties:
              name:
                type: string
              wiki:
                type: string
              knownFor:
                type: string

              

    EchoWithValidationFunction:
      Type: AWS::Serverless::Function 
      Properties:
       ...
        Events:
          Request:
            Type: Api 
            Properties:
              RestApiId: 
                Ref: EchoWithValidationAPI
              RequestModel:
                Model: Inventor
                Required: true
    

Note that, even that this example is similar with the awslabs/serverless-application-model one, there seems to be an issue with this configuration and you still need to manually activate the validator in the API Gateway console: mindit.io

Lambda Custom Integration

Template file: template-li.yaml

Lambda proxy integration are very easy to set-up, but it offers you less control over the workflow.

If you target a better separation of responsibilities, between the integration layer and functional part of your application, you should look into utilizing lambda custom integration. For example, lambda custom integration can be used to transform the request payload by using Velocity Template Language engine or to map response codes and messages the ones understood by the client application.

As shown in the figure below the API Gateway is not packaging the entire client request, rather (if no transformers are used) the body of the request is forwarded as is to the lambda function: mindit.io

If you want to use custom integration, you need to use swagger to specify the interface definition:

    EchoLiApi:
      Type: AWS::Serverless::Api
      Properties:
        ...
        DefinitionBody:
          swagger: "2.0"
          paths:
            /echo:
              post:
                responses:
                  "200": 
                    description: "200 response"
                x-amazon-apigateway-integration:
                  uri:
                    Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${EchoLiFunc.Arn}/invocations
                  responses:
                    default:
                      statusCode: 200
                  passthroughBehavior: when_no_match
                  httpMethod: POST
                  type: AWS
                x-amazon-apigateway-any-method:
                  produces: application/json


    EchoLiFunc:
      Type: AWS::Serverless::Function 
      Properties:
        ...
        Events:
          Request:
            Type: Api
            Properties:
              RestApiId: !Ref EchoLiApi
              Path: /echo
              Method: post

Lambda Custom Integration with body content validation

Template file: template-li-validate.yaml

In case of custom integration the entire validation of the request should happen within the swagger definition:

Resources:


    EchoLiApi:
      Type: AWS::Serverless::Api
      Properties:
        Name: echo-li-validate
        StageName: Prod
        TracingEnabled: True
        DefinitionBody:
          swagger: "2.0"
          info:
            title: "EchoAPI"
          x-amazon-apigateway-request-validators:
            Validate Body: 
              validateRequestBody: True
              validateRequestParameters: False
          paths:
            /echo:
              post:
                consumes:
                - "application/json"
                produces:
                - "application/json"
                x-amazon-apigateway-request-validator: "Validate Body"
                parameters:
                - in: "body"
                  name: "Inventor"
                  required: true
                  schema: 
                    $ref: "#/definitions/Inventor"
                responses:
                  "200": 
                    description: "200 response"
                x-amazon-apigateway-integration:
                  uri:
                    Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${EchoLiFunc.Arn}/invocations
                  responses:
                    default:
                      statusCode: 200
                  passthroughBehavior: when_no_match
                  httpMethod: POST
                  type: AWS
                x-amazon-apigateway-any-method:
                  produces: application/json
          definitions: 
            Inventor:
              description: "Inventor model"
              type: "object"
              required:
                - "name"
                - "wiki"
              properties:
                name:
                  type: "string"
                wiki:
                  type: "string"
                knownFor:
                  type: "string"

About

Sample options for implementing REST services using AWS Serverless Application Model.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published