Repository: serverless/serverless (JavaScript)
Date: 2019-03-26 14:08:23+00:00
Task type: Feature

Issue:
Add support for AWS API Gateway Basic Request Validation
<!--
1. If you have a question and not a bug/feature request please ask it at http://forum.serverless.com
2. Please check if an issue already exists so there are no duplicates
3. Check out and follow our Guidelines: https://github.com/serverless/serverless/blob/master/CONTRIBUTING.md
4. Fill out the whole template so we have a good overview on the issue
5. Do not remove any section of the template. If something is not applicable leave it empty but leave it in the Issue
6. Please follow the template, otherwise we'll have to ask you to update it
-->

# This is a Feature Proposal

## Description

AWS API GW officially supports this now  - http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-request-validation.html

For example, it could be set as a new property somewhere in `service.function.events.http.validation` config object. 

Blog post: https://aws.amazon.com/blogs/compute/how-to-remove-boilerplate-validation-logic-in-your-rest-apis-with-amazon-api-gateway-request-validation/




Issue discussion:
👍
@pmuens This would be a great effort, and thanks for taking this as a feature (on April 12). And I am curious what the status/help-wanted label means here (April 12), does that mean you need some volunteer to implement this? (If so, I am interested further discussing this issue)

PS: here is a question I asked related this issue http://forum.serverless.com/t/can-serverless-offer-parameter-validation-capability/1919, where I listed why I feel this should be a great feature to add for Serverless team.
@syang help wanted means we would love this feature =)

Do you know if this is supported via cloudformation yet?
This method uses swagger as the source, but I was able to get this kind of working without any serverless code/plugin modifications:

1. Add the API gateway extensions to your swagger doc:
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html

2. Add the following to the resources section of serverless.yml
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html
```
    ApiGatewayRestApi:
      Type: AWS::ApiGateway::RestApi
      Properties:
        Name: ${self:service}-api
        Body: ${file(swagger.yml)}
```

This is not ideal in many ways.  For one, you have to inject the lambda uri/arn into the swagger doc.  Also, you have two conflicting api definitions in your cloud formation.... but it did work for me in a manual test.

Ideally there would be an option to have serverless reference the swagger doc directly for function definitinons, but dynamically inject the `uri` parameter when building the cloudformation.

@iadknet The approach you listed above sounds a bit too heavy to me (not saying this is bad, maybe there is no way avoiding the heaviness). Let me elaborate my thoughts from a user perspective:

- Serverless attracted me because of it's simplicity (hide the unnecessary CloudFormation syntax and etc). For this kind of users, who prefer simplicity, learning a full Swagger + Amazon extension might not be ideal route for them. (again, I am not sure if there could be way to hide / abstract that learning curve)






@syang I can see where you are coming from. We were attracted to Serverless for the way it simplified other aspects of creating lambda-based APIs.  It does a lot of things that we would have had to build ourselves. This includes a large part of the delivery pipeline, a great set of local development helpers, and easy configuration of IAM roles.

On the other side we have been spending a lot of time fighting against some of the abstractions in Serverless that run counter to our established toolchains and development patterns.

Swagger is already built into our development process as we try to follow a 'design-first' development pattern, by writing the API specification first and then writing our code so it implements the design.  There is a large ecosystem of swagger-centric tools that help enable this approach.

AWS also seems to be embracing Swagger/OpenAPI as the preferred method for defining API Gateway APIs.

So, in that aspect, Serverless is kind of cumbersome, because we are defining our APIs in two places, and have not been able to take full advantage of the Swagger feature set.

On the infrastructure side, we also prefer to use Terraform over CloudFormation (which I know is a whole separate philosophical battle that has been discussed to death).

So for us the Serverless project has been a bit of love/hate/frustration. The answer is we probably just need to build our own toolchain, learn how to write Serverless plugins, or hope someone else writes plugins that cover our use-cases :)
@iadknet I fully understand your point of view. For me both ways of API definitions are valid, but different approaches. 

IMO a Swagger integration to define the API can be integrated as plugin that would read the API definition from a Swagger definition and use that to create the Serverless service configuration internally. 

