How do I set up a model to use for validation in a lambda function in a SAM template?
Asked Answered
E

1

2

I am trying to set up an AWS API Gateway with SAM templates. I want to use defined models to validate the incoming event data before inserting it into a database, which I have done through the interface, but I need to be able to do it through code. Unfortunately, I am getting a variety of errors on deploying code to AWS, and running it locally does not validate the incoming data. Here is my template.yaml file:

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  company

Resources:
  ApiGatewayApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Models:
        company:
          $schema: 'http://json-schema.org/draft-04/schema#'
          type: object
          properties: 
            name:
              type: string
            email:
              type: string
            website:
              type: string
            phone:
              type: string
          required:
            - name
            - phone

  PostCompanyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/functions
      Handler: company.postCompany
      Runtime: nodejs14.x
      Architectures:
        - x86_64
      Events:
        Company:
          Type: Api
          Properties:
            Path: /company
            Method: post
            RestApiId: !Ref ApiGatewayApi
            RequestModel:
              Model: !Ref company
              Required: true
              ValidateBody: true
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: "es2020"
        Sourcemap: true
        EntryPoints:
          - company.ts

This code works fine without the validation (in that it can successfully create a company entry), but when I try to reference the company model for validation I get the following error on sam deploy

Error: Failed to create changeset for the stack: companyAPI, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: Transform AWS::Serverless-2016-10-31 failed with: Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [PostCompanyFunction] is invalid. Event with id [Company] is invalid. Unable to set RequestModel [{u'Ref': u'company'}] on API method [post] for path [/company] because the related API does not contain valid Models.

I've tried removing !Ref from the model property of the request model, but that shows this error instead:

Error: Failed to create changeset for the stack: companyAPI, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: Unresolved resource dependencies [ServerlessRestApi] in the Outputs block of the template

I've read through just about every resource I can find on this and still haven't been able to make it work. Any help is greatly appreciated.

Resources used so far:

AWS SAM - Enforcing Request Validation in API Gateway Method by SAM Template

https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html

https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html#sam-api-models

How to add a request validator in a AWS SAM template for AWS::Serverless::Api?

https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-requestmodel.html

https://github.com/aws/aws-sam-cli/issues/364

I also found the request and PR that added that RequestModel property, but still no luck making it work.

Exhibitionist answered 20/5, 2022 at 22:3 Comment(0)
E
3

Ok, I think I finally figured it out. ApiGatewayApi is a reserved word, so I had to rename that in my resources. You also have to use that resource name in your output templates. Here's the final template that does check the request body against a defined model. Also worth noting that this does not work when running with sam local start-api because the model resides in API Gateway, and that is not spun up in a docker container. This does, however, work when you run sam deploy.

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  company-service

Resources:
  CompanyRestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Models:
        companyModel:
          type: object
          required:
            - name
            - phone
          properties: 
            name:
              type: string
            email:
              type: string
            website:
              type: string
            phone:
              type: string
  # Company Endpoints
  PostCompanyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/functions
      Handler: company.postCompany
      Runtime: nodejs14.x
      Architectures:
        - x86_64
      Events:
        Company:
          Type: Api
          Properties:
            Path: /company
            Method: post
            RestApiId: !Ref CompanyRestApi
            RequestModel:
              Model: companyModel
              Required: true
              ValidateBody: true
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: "es2020"
        Sourcemap: true
        EntryPoints:
          - company.ts

Outputs:
# Company Outputs
  PostCompanyFunctionApi:
    Description: "API Gateway endpoint URL for Prod stage for PostCompaniesFunction"
    Value: !Sub "https://${CompanyRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/company"
  PutCompanyFunctionApi:
    Description: "API Gateway endpoint URL for Prod stage for 
Exhibitionist answered 23/6, 2022 at 19:19 Comment(1)
Awesome, yo saved my day :). Please, consider your answer as the accepted oneMozell

© 2022 - 2024 — McMap. All rights reserved.