How can I make apigateway forward root path to integrated http endpoint? - amazon-web-services

I created a REST api gateway in AWS and configure it to pass through all requests to a http endpoint. The configuration I have is
After deploy to a stage (dev) it gives me an invoke URL, like https://xxxx.execute-api.ap-southeast-2.amazonaws.com/dev,
it works fine if I invoke the url by appending a sub path like: https://xxxx.execute-api.ap-southeast-2.amazonaws.com/dev/xxxxx`, I can see it forward the request to downstream http endpoint. However it doesn't forward any request if I invoke the base url: https://xxxx.execute-api.ap-southeast-2.amazonaws.com/dev. How can I make it work with the base invoke url without any subpath?
I tired to add an additional / path resource in API gateway but it doesn't allow me to add it.

The application must be able to receive requests at any path, including the root path: /. An API Gateway resource with a path of /{proxy+} captures every path except the root path. Making a request for the root path results in a 403 response from API Gateway with the message Missing Authentication Token.
To fix this omission, add an additional resource to the API with the path set to / and link that new resource to the same http endpoint as used in the existing /{proxy+} resource.
The updated OpenAPI document now looks like the following code example:
{
"openapi": "3.0",
"info": {
"title": "All-capturing example",
"version": "1.0"
},
"paths": {
"/": {
"x-amazon-apigateway-any-method": {
"responses": {},
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": ""
}
}
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
"responses": {},
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": ""
}
}
}
}
}

Related

How to redirect to a new domain using API Gateway with URL params?

I've been using API Gateway to create free HTTPS redirects from one domain to another. But today, I need to include all the URL parameters in the redirect. For example:
example.com/articles/fun-article?source=google&something=else
to
www.example.com/articles/fun-article?source=google&something=else
I was going over the CloudFormation API Gateway docs and couldn't find anything that could allow me to do so. And I was wondering if I'm missing something. So, for example here is the part of the CFN that does the redirect:
"x-amazon-apigateway-integration": {
"type": "mock",
"responses": {
"301": {
"statusCode": "301",
"responseParameters": {
"method.response.header.Location": "'https://tmmwith.us/'"
}
}
},
"requestTemplates": {
"application/json": "{\"statusCode\": 301}"
},
"passthroughBehavior": "when_no_match"
}
In the AWS::ApiGateway::RestApi resource, any ideas if there is a way to mention in the method.response.header.Location to include all the parameters of the URL?

Appsync as Proxy to another Graphql server

I have a existing graphql server provied by 3rd party. I also have my own backend running on EC2 to provide APIs.
I'm trying to build the appsync with aws-cdk for connecting to both 3rd party graphql and my backend instance also.
With the graphql server, appsync will act as proxy to forward queries only. My questions are:
Do we have anyway to load remote schema and populate it in appsync along with its schema?
How can we forward the requests to another graphql server using aws-cdk? I'm trying something like this:
private get _requestMappingTemplate(): string {
return `
{
"version": "2018-05-29",
"method": "GET",
"resourcePath": $util.toJson("/graphql"),
"params": {
"headers": {
"Authorization": "Bearer $ctx.request.headers.Authorization"
},
"body": {
"query": "$util.escapeJavaScript($ctx.info.getSelectionSetGraphQL())"
}
}
}`;
}
But from the aws doc, getSelectionSetGraphQL returns string representation of the selection set, formatted as GraphQL schema definition language (SDL). Although fragments aren't merged into the selection set
Is that possible to setup AppSync for forwarding request to another GraphQL servers? Any best practice to follow?
It's quite a bit more complicated. I'm still working on it, and the best I got so far is given below. It still drops query arguments, so has limited use.
#* TODO: Add some more interesting info to the operation name, e.g. a timestamp *#
#set($operationName = $context.info.parentTypeName)
#set($payloadBody = {
"query": "$util.str.toLower($context.info.parentTypeName) $operationName { $context.info.fieldName $context.info.selectionSetGraphQL }",
"operationName": $operationName,
"variables": $context.info.variables
})
{
"version" : "2018-05-29",
"operation": "Invoke",
"payload":{
"path": "/graphql",
"httpMethod": "POST",
"headers": $util.toJson($ctx.request.headers),
"requestContext": {
"authorizer": {
"claims": $context.identity.claims
}
},
"body": "$util.escapeJavaScript($util.toJson($payloadBody))",
"isBase64Encoded": false
},
}

Can the "queryStringParameter" in the eventbody of a GET request be moved to the "body" of the event for an AWS API Gateway HTTP API?

I'm trying to get my "queryStringParameters" in the event into the "body" key of the event so I can parse them in the same way that I parse post requests. Is this possible with a HTTP api? I tried using parameter mappings in the integration settings but this only allows me to append the body to the queryString.
The way that you get the values from a POST request using body and with query parameters are different. So both ca not be in the body property as you want.
The queryStringParameters is passed when you add a URL Query String Parameter in your Api gateway resource.
When you send a request to the event in your API the following event will be send to the Lamba function
{
"resource": "/{proxy+}",
"path": "/path/to/resource",
"httpMethod": "POST",
"isBase64Encoded": true,
"queryStringParameters": {
"foo": "bar"
},
"multiValueQueryStringParameters": {
"foo": [
"bar"
]
},
"pathParameters": {
"proxy": "/path/to/resource"
},
"stageVariables": {
"baz": "qux"
},
"body":"{ \"time\": \"evening\" }",
"headers": {
...
},
...
}
You be able to get the query parameters of event in your Lambda using the properties:
event.queryStringParameters && event.queryStringParameters.foo
And the body we need to get with the body property:
if (event.body) {
let body = JSON.parse(event.body)
if (body.time)
time = body.time;
}
In this way in your Lambda you need to parse them in different ways.
For more information of how to work with API Gateway and Lambda take a look here: Tutorial: Build a Hello World REST API with Lambda proxy integration

APIGateway does not perform request validation when called using POSTMan

Just learning my way through AWS - I have an APIGateway REST API setup with Lambda proxy integration. The API has a model defined, and request validation setup on the body using this model.
Say the model is
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"propertyA": {
"type": "string"
},
"propertyB": {
"type": "string"
},
"propertyC": {
"type": "string"
},
"propertyD": {
"type": "string"
}
},
"required": ["propertyA", "propertyB", "propertyC", "propertyD"]
}
Now, if I test the API via APIGateway console, and purposely give an invalid input (omitting a required property propertyD):
{
"propertyA": "valueA",
"propertyB": "valueB",
"propertyC": "valueC"
}
the request fails with the error(400): Sun Jul 11 13:07:07 UTC 2021 : Request body does not match model schema for content type application/json: [object has missing required properties (["propertyD"])]
But when I invoke the same API(and stage) with the same invalid input from Postman, the validation seems to be not happening, and request is proxied to Lambda, which even returns a 200 OK as long as I comment out the parts of code that depend on propertyD.
What's the difference here? Should I be passing in any request header from the client side? I couldn't find anything from the AWS documentations
Answering my question-
Issue was with the headers used in the request - Postman defaulted the JSON as a Content-Type of text/plain, I had to switch to JSON using the dropdown in Body tab to make PostMan set the Content-Type to application/json
Following this post seems to have fixed the problem: https://itnext.io/how-to-validate-http-requests-before-they-reach-lambda-2fff68bfe93b, although it doesn't explain how
Apparently the magic lies with the config adding Content-Type Header under HTTP Request Headers section, even though the header is set correctly as application/json in PostMan.

AWS - HTTP API Gateway - How do I block favicon requests?

I'm using a HTTP API Gateway to trigger a lambda invocation. When I use the url from postman, no issues. When I use it from my browser, it always makes a 2nd request, for the favicon.
Is there anyway in the gateway itself to block the favicon request from getting to the lambda?
I'm using the following terraform:
resource "aws_apigatewayv2_api" "retry_api" {
name = "${var.environment}_${var.cdp_domain}_retry_api"
protocol_type = "HTTP"
description = "To pass commands into the retry lambda."
target = module.retry-support.etl_lambda_arn
}
resource "aws_lambda_permission" "allow_retry_api" {
statement_id = "AllowAPIgatewayInvokation"
action = "lambda:InvokeFunction"
function_name = module.retry-support.etl_lambda_arn
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.retry_api.execution_arn}/*/*"
}
This won't block the favicon request made from the browser, rather won't invoke the Lambda for those requests.
Assuming the API endpoint is /hello and the http method is GET, you can restrict api-gateway to invoke the lambda for only this URL. The format would be like this.
arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/hello
So the source_arn in aws_lambda_permission would change to something like this
source_arn = "${aws_apigatewayv2_api.retry_api.execution_arn}/*/*/GET/hello"
The answer assumes the existing / in the end is for apiId and stage respectively. Otherwise check the value for ${aws_apigatewayv2_api.retry_api.execution_arn} and make modifications accordingly.
This answer can also help. You can provide the openapi specification in the body for your supported path only. For the above case the relevant path section of the openapi specification invoking a Lambda named HelloWorldFunction would look like
"paths": {
"/hello": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations"
},
"payloadFormatVersion": "2.0"
},
"responses": {} //Provide the expected response model
}
}
}
Here is a link to OpenApi Specification.
Normally, I would do this by putting cloudfront in front of the API gateway, and map the favicon.ico to an S3 bucket.
If you really want to handle it at the API GW level, you can create a /favicon.ico route, and set the integration to MOCK - this will return a specific value, and not invoke lambda (or any other back end).