API Gateway Canned Response From GET Request - amazon-web-services

I'm looking for a simple way to provide a message in a GET response of an API Gateway. I have a template that has several lambda integrations on the backend that are configured for various purposes - including an authorizer that validates requests upon requesting. I am looking to implement an additional GET method who's only responsibility is to return a JSON formatted response. Rather than have another lambda do this simple response, I would like to generate the response in the CFT. I just can't figure out what exactly is needed. I tried out the following AWS::ApiGateway::Method with an AWS::ApiGateway::Model but I don't think I am doing it correctly. I defer to the wizards of stackOverflow!
GetMethodEndpoint:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
OperationName: 'mock'
ResourceId: !Ref APIResource
RestApiId: !Ref apiGateway
HttpMethod: "GET"
MethodResponses:
- ResponseModels:
application/json: !Ref ApiGatewayModel
StatusCode: 200
Integration:
PassthroughBehavior: WHEN_NO_TEMPLATES
TimeoutInMillis: 29000
ConnectionType: 'INTERNET'
Type: "MOCK"
IntegrationResponses:
- StatusCode: 200
SelectionPattern: '2\d{2}'
ResponseTemplates:
application/json: "{\"message\":{\"support_info\": \"This is a canned response that I want returned when the GET request is made. It would be ideal for this to be nested json but...\}"
ApiGatewayModel:
Type: AWS::ApiGateway::Model
Properties:
ContentType: 'application/json'
RestApiId: !Ref apiGateway
Schema: {}

Defining API Gateway with Cloudformation is a nightmare.
Based on experience, it's easier to prepare API Gateway resources, methods, integrations, responses, transformations with GUI and then export API definition to OpenAPI format.
You can than provide OpenAPI file content as Body property of AWS::ApiGateway::RestApi resource, or as S3 object location with BodyS3Location property of AWS::ApiGateway::RestApi
I think Swager is also supported.

Related

How can I add CORS on apigateway http_proxy without setting a lambda?

I have a API gateway and there are a few paths defined in the gateway. I'd like to enable CORS to one of the path which works as http_proxy pointing to an appsync in the backend. The appsync resolvers point to a dynamodb table. so frontend calls API gateway who pass the requests to appsync and the request is resolved by dynamodb.
My problem is to enable CORS on that particular path. But based on this doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html it says I have to response the CORS header in backend which is a lambda. But I don't have a lambda in the backend. All the backends are just appsync + some resolvers (e.g. Dynamodb). How can I make it support CORS?
When I call appsync endpoint directly, it response access-control-allow-origin: * and it works fine for frontend (a web site). However, if the frontend calls API gateway domain, it will get rejected because of cors. I believe apigateway doesn't allow CORS so how can I enable cors in http_proxy integration without a backend lambda?
Let's say you have the following CloudFormation declaration for your GraphQL based HTTP_PROXY integration for POST method
GraphQLPostMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
Rest of the properties for HTTP_PROXY integration
Define one more method for OPTIONS request. If the source i.e. AppSync responds to Options request with appropriate headers, you should be fine with a HTTP_PROXY integration. If it doesn't or you don't want to send the Options request to AppSync, in that case you can use the MOCK integration for the Options method.
GraphQLOptionsMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: OPTIONS
ResourceId: !Ref <ReplaceWithYourResrouce>
RestApiId: !Ref <ReplaceWithYourAPI>
MethodResponses:
- ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
StatusCode: '200'
Integration:
Type: MOCK
IntegrationResponses:
- ResponseTemplates:
application/json: ''
StatusCode: '200'
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Methods: "'POST'"
method.response.header.Access-Control-Allow-Origin: !Sub "'${ReplaceWithYourCORSAllowedOrigin}' or hardcode to *"
PassthroughBehavior: NEVER
RequestTemplates:
application/json: '{"statusCode": 200}'

What is the difference between AWS and AWS_PROXY in CloudFormation::APIGateway?

