DianaIonita / serverless-api-gateway-throttling

A plugin for the Serverless framework which configures throttling for API Gateway endpoints.
ISC License
71 stars 7 forks source link

Unsupported apiGateway.restApiId object #32

Open neeraj87 opened 1 year ago

neeraj87 commented 1 year ago

My objective is to add throttling to one of the public facing apis using APIGateway (rest api). I am using Lambda functions with Serverless framework.

My serverless.yml file looks like this

service: "my-service"
frameworkVersion: '3'

package:
  individually: true

plugins:
  - serverless-domain-manager
  - serverless-plugin-log-retention
  - serverless-prune-plugin
  - serverless-bundle
  - serverless-api-gateway-throttling

custom:
  stage: ${opt:stage}
  region: ${env:REGION, 'us-east-2'}
  accountId: ${ssm:accountId}
  product: "my-service"
  authorizers:
    prod: arn:aws:lambda:${self:custom.region}:${self:custom.accountId}:function:authorizer-func-prod
    stage: arn:aws:lambda:${self:custom.region}:${self:custom.accountId}:function:authorizer-func-stage
    dev: arn:aws:lambda:${self:custom.region}:${self:custom.accountId}:function:authorizer-func-stage
  logRetentionPeriod:
    dev: 14
    prod: 30
  customDomain:
    domainName: ${self:custom.product}-${self:custom.stage}.myservice.com
    basePath: ""
    stage: ${self:custom.stage, 'dev'}
    certificateName: "*.myservice.com"
    endpointType: "regional"
    createRoute53Record: true
    securityPolicy: tls_1_2
    apiType: rest
    autoDomain: true
  serverless-offline:
    noPrependStageInUrl: true

provider:
  name: aws
  runtime: nodejs14.x
  stage: ${self:custom.stage}
  region: ${self:custom.region}
  memorySize: 256
  timeout: 30
  deploymentBucket:
    name: ${self:custom.product}-deployment-bucket
    serverSideEncryption: AES256
  apiGateway:
    restApiId:
      Ref: ApiGatewayRestApi
    restApiRootResourceId:
     Fn::GetAtt:
          - ApiGatewayRestApi
          - RootResourceId
    binaryMediaTypes:
      - "multipart/form-data"
  environment:
    region: ${self:custom.region}
    stage: ${self:custom.stage}
    module: ${self:service}
    accountId: ${self:custom.accountId}
    DB_USER: ${ssm:/${self:custom.stage}/mongo/db/user}
    DB_USER_PASSWORD: ${ssm:/${self:custom.stage}/mongo/db/password}
    DB_NAME: leads
    DB_HOST: ${ssm:/${self:custom.stage}/mongo/db/host}

functions:
  PrivateAPI:
    name: ${self:service}-${self:custom.stage}-api
    handler: src/lambda.handler
    logRetentionInDays: ${self:custom.logRetentionPeriod.${self:custom.stage}}
    events:
      - http:
          method: any
          path: /api/{proxy+}
          authorizer:
            arn: ${self:custom.authorizers.${self:provider.stage}}
            resultTtlInSeconds: 0
            identitySource: method.request.header.Authorization
            identityValidationExpression: ^Bearer.+
          cors:
            origins:
              - "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
            allowCredentials: true
            maxAge: 86400
  PublicAPI:
    name: ${self:service}-${self:custom.stage}-public-api
    handler: src/lambda.handler
    logRetentionInDays: ${self:custom.logRetentionPeriod.${self:custom.stage}}
    events:
      - http:
          method: any
          path: /public/{proxy+}
          throttling:
            maxRequestsPerSecond: 100
            maxConcurrentRequests: 500
          cors:
            origins:
              - "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
            allowCredentials: true
            maxAge: 86400

resources:
  Resources:
    ApiGatewayRestApi:
      Type: AWS::ApiGateway::RestApi
      Properties:
        Name: ${self:service}-${self:custom.stage}
    GatewayResponse:
      Type: "AWS::ApiGateway::GatewayResponse"
      Properties:
        ResponseParameters:
          gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
          gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
        ResponseType: EXPIRED_TOKEN
        RestApiId:
          Ref: "ApiGatewayRestApi"
        StatusCode: "401"
    AuthFailureGatewayResponse:
      Type: "AWS::ApiGateway::GatewayResponse"
      Properties:
        ResponseParameters:
          gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
          gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
        ResponseType: UNAUTHORIZED
        RestApiId:
          Ref: "ApiGatewayRestApi"
        StatusCode: "401"

  Outputs:
    ApiGatewayRestApiId:
      Value:
        Ref: ApiGatewayRestApi
      Export:
        Name: MyApiGateway-restApiId
    ApiGatewayRestApiRootResourceId:
      Value:
        Fn::GetAtt:
          - ApiGatewayRestApi
          - RootResourceId
      Export:
        Name: MyApiGateway-rootResourceId

When I perform sls deploy or via Codebuild I get the following error

Unsupported apiGateway.restApiId object
    at ServerlessCustomDomain.<anonymous> (/codebuild/output/src937094620/src/bitbucket.org/docspacecrew/ease-leads-backend/node_modules/serverless-domain-manager/dist/src/index.js:294:27)
    at Generator.throw (<anonymous>)
    at rejected (/codebuild/output/src937094620/src/bitbucket.org/docspacecrew/ease-leads-backend/node_modules/serverless-domain-manager/dist/src/index.js:6:65)

I don't know what I am doing wrong.

DianaIonita commented 1 year ago

Hi @neeraj87,

Thanks for raising this issue. The plugin seems to have problems with the definition of the restApiId here:

provider:
  apiGateway:
    restApiId:
      Ref: ApiGatewayRestApi

The restApiId is not required to be defined for this plugin, unless the REST API is created in another project. If the project that this yaml is from creates it, then it should be picked up automatically. Could you please try removing the restApiId definition?

motymichaely commented 10 months ago

Hey, I am facing the same issue when using Import from another stack of the rest API ID:

provider:
  apiGateway:
    restApiId:
      'Fn::ImportValue': ${self:custom.serviceSlug}-ApiGatewayRestApi

The error I am getting is: Expected params.restApiId to be a string.

Since this is the only way to have multiple serverless configuration files to be deployed under the same rest API, I see no way to use this plugin without fixing this.

Thanks!