Of course it is not an easy pick - there are lots of edge cases and general procedures that had to be defined how such a plugin would and could work, especially how to integrate lambda functions with endpoints. Writing plugins themselves has become quite easy with the latest few versions of Serverless in general.

I am sure, that such a plugin - including its planning, finding solutions, etc. can be designed, planned and created with help of the community in a quite reliable way as project/repo on GitHub. As long as there is enough demand and engagement.

@syang thanks for asking!

@DavidWells already answered your question, but I just wanted to echo it again: We'd love to get some help with this feature. That doesn't necessarily mean that it needs to be in the shape of a feature complete PR. A discussion about the potential implementation is yet another way how this help can be expressed, so 👍 for chiming in and starting the discussion.

@iadknet thanks for providing a potential solution for it! 💪 And thanks a lot for the feedback in general. Feedback like this helps us to understand the pain points and guides the planning of the Framework.

It would be nice if we could get this working with core CloudFormation and w/o Swagger / OpenAPI. Our `http` event definition is heavily built atop of CloudFormation. Native Swagger support is not planned anytime soon (yet).
I do still think an API Gateway Basic Request Validation plugin would be best served by by wrapping it in a Swagger plugin implementation.

To create a Request Validation plugin would require the following:


- Create a design that allows defining models/schema in serverless.yml
- Have each function define its response model?
- Have the ability to define a model and re-use it across multiple functions?

Once you have answered and implemented the above, you have essentially created a competing standard for Swagger, which is just a collection of API endpoint and model definitions in yml format.

Also, with `AWS::ApiGateway::RestApi` Swagger *is* valid CloudFormation... and it appears that Amazon is moving towards using Swagger to drive API definitions via CloudFormation/SAM.  For example, I think Swagger might be the only way to enable request validation via CloudFormation (I may be wrong about that).

I do have some interest in tackling the creation of a Swagger plugin, but I am a pretty novice Node developer.  I know enough Node to be able to write a simple Express app, or simple Lambdas, but I haven't done anything overly complex.

I did start to look into what it might take to write a plugin recently, but got bogged down by trying track down documentation around lifecycleEvents, and figuring out what I needed to hook into/override.

The functionality I was hoping to implement:

- Skip the creation of `AWS::ApiGateway::Resource` and `AWS::ApiGateway::Method` items
- Read in swagger.yml and inject the `x-amazon-apigateway-integration: uri: xxxxxxx` property to reference the arn for the corresponding `AWS::Lambda::Function` resource
- Add a `Body` property to `ApiGatewayRestApi` with the modified swagger.yml

As a bonus it would be nice to use swagger.yml as a source for local development functionality such as invoke local/offline.  There are some other plugins that can take swagger.yml and generate function definitions in serverless.yml, which could be a workaround. Even nicer would be to have the plugin override the Serverless logic that reads from serverless.yml for those definitions, and reference swagger when building its internal data structures.  I don't know how hard/brittle that would be.

Time and learning curve permitting, I would be interested in tacking a crack at at least some of the above functionality.   Any pointers in where to start looking would be helpful. 
@iadknet @DavidWells @pmuens 

> Time and learning curve permitting, I would be interested in tacking a crack at at least some of the above functionality. Any pointers in where to start looking would be helpful.

Maybe some kind of meeting and design discussion session (whiteboard style) would help. I'd be happy to see how this may leads to. 




@iadknet thanks for the very interesting implementation proposal. Would love to see such an implementation as a plugin. We're currently looking into ways how we can enrich the discovery of plugin lifecycle events and aid plugin developer here (see #2821).

@syang 

> Maybe some kind of meeting and design discussion session (whiteboard style) would help. I'd be happy to see how this may leads to.

That sounds like a good idea. I'd be in for that 👍
@pmuens I could provide a whiteboard discussion / conference facility if people are interested in this idea. If people in this thread are all in SF, we should have a casual in-person meeting + whiteboard discussion.
> I could provide a whiteboard discussion / conference facility if people are interested in this idea.

@syang that would work best for me 👍 . I'm not in SF right now so in-person meeting wouldn't be possible for me.
Is there any further progress on this issue?
> Is there any further progress on this issue?

Thanks for commenting @sbkn 👍 

AFAIK this issue is still in progress and needs some more discussion so that we get a final implementation proposal in place.

Do you have some feedback on the current comments and implementation proposals in this thread @sbkn?
> Do you have some feedback on the current comments and implementation proposals in this thread @sbkn?

I'm curious whether we can set a model defined in APIG as validation basis without using swagger at all. In the console you'd go to `Method Request`, set `Request Validator` to f.e. Validate body and add a model in the `Request body` - that's it.

The models needed you'd add f.e. w/ `serverless-aws-documentation`.
Thanks for getting back @sbkn 👍 

> I'm curious whether we can set a model defined in APIG as validation basis without using swagger at all.

IMO only using raw CloudFormation (and not introducing Swagger / OpenAPI) would be the best solution here.

Does anyone know if API Gateway supports such a feature via CloudFormation?
API Gateway has some extensions to Swagger (listed here: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html) of which request validators are included. I haven't experimented with those specific extensions, but have had success with others.

EDIT: Whoooooops - just realised you probably meant without using Swagger/OpenAPI. My bad!
> API Gateway has some extensions to Swagger (listed here: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html) of which request validators are included. I haven't experimented with those specific extensions, but have had success with others.

Thanks for the comment @raids 👍

> EDIT: Whoooooops - just realised you probably meant without using Swagger/OpenAPI. My bad!

No worries! Yes, implementing this w/o Swagger / OpenAPI would be great!
I *think* that request validation is now supported in Cloudformation via the `Schema` property: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-model.html
Btw do you guys know how to define and create a model an apply it to a method in the same cloudformation? 
@0libre I have done something like this and it creates the model perfectly fine in backend. But does not apply validators to my methods :( 

```
resources:
  Resources:
    PetsModelNoFlatten: 
      Type: "AWS::ApiGateway::Model"
      Properties: 
        RestApiId: {Ref: ApiGatewayRestApi}
        ContentType: "application/json"
        Description: "Schema for Pets example"
        Name: "PetsModelNoFlatten"
        Schema: 
          Fn::Join: 
            - ""
            - 
              - "{"
              - "   \"$schema\": \"http://json-schema.org/draft-04/schema#\","
              - "   \"title\": \"PetsModelNoFlatten\","
              - "   \"type\": \"array\","
              - "   \"items\": {"
              - "       \"type\": \"object\","
              - "       \"properties\": {"
              - "           \"number\": { \"type\": \"integer\" },"
              - "           \"class\": { \"type\": \"string\" },"
              - "           \"salesPrice\": { \"type\": \"number\" }"
              - "       }"
              - "   }"
              - "}"
```
I think the missing piece to this puzzle is that in combination with the model definition, request validation needs to be enabled for the method request: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-requestvalidator.html

```
"$schema": "http://json-schema.org/draft-04/schema#"
title: InputModel
id: "#InputModel"
type: object
properties:
  id:
    type: string
  state:
    type: string
    enum:
      - NEW
      - DOWNLOAD
      - VERIFY
      - CLEAR
      - INSTALL
      - REBOOT
      - SUCCESS
      - ERROR
```

After defining a model like the above, I went into the APIG console and manually turned on validation, then tested it.  I got a 400 error to the client as expected, and in the logs:

```(0ed9ced3-a2f2-11e7-b6eb-bbbc06515723) Request body does not match model schema for content type application/json: [instance value ("GRARFELB") not found in enum (possible values: ["NEW","DOWNLOAD","VERIFY","CLEAR","INSTALL","REBOOT","SUCCESS","ERROR"])]```
@mjmac Yes - the fact you can apply created resources to methods in APIG console I know. But I got quite complex API where doing it manually for every method would be pointless hence I do for now validation in code until I can figure out how to do that automatically
@RafPe Sure, I wasn't suggesting that manually enabling validation was a solution - just pointing out that it does work with defined models and that there is CloudFormation support for this. I had the impression that there was some doubt that CloudFormation supported it, which is a requirement for the serverless framework to drive it. I have been exploring serverless-aws-documentation for model definition. Maybe extending it to optionally enable validation would be the path of least resistance?
I would be really interested if it would be possible to hook this into cloudformation template - so the resources could be created and assigned to methods. That would make life so much easier in terms of validation and schema control :)

Will try to do some more research and report back if I find something
https://forums.aws.amazon.com/thread.jspa?messageID=787077

So close! But we're not there yet. We can define validators via CloudFormation now, but there is still no way to attach a validator to an APIG method yet. The good news is that it seems like it should be possible sometime soon. When it is, perhaps the framework should support it with new directives like the following:

```
provider:
  name: aws
 ...
  validators:
    all:
      body: true
      parameters: true
    params:
      parameters: true
...
functions:
  function1:
    events:
      - http:
          path: /foo/bar/{baz}
          method: POST
          validation: all
```

This would result in generated CloudFormation like the following:
```
  RequestValidatorAll:
    Type: AWS::ApiGateway::RequestValidator
    Properties:
      Name: All
      RestApiId: 
        Ref: ApiGatewayRestApi
      ValidateRequestBody: true
      ValidateRequestParameters: true
  RequestValidatorParams:
    Type: AWS::ApiGateway::RequestValidator
    Properties:
      Name: Params
      RestApiId: 
        Ref: ApiGatewayRestApi
      ValidateRequestBody: false
      ValidateRequestParameters: true
  ApiGatewayMethodFunction1:
    Type: AWS::ApiGateway::Method
    Properties:
      ...
      RequestValidator: <-- DOES NOT EXIST YET, BUT SOON?
        Ref: RequestValidatorAll
```
I would say we would need to have something a bit different. Look at the situation where we create multiple models by defining resources ( that we already can create ) but then in functions we specify the model name ( or reference to model ) so in different parts of API we can apply different schemas

```
...
functions:
  function1:
    events:
      - http:
          path: /foo/bar/{baz}
          method: POST
          request_model: ModelNameComesHere
```

btw - I think there is plugin for it - have not tested - https://github.com/9cookies/serverless-aws-documentation
I think we're talking about two aspects of the same thing... I was coming at it from already having defined models via the serverless-aws-documentation plugin, but I see where you're coming from. It would be nice to have a canonical way of doing request validation, but I wouldn't want to lose the flexibility of being able to do it either way.
I will try connecting those 2 and see if this will do what I need :D
@RafPe Any luck with using `serverless-aws-documentation` to attach validators?  I'm looking at doing the exact same thing.
Any updates on this issue? Support for `validation: all / params-only / body-only` would be a great addition.
We'd be very interested in this as well
:+1: 
@matttowerssonos @mjmac Hey - been quite distracted with other projects so managed to test it today .... and it `works awesomely` 

So in my serverless.yml I have added required variables as described on plugin page 

```
  documentation:
    models:
      -
        name: "testus"
        description: "This is a test"
        contentType: "application/json"
        schema: ${file(apis/models/testus.json)}   
```

And then in method which I wanted to apply the model to 
```
  register:
    handler: apis/user/register.register
    events:
      - http:
          documentation:
            summary: "Register user"
            description: "Registers new user"
            tags:
              - "user"
              - "create"
            requestBody:
              description: "Request body description"
            requestModels:
              "application/json": "testus"      
          path: user/register
          method: post
          cors: true
          private: true
```

Since I just need models that was all I added. As you noticed I need to specify JSON schema file. This one I got by using schema generator from https://jsonschema.net/ 

Then I just deployed my functions ... and everything was as expected. So this opens the doors for me to remove redundant validation code for post messages ;) 

Hope it helps! 
@RafPe Did you find a way to attach the RequestValidator to the Method enabling the validation for this Model?

The Cloudformation for this now exists but I am unable to figure out how we can implement this using serverless because the model resource is created using serverless so I cannot add RequestValidatorId after the CF is built.

http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-method.html#cfn-apigateway-method-requestvalidatorid
@db-beachbody I have found a way :) At the moment I have written simple plugin that will attach selected `RequestValidator` into selected methods. I will post the plugin repo URL once it will be in state of being usable by others ;) 

But it works 👍 

..... so it has not taken so long and plugin is here => https://github.com/RafPe/serverless-reqvalidator-plugin 
Have been working for me without any problems. Enjoy ppl 👍 
@RafPe Thank you so much, this has saved me so much time and prevented me from having to create an ugly hack.
@RafPe plugin works great, although it took me a while to discover it actually depends on [documentation plugin](https://github.com/9cookies/serverless-aws-documentation)

If someone gets to read this far, this is FULL example that works:
```yaml
service:
  name: my-service

plugins:
  - serverless-webpack
  - serverless-reqvalidator-plugin
  - serverless-aws-documentation

provider:
  name: aws
  runtime: nodejs6.10
  region: eu-west-2
  environment:
    NODE_ENV: ${self:provider.stage}
custom:
  documentation:
    api:
      info:
        version: '1.0.0'
        title: My API
        description: This is my API
      tags:
        -
          name: User
          description: User Management
    models:
      - name: MessageResponse
        contentType: "application/json"
        schema:
          type: object
          properties:
            message:
              type: string
      - name: RegisterUserRequest
        contentType: "application/json"
        schema:
          required: 
            - email
            - password
          properties:
            email:
              type: string
            password:
              type: string
      - name: RegisterUserResponse
        contentType: "application/json"
        schema:
          type: object
          properties:
            result:
              type: string
      - name: 400JsonResponse
        contentType: "application/json"
        schema:
          type: object
          properties:
            message:
              type: string
            statusCode:
              type: number
  commonModelSchemaFragments:
    MethodResponse400Json:
      statusCode: '400'
      responseModels:
        "application/json": 400JsonResponse

functions:
  signUp:
    handler: handler.signUp
    events:
      - http:
          documentation:
            summary: "Register user"
            description: "Registers new user"
            tags:
              - User
            requestModels:
              "application/json": RegisterUserRequest
          method: post
          path: signup
          reqValidatorName: onlyBody
          methodResponses:
              - statusCode: '200'
                responseModels:
                  "application/json": RegisterUserResponse
              - ${self:custom.commonModelSchemaFragments.MethodResponse400Json}

package:
  include:
    handler.ts

resources:
  Resources:
    onlyBody:  
      Type: "AWS::ApiGateway::RequestValidator"
      Properties:
        Name: 'only-body'
        RestApiId: 
          Ref: ApiGatewayRestApi
        ValidateRequestBody: true
        ValidateRequestParameters: false
```

@cortopy Thx! Great u got it working. I updated my docs on that topic as well
This is exactly what I was looking for!  Does this support nested models where in swagger you have a property and you use the $ref: '#/definitions/myOtherModel' . If so what is the syntax?
Sorry. Me bad.  I found the way in the serverless-aws-documentation plugin
$ref: "{{model: MyOtherModel}}"
Will this work with Java instead of Node?  It looks like webpack is choking on my handler which is defined as a Java package like  "handler: com.my.package.handler" .  I get an error on deploy saying No matching handler found for 'com.my.package'. Check your service definition.  Is this compatible or do I need some additional configuration to handle java packages?

@jjkirby I'm using this in Java and haven't had any problems so far.

For example, with the package `com.serverless.SampleHandler` and serverless.yml:
```
functions:
  sample:
    handler: com.serverless.SampleHandler
```

Make sure you build the zip/package before deploy.  This adds a `buildZip` task into `build.gradle` with the Zip name, and a `package` setting within `serverless.yml`.  I'm using the [Gradle template](https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/aws-java-gradle) which added this for me.  So I just have to use `build gradle` prior to `sls deploy`.
I figured it out.  I didn't need serverless-webpack plugin because I believe that is for node based implementations.  Removed that plugin and it went fine 
Folks, thanks for your efforts...

I am seeing behaviour where if the Content-Type is either not set or set wrongly in the HTTP request, the request is passed directly to the lambda function without validation. Where Content-Type matches the template (application/json) than validation occurs correctly. 

The occurs in both lambda and lambda_proxy integration approaches.

What I am looking to achieve is to have all requests rejected (by validation) which do not correctly validate to an expected Content-Type (as defined in serverless.yml).  I am using serverless-aws-documentation and serverless-reqvalidator-plugin

**integration:lamba (with passThrough: NEVER)**
Content-Type: application/json => validated correctly.
Content-Type: application/x-www-form-urlencoded => Passthrough (i.e. not validated)
Content-Type anything else => Unsupported Media Type (correct behaviour)

I can see from the serverless code that where integraton=lambda default RequestTemplates of 'application/json' and 'application/x-www-form-urlencoded' are added; there doesn't seem to be a way to suppress this.  If this RequestTemplate is manually deleted via the console then I get the behaviour I want (i.e. all requests are validated). 

I have also tried adding a requestModel for application/x-www-form-urlencoded which serverless seems to correctly load to AWSGateway but doesn't seem to correctly validate.

**integration:lambda_proxy**
Content-Type: application/json => validated correctly.
Content-Type anything else => Passthrough (i.e. not validated)

The lambda_proxy documentation is quite clear that anything which isn't matched is passed to the lambda function, so this isn't too surprising.  I guess one approach could be to validate the Content-Type in the lambda function, but it would be a better solution for an invalid Content-Type to never reach the function.

**Config files**
serverless.yml
```service: sportsmgr

frameworkVersion: ">=1.1.0 <2.0.0"

plugins:
  - serverless-plugin-typescript
  - serverless-dynamodb-local
  - serverless-offline
  - serverless-aws-documentation
  - serverless-reqvalidator-plugin

package:
  include:
    - config
    - models

custom:
  documentation:
    info:
      ...cut...
    models:
      -
        name: "ErrorResponse"
        description: "This is an error"
        contentType: "application/json"
        schema: ${file(models/errorResponse.json)}
      -
        name: "ObjectID"
        description: "Object ID"
        contentType: "application/json"
        schema: ${file(models/objectid.json)}
      -
        name: "TenantData"
        description: "Tenant Data Object"
        contentType: "application/json"
        schema: ${file(models/tenant.json)}
      -
        name: "TenantDataUrlencoded"
        description: "Tenant Data Object"
        contentType: "application/x-www-form-urlencoded"
        schema: ${file(models/tenant.json)}

  commonModelSchemaFragments:
    ErrorResponse400:
      statusCode: "400"
      responseModels:
        "application/json": "ErrorResponse"
    ErrorResponse500:
      statusCode: "500"
      responseModels:
        "application/json": "ErrorResponse"

provider:
  name: aws
  stage: dev
  region: eu-west-1
  runtime: nodejs8.10

functions:
  tenant_create:
    handler: src/handlers/tenant.createHandler
    events:
      - http:
          path: /tenants
          method: post
          cors: true
          reqValidatorName: 'xMyRequestValidator'
          integration: lambda
          request:
            passThrough: NEVER
          documentation:
            summary: "Create a new tenant"
            description: "Create a new tenant"
            requestModels:
              "application/json": "TenantData"
              "application/x-www-form-urlencoded": "TenantDataUrlencoded"
            methodResponses:
              -
                statusCode: "200"
                responseModels:
                  "application/json": "ObjectID"
              - ${self:custom.commonModelSchemaFragments.ErrorResponse400}
              - ${self:custom.commonModelSchemaFragments.ErrorResponse500}

resources:
  Resources:
    xMyRequestValidator:
      Type: "AWS::ApiGateway::RequestValidator"
      Properties:
        Name: 'my-req-validator'
        RestApiId:
          Ref: ApiGatewayRestApi
        ValidateRequestBody: true
        ValidateRequestParameters: true
```

models/tenant.json
```{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "tenant",
  "type": "object",
  "properties": {
    "title": {
      "type": "string"
    }
  },
  "required": [
    "title"
  ],
  "additionalProperties": false
}
```
@mcroker did you find a solution to the issue posted above?
Is this not what [Custom Request Templates](https://serverless.com/framework/docs/providers/aws/events/apigateway#custom-request-templates) are about?

Solution patch:
diff --git a/docs/providers/aws/events/apigateway.md b/docs/providers/aws/events/apigateway.md
index 0a50158a4b8..b319692e4e6 100644
--- a/docs/providers/aws/events/apigateway.md
+++ b/docs/providers/aws/events/apigateway.md
@@ -24,6 +24,7 @@ layout: Doc
     - [Setting API keys for your Rest API](#setting-api-keys-for-your-rest-api)
     - [Configuring endpoint types](#configuring-endpoint-types)
     - [Request Parameters](#request-parameters)
+    - [Request Schema Validation](#request-schema-validation)
     - [Setting source of API key for metering requests](#setting-source-of-api-key-for-metering-requests)
   - [Lambda Integration](#lambda-integration)
     - [Example "LAMBDA" event (before customization)](#example-lambda-event-before-customization)
@@ -641,6 +642,50 @@ functions:
                 id: true
 ```
 
+### Request Schema Validators
+
+To use request schema validation with API gateway, add the [JSON Schema](https://json-schema.org/)
+for your content type. Since JSON Schema is represented in JSON, it's easier to include it from a
+file.
+
+```yml
+functions:
+  create:
+    handler: posts.create
+    events:
+      - http:
+          path: posts/create
+          method: post
+          request:
+            schema:
+              application/json: ${file(create_request.json)}
+```
+
+A sample schema contained in `create_request.json` might look something like this:
+
+```json
+{
+  "definitions": {},
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "type": "object",
+  "title": "The Root Schema",
+  "required": [
+    "username"
+  ],
+  "properties": {
+    "username": {
+      "type": "string",
+      "title": "The Foo Schema",
+      "default": "",
+      "pattern": "^[a-zA-Z0-9]+$"
+    }
+  }
+}
+```
+
+**NOTE:** schema validators are only applied to content types you specify. Other content types are
+not blocked.
+
 ### Setting source of API key for metering requests
 
 API Gateway provide a feature for metering your API's requests and you can choice [the source of key](https://docs.aws.amazon.com/apigateway/api-reference/resource/rest-api/#apiKeySource) which is used for metering. If you want to acquire that key from the request's X-API-Key header, set option like this:
diff --git a/lib/plugins/aws/lib/naming.js b/lib/plugins/aws/lib/naming.js
index 7f78238c3af..69d3f344dc8 100644
--- a/lib/plugins/aws/lib/naming.js
+++ b/lib/plugins/aws/lib/naming.js
@@ -242,6 +242,13 @@ module.exports = {
   getMethodLogicalId(resourceId, methodName) {
     return `ApiGatewayMethod${resourceId}${this.normalizeMethodName(methodName)}`;
   },
+  getValidatorLogicalId(resourceId, methodName) {
+    return `${this.getMethodLogicalId(resourceId, methodName)}Validator`;
+  },
+  getModelLogicalId(resourceId, methodName, contentType) {
+    return `${this.getMethodLogicalId(resourceId, methodName)}${_.startCase(
+      contentType).replace(' ', '')}Model`;
+  },
   getApiKeyLogicalId(apiKeyNumber, apiKeyName) {
     if (apiKeyName) {
       return `ApiGatewayApiKey${this.normalizeName(apiKeyName)}${apiKeyNumber}`;
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js
index 77fd557c18f..0d12adb4dbe 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js
@@ -30,6 +30,8 @@ module.exports = {
 
       const methodLogicalId = this.provider.naming
         .getMethodLogicalId(resourceName, event.http.method);
+      const validatorLogicalId = this.provider.naming
+        .getValidatorLogicalId(resourceName, event.http.method);
       const lambdaLogicalId = this.provider.naming
         .getLambdaLogicalId(event.functionName);
 
@@ -67,6 +69,42 @@ module.exports = {
 
       this.apiGatewayMethodLogicalIds.push(methodLogicalId);
 
+      if (event.http.request && event.http.request.schema) {
+        for (const requestSchema of _.entries(event.http.request.schema)) {
+          const contentType = requestSchema[0];
+          const schema = requestSchema[1];
+
+          const modelLogicalId = this.provider.naming
+            .getModelLogicalId(resourceName, event.http.method, contentType);
+
+          template.Properties.RequestValidatorId = { Ref: validatorLogicalId };
+          template.Properties.RequestModels = { [contentType]: { Ref: modelLogicalId } };
+
+          _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
+            [modelLogicalId]: {
+              Type: 'AWS::ApiGateway::Model',
+              Properties: {
+                RestApiId: {
+                  Ref: this.provider.naming.getRestApiLogicalId(),
+                },
+                ContentType: contentType,
+                Schema: schema,
+              },
+            },
+            [validatorLogicalId]: {
+              Type: 'AWS::ApiGateway::RequestValidator',
+              Properties: {
+                RestApiId: {
+                  Ref: this.provider.naming.getRestApiLogicalId(),
+                },
+                ValidateRequestBody: true,
+                ValidateRequestParameters: true,
+              },
+            },
+          });
+        }
+      }
+
       _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
         [methodLogicalId]: template,
       });
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
index f0b724b6829..b435c8fc3be 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
@@ -98,7 +98,9 @@ module.exports = {
             if (http.request) {
               const keys = Object.keys(http.request);
               const allowedKeys =
-                http.integration === 'AWS_PROXY' ? ['parameters'] : ['parameters', 'uri'];
+                http.integration === 'AWS_PROXY'
+                  ? ['parameters', 'schema']
+                  : ['parameters', 'uri', 'schema'];
 
               if (!_.isEmpty(_.difference(keys, allowedKeys))) {
                 const requestWarningMessage = [


Test patch:
diff --git a/lib/plugins/aws/lib/naming.test.js b/lib/plugins/aws/lib/naming.test.js
index da0557213e5..3eee2e78751 100644
--- a/lib/plugins/aws/lib/naming.test.js
+++ b/lib/plugins/aws/lib/naming.test.js
@@ -390,6 +390,22 @@ describe('#naming()', () => {
     });
   });
 
+  describe('#getValidatorLogicalId()', () => {
+    it('', () => {
+      expect(sdk.naming.getValidatorLogicalId(
+        'ResourceId', 'get'
+      )).to.equal('ApiGatewayMethodResourceIdGetValidator');
+    });
+  });
+
+  describe('#getModelLogicalId()', () => {
+    it('', () => {
+      expect(sdk.naming.getModelLogicalId(
+        'ResourceId', 'get', 'application/json'
+      )).to.equal('ApiGatewayMethodResourceIdGetApplicationJsonModel');
+    });
+  });
+
   describe('#getApiKeyLogicalId(keyIndex)', () => {
     it('should produce the given index with ApiGatewayApiKey as a prefix', () => {
       expect(sdk.naming.getApiKeyLogicalId(1)).to.equal('ApiGatewayApiKey1');
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
index 705e477ce50..78ef18d3798 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
@@ -57,6 +57,54 @@ describe('#compileMethods()', () => {
     };
   });
 
+  it('should have request validators/models defined when they are set', () => {
+    awsCompileApigEvents.validated.events = [
+      {
+        functionName: 'First',
+        http: {
+          path: 'users/create',
+          method: 'post',
+          integration: 'AWS',
+          request: { schema: { 'application/json': { foo: 'bar' } } },
+        },
+      },
+    ];
+    return awsCompileApigEvents.compileMethods().then(() => {
+      expect(
+        awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources.ApiGatewayMethodUsersCreatePostValidator
+      ).to.deep.equal({
+        Type: 'AWS::ApiGateway::RequestValidator',
+        Properties: {
+          RestApiId: { Ref: 'ApiGatewayRestApi' },
+          ValidateRequestBody: true,
+          ValidateRequestParameters: true,
+        },
+      });
+      expect(
+        awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources.ApiGatewayMethodUsersCreatePostApplicationJsonModel
+      ).to.deep.equal({
+        Type: 'AWS::ApiGateway::Model',
+        Properties: {
+          RestApiId: { Ref: 'ApiGatewayRestApi' },
+          ContentType: 'application/json',
+          Schema: { foo: 'bar' },
+        },
+      });
+      expect(
+        awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources.ApiGatewayMethodUsersCreatePost.Properties.RequestModels
+      ).to.deep.equal({
+        'application/json': { Ref: 'ApiGatewayMethodUsersCreatePostApplicationJsonModel' },
+      });
+      expect(
+        awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources.ApiGatewayMethodUsersCreatePost.Properties.RequestValidatorId
+      ).to.deep.equal({ Ref: 'ApiGatewayMethodUsersCreatePostValidator' });
+    });
+  });
+
   it('should have request parameters defined when they are set', () => {
     awsCompileApigEvents.validated.events = [
       {