What is the difference between AWS and AWS_PROXY in a CloudFormation template with a AWS::ApiGateway::Method - Integration:Type, with a Lambda backend? I was constantly getting 502 errors just now and realized that I needed to respond with a very specifically formatted JSON response. When I created API Gateways from the console I never bumped into this issue. It does work now but I want to know the underlying differences so I can learn.
Here's the part from the CF Template:
VisitorCounterAPIGatewayRootMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY #THIS is my question. AWS or AWS_PROXY?
Uri: !Sub
- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
- lambdaArn: !GetAtt VisitorCountLambda.Arn
ResourceId: !GetAtt VisitorCounterAPIGateway.RootResourceId
RestApiId: !Ref VisitorCounterAPIGateway
And here's the response code from my Lambda function (Python3.7):
apiResponse = {
"isBase64Encoded": False,
"statusCode": 200,
"body": json.dumps({
"visitorCount": int(float(response["Attributes"]["amount"]))
})
}
Thank you.
AWS and AWS_PROXY are two types of integration in API Gateway.
AWS_PROXY is only used with lambda and it is the simplest way of working with lambda with API Gateway. It means:
For HTTP proxy integration, API Gateway passes the entire request and response between the frontend and an HTTP backend. For Lambda proxy integration, API Gateway sends the entire request as input to a backend Lambda function. API Gateway then transforms the Lambda function output to a frontend HTTP response.
AWS can be used with any AWS service, including lambda. It is more complex to setup and usually it is used when you want to integrate API gateway with, e.g., SQS or Kinesis or other AWS services:
This type of integration lets an API expose AWS service actions. In AWS integration, you must configure both the integration request and integration response and set up necessary data mappings from the method request to the integration request, and from the integration response to the method response.
So to answer your question:
Type: AWS_PROXY #THIS is my question. AWS or AWS_PROXY?
If you can, use AWS_PROXY. This is the most streamlined and easiest to work and setup form of integration with lambda. It only works with lambda anyway. Since entire response and reply are just pass-through the API gateway, you must ensure that your lambda returns correct headers and status code to the caller.
Use AWS with other services, such as SQS. You can still use lambda, but it requires more setup. But the benefit is that you have more control over what's get passed into your lambda, and how the response is passed to the caller. Usually you would use AWS with lambda if you have existing lambda and you can't change it. This way in AWS integration you can shape and mold the inputs and outputs from the caller to lambda and back, without changing the lambda.
Short example on AWS and AWS_PROXY integrations in CloudFormation, showing that when you use AWS integration, you can define extra parameters (myParam) that can be passed to your lambda:
AWS_PROXY
MyApiResourceMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: None
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaArn1}/invocations
MethodResponses:
- ResponseModels: {"application/json": "Empty"}
StatusCode: 200
ResourceId: !Ref MyResource
RestApiId: !Ref MyRestApi
AWS
MyApiResourceMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: None
#AuthorizerId: String
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
- ResponseTemplates:
application/json: ""
StatusCode: 200
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
application/json: |
{
"myParam": "$input.params('myParam')"
}
Type: AWS
Uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaArn2}/invocations
MethodResponses:
- ResponseModels: {"application/json": "Empty"}
StatusCode: 200
ResourceId: !Ref MyResource
RestApiId: !Ref MyRestApi

Creating an API Gateway AWS Integration with S3

I would like to set up API Gateway to retrieve files from a specific bucket on S3.
The GET method for the resource in the Cloudformation template looks like this:
GetMethod:
Type: "AWS::ApiGateway::Method"
Properties:
HttpMethod: GET
ApiKeyRequired: true
AuthorizationType: NONE
RequestParameters:
'method.request.path.listId': true
RestApiId:
Ref: ApiGatewayRestApi
ResourceId:
Ref: Lists
Integration:
Type: aws
Credentials:
Ref: S3ReadAccessRole
IntegrationHttpMethod: GET
PassthroughBehavior: WHEN_NO_MATCH
RequestParameters:
'integration.request.path.key': 'method.request.path.listId'
I am having trouble specifying the S3 URI to integrate with. I've done the following with no success:
The S3 URI: arn:aws:s3:::lists/groceries/{key}
The API Gateway based uri based on this article. arn:aws:apigateway:us-west-2:s3:lists/groceries/{key}
Please help me figure out the correct URI to allow objects on S3 to be retrieved using API Gateway directly.

not a valid key=value pair (missing equal-sign) in Authorization header

While hitting an API from Postman I am getting this error.
API details:
URL:
https://account-perf.myglobal.com/v1/users/00uk0khprrME7gZOU0h7/credentials/change_password
Header:
Content-Type:application/json Authorization:Bearer
n7mbkw74jsubd7rauhptdnre
Type:
POST
Body:
{"password":"Baddy125#","token":"eyJhbGci...."}
Edit 1:
Web-service call to generate token-
URL-
https://api-perf.myglobal.com/rest/oauth2/v1/token
Type-
POST
Body-
client_id:abcd client_secret:xyz grant_type:client_credentials
I had this whenever any unhandled endpoint method or resource was called. My setup is an API Gateway with defined resources (e.g. /myendpoint) and defined methods for those endpoints (e.g. GET).
To fix it, I created a Node.js Lambda function that just returned a 404. Then I added any ANY method at the root of the endpoints / and pointed it as a Lambda proxy function to the ANY methods.
Then I added a proxy resource, e.g. /{proxy} -- there's a checkbox you can click when creating a resource to tell it to proxy. An ANY method on that resource pointing to the same Lambda function, deploy the API, and I'm done.
Now instead of the auth bearer token error, I get a proper HTTP 404 error.
#Matt H - That's pretty good idea this gave me an inspiration of another one.
Assuming all of the other paths within the API are explicitly specified, I created a default path /{proxy+} which would return a http 404, message resource not found. Instead of using lambda, I was able to create a mock response, so there isn't even any cost incurred for getting Lambda to return the response.
I created my APIs via Open API spec. This is how my YAML for the implementation would like
/{proxy+}:
x-amazon-apigateway-any-method:
responses:
404:
description: "404 response"
content: {}
x-amazon-apigateway-integration:
responses:
404:
statusCode: "404"
responseTemplates:
application/json: "{\"message\":\"resource not available\"}"
requestTemplates:
application/json: "{\"statusCode\": 404}"
passthroughBehavior: "when_no_templates"
type: "mock"
Serverless also has the ability to specify inline mock response. Below can be a sample:
functions:
default:
handler: handler.default
events:
- http:
path: hello
cors: true
method: get
integration: mock
request:
template:
application/json: '{"statusCode": 404}'
response:
template: $input.path('$')
statusCodes:
404:
pattern: '' #default method
template:
application/json: '{"statusCode": 404, "message":"resource not found"}'
serverless doc: https://www.serverless.com/framework/docs/providers/aws/events/apigateway/#custom-response-templates
Analyze and validate the request path, in case the request is incorrect this error is thrown at API Gateway. I stepped on the same error, when corrected the request parameters it worked well.
Let me know.
I managed to do it by proxying the ANY /{proxy+} route to a lambda which will always respond with an HTTP 404
Since all other routes are precisely configured, this ANY /{proxy+} route acts as the default one and will catch any unmatched request
Here is how I did it with CloudFormation :
Parameters:
RestAPI:
Type: String
RestApiRootResourceId:
Type: String
LambdaName:
Type: String
Path:
Type: String
RootResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestAPI
ParentId: !Ref RestApiRootResourceId
PathPart: !Ref Path
ProxyResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref RestAPI
ParentId: !Ref RestApiRootResourceId
PathPart: "{proxy+}"
AnyMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId: !Ref RestAPI
ResourceId: !Ref ProxyResource
HttpMethod: ANY
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
PassthroughBehavior: WHEN_NO_MATCH
Uri:
Fn::Join:
- ":"
- - !Sub "arn:aws:apigateway:${AWS::Region}:lambda"
- !Sub "path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function"
- !Sub "${LambdaName}/invocations"
ApiGatewayInvokeLambdaPermissionAny:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName:
Fn::Join:
- ":"
- - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function"
- !Ref LambdaName
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Join:
- ":"
- - !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}"
- !Sub "${RestAPI}/*/ANY/*"
Paramters :
RestAPI is your API ID
RestApiRootResourceId is the ID of the root resource (!GetAtt "RestApi.RootResourceId")
LambdaName is the name of your proxy lambda
Path is something I have bewteen the stage and the root (for mty specific usage)

AWS Api Gateway with Lambda integration only works via console

I have a CloudFormation template which sets up an API Gateway with a Lambda integration. When testing the integration via the AWS console it works.
When I attempt to use the HTTP method via Postman or Chrome, the response is null.
The full template can be seen here:
https://github.com/mattcanty/musicdown/blob/07b899d5924ab2fcc675cc521706b0c217e4e07e/cloudformation.yaml
I think something is not quite right here:
MusicdownsGet:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref ApiGateway
ResourceId: !Ref MusicdownResource
HttpMethod: GET
RequestParameters:
method.request.querystring.filter: true
AuthorizationType: NONE
RequestModels:
application/json: !Ref MusicdownModel
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Lambda.Arn}/invocations
IntegrationResponses:
- StatusCode: 200
RequestTemplates:
application/json: "{ \"filter\": \"$input.params('filter')\" }"
MethodResponses:
- StatusCode: 200