service: apigw-rest-api-dynamodb-sls frameworkVersion: '^3' # require serverless v3 or later params: default: AppName: api-music provider: name: aws # override the default stage (dev) stage: ${opt:stage, "v1"} # use --region option value or the default - us-east-1 region: ${opt:region, "us-east-1"} resources: # Override the default description Description: An Amazon API Gateway REST API that integrates with an Amazon DynamoDB table (Serverless Framework). Resources: DynamoDBTable: Type: 'AWS::DynamoDB::Table' Properties: TableName: Music # A list of attributes that describe the key schema for the DynamoDB table and indexes. AttributeDefinitions: - AttributeName: id AttributeType: S - AttributeName: artist AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 GlobalSecondaryIndexes: - IndexName: Artist-Index KeySchema: - AttributeName: artist KeyType: HASH Projection: ProjectionType: INCLUDE NonKeyAttributes: - album ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 APIGatewayRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Action: - 'sts:AssumeRole' Effect: Allow Principal: Service: - apigateway.amazonaws.com Policies: - PolicyName: APIGatewayDynamoDBPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - 'dynamodb:PutItem' - 'dynamodb:Query' # Including * in the resource Arn allows access to the DynamoDB table and indexes Resource: !Sub - '${varTableArn}*' - varTableArn: !GetAtt DynamoDBTable.Arn Api: Type: 'AWS::ApiGateway::RestApi' Properties: Name: ${param:AppName} ApiKeySourceType: HEADER MusicResource: Type: 'AWS::ApiGateway::Resource' Properties: RestApiId: !Ref Api ParentId: !GetAtt Api.RootResourceId PathPart: 'music' MusicMethodPost: Type: 'AWS::ApiGateway::Method' Properties: RestApiId: !Ref Api ResourceId: !Ref MusicResource HttpMethod: POST ApiKeyRequired: true AuthorizationType: NONE Integration: Type: AWS Credentials: !GetAtt APIGatewayRole.Arn # Should always be POST when integrating with AWS services IntegrationHttpMethod: POST # More info: https://docs.aws.amazon.com/apigateway/api-reference/resource/integration/ Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:dynamodb:action/PutItem' PassthroughBehavior: WHEN_NO_TEMPLATES RequestTemplates: # Build the structure of the request that is sent when calling DynamoDB PutItem # Using single-line, stringified JSON as the mapping template # Example body when making API request: {"artist": "The Beatles", "album": "Abbey Road"} # Use the unique id of the API context variable (eg: $context.requestId) as the DynamoDB item id application/json: "{\"TableName\":\"Music\",\"Item\":{\"id\":{\"S\":\"$context.requestId\"},\"artist\":{\"S\":\"$input.path('$.artist')\"},\"album\":{\"S\":\"$input.path('$.album')\"}}}" IntegrationResponses: - StatusCode: '200' ResponseTemplates: application/json: "{}" MethodResponses: - StatusCode: '200' MusicArtistResource: Type: 'AWS::ApiGateway::Resource' Properties: RestApiId: !Ref Api ParentId: !Ref MusicResource PathPart: '{artist}' MusicArtistMethodGet: Type: 'AWS::ApiGateway::Method' Properties: RestApiId: !Ref Api ResourceId: !Ref MusicArtistResource HttpMethod: GET ApiKeyRequired: true AuthorizationType: NONE RequestParameters: # Determines whether the path parameter (eg: artist) is required method.request.path.artist: true Integration: Type: AWS Credentials: !GetAtt APIGatewayRole.Arn # Should always be POST when integrating with AWS services IntegrationHttpMethod: POST # More info: https://docs.aws.amazon.com/apigateway/api-reference/resource/integration/ Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:dynamodb:action/Query' PassthroughBehavior: WHEN_NO_TEMPLATES RequestParameters: integration.request.path.artist: method.request.path.artist RequestTemplates: # Build the structure of the request that is sent when calling DynamoDB Query # Using single-line, stringified JSON as the mapping template # Use $util.urlDecode($input.params('pathname')) to decode if the url path value contains spaces # A + or %20 may be used as a space in the url application/json: "{\"TableName\":\"Music\",\"IndexName\":\"Artist-Index\",\"KeyConditionExpression\":\"artist=:v1\",\"ExpressionAttributeValues\":{\":v1\":{\"S\":\"$util.urlDecode($input.params('artist'))\"}}}" IntegrationResponses: - StatusCode: '200' ResponseTemplates: # Modify the response of the DynamoDB Query before sending back to the caller # Using single-line Velocity Template Language (VTL) code as the mapping template # \n represents a new line, \t represents a tab character, \" represents a single quote character # Example response: {"music":[{"id":"38bfb57e-a5a8-4fed-9a4f-391d66d5e987","artist":"The Beatles","album":"Abbey Road"}]} application/json: "#set($inputRoot = $input.path('$'))\n{\n\t\"music\": [\n\t\t#foreach($field in $inputRoot.Items) {\n\t\t\t\"id\": \"$field.id.S\",\n\t\t\t\"artist\": \"$field.artist.S\",\n\t\t\t\"album\": \"$field.album.S\"\n\t\t}#if($foreach.hasNext),#end\n\t\t#end\n\t]\n}" MethodResponses: - StatusCode: '200' ApiDeployment: Type: 'AWS::ApiGateway::Deployment' DependsOn: - MusicArtistMethodGet Properties: RestApiId: !Ref Api StageName: ${self:provider.stage} ApiKey: Type: 'AWS::ApiGateway::ApiKey' DependsOn: - ApiDeployment Properties: Enabled: true Name: ${param:AppName}-apikey StageKeys: - RestApiId: !Ref Api StageName: ${self:provider.stage} ApiUsagePlan: Type: 'AWS::ApiGateway::UsagePlan' DependsOn: - ApiDeployment Properties: ApiStages: - ApiId: !Ref Api Stage: ${self:provider.stage} Throttle: RateLimit: 500 BurstLimit: 1000 UsagePlanName: ${param:AppName}-usage-plan Quota: Limit: 10000 Period: MONTH ApiUsagePlanKey: Type: 'AWS::ApiGateway::UsagePlanKey' Properties: KeyType: API_KEY KeyId: !Ref ApiKey UsagePlanId: !Ref ApiUsagePlan Outputs: ApiRootUrl: Description: Root Url of the API Value: !Sub - 'https://${ApiId}.execute-api.${AWS::Region}.amazonaws.com/${self:provider.stage}' - ApiId: !Ref Api