I'm configuring the caching on AWS API Gateway side to improve performance of my REST API. The endpoint I'm trying to configure is using a query parameter. I already enabled caching on AWS API Gateway side but unfortunately had to find out that it's ignoring the query parameters when building the cache key.
For instance, when I make first GET call with query parameter "test1"
GET https://2kdslm234ds9.execute-api.us-east-1.amazonaws.com/api/test?search=test1
Response for this call is saved in cache, and when after that I make call another query parameter - "test2"
GET https://2kdslm234ds9.execute-api.us-east-1.amazonaws.com/api/test?search=test2
I get again response for first call.
Settings for caching are pretty simple and I didn't find something related to parameters configuration.
How can I configure Gateway caching to take into account query parameters?
You need to configure this option in the Gateway API panel.
Choose your API and click Resources.
Choose the method and see the
URL Query String session.
If there is no query string, add one.
Mark the "caching" option of the query string.
Perform the final tests and finally, deploy changes.
Screenshot
The following is how we can achieve this utilising SAM:
The end result in the AWS API Gateway console must display that the set caching checkbox is:
The *.yml template for the API Gateway would be:
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
CacheClusterEnabled: true
CacheClusterSize: '0.5'
MethodSettings:
- HttpMethod: GET
CacheTtlInSeconds: 120
ResourcePath: "/getData"
CachingEnabled: true
DefinitionBody:
swagger: 2.0
basePath: /Prod
info:
title: OutService
x-amazon-apigateway-policy:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: "*"
Action: execute-api:Invoke
Resource:
- execute-api:/*/*/*
paths:
"/getData":
get:
# ** Parameter(s) can be set here **
parameters:
- name: "path"
in: "query"
required: "false"
type: "string"
x-amazon-apigateway-integration:
# ** Key is cached **
cacheKeyParameters:
- method.request.querystring.path
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OutLambda.Arn}/invocations
responses: {}
EndpointConfiguration: PRIVATE
Cors:
AllowHeaders: "'*'"
Related
I have a yaml file for the API Gateway integration like this:
/users/{userId}:
get:
description: Get user details
parameters:
- description: Valid user id
in: path
name: userId
required: true
schema:
type: string
responses:
'200':
content: {}
description: Success
headers:
Access-Control-Allow-Origin:
schema:
type: string
'400':
content: {}
description: Invalid user id
security:
- NodeJsAuth: []
summary: Get User Id Details
tags:
- User
x-amazon-apigateway-integration:
connectionId: ${stageVariables.vpcLinkId}
connectionType: VPC_LINK
httpMethod: GET
passthroughBehavior: when_no_match
requestParameters:
integration.request.path.userId: method.request.path.userId
responses:
default:
responseParameters:
method.response.header.Access-Control-Allow-Origin: '''*'''
statusCode: '200'
type: http_proxy
uri: '#{EksElbUri}#/user/{userId}'
I want to enable cache for this method. I know I can enable it manually but I want to do it in YAML file so it could be easier to execute.
I have my stage enabled the cache already (via AWS Console UI). However I could not find anywhere that is mentioning how to enable cache for this method ONLY.
I am aware that once enabled the cache in stage, it will enabled for all GET endpoints and I have many GET endpoints but I only want this one to be cached.
Is there a way that I can specify the cache in OPEN-API so I no need to disable all the other GET endpoint one by one manually?
Thank you.
What will you'll find easier is edit the API in the API GW Console, enabling cache. Deploy the API to your stage. Then go to Stages -> your stage, and export the API in whatever format you are currently editing in. This is what I do saves you having indentation problems, and wasting time trying to find the exact equivalent syntax.
Once you have done that once your file is easier to edit and you can typically figure out the edits you want to make.
I am trying to remove the redundant path which is used in both my serverless configuration and aws api gateway mapping.
Problem:
Login serverless yaml
serverless.yml
frameworkversion: '>1.8'
service: ${stage}-login
provider:
name: aws
runtime: nodejs10.x
timeout: 12
functions:
login:
name: login
handler: login.handler
events:
- http:
path: login
cors: true
integration: lambda
request:
passThrough: WHEN_NO_MATCH
template:
application/json:
<response omitted>
plugins:
- serverless-offline
API mapping to my custom domain
API - login-dev
Stage - dev
Path(optional) - login
Goal:
Lambda Functions :
login - {base url}/dev/login
register - {base url}/dev/register
What happened:
login {base url}/dev/login/login
register - {base url}/dev/register/register
Actions taken:
Tried to remove the Path(optional) but it would not allow me to add another lambda function if path is omitted.
Tried to proxy(unsure if this works the way i understand it) but it doesn;t allow because an error shows that {login} is used in one of my lambda function parameters.
Removed path in serverless yaml configuration file and replaced it with blank or / - but not an option for me because i need to keep the existing configuration.
Any help is very much appreciated.
Have you tried this:
functions:
login:
name: login
handler: login.handler
events:
- http:
path: /login
................
By adding a "/" in the starting of path.
I don't understand the following behavior of my API Gateway and Cognito User Pool Authorizer. Using the AWS SAM template, I have deployed the following Cognito User Pool:
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref CognitoUserPool
ClientName: !Sub CognitoUserPoolClient-${StageName}
GenerateSecret: false
SupportedIdentityProviders:
- COGNITO
ExplicitAuthFlows:
- ALLOW_USER_PASSWORD_AUTH
- ALLOW_CUSTOM_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
- ALLOW_USER_SRP_AUTH
OptalXCognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
AutoVerifiedAttributes:
- email
EmailVerificationSubject: Verify your email.
EmailVerificationMessage: Please click the link below to verify your email address. {####}
EmailConfiguration:
EmailSendingAccount: DEVELOPER
From: Widgets Co <admin#widgets.com>
ReplyToEmailAddress: admin#widgets.com
SourceArn: !FindInMap [ StageMap, !Ref StageName, EmailSESArn ]
UserPoolName: !Sub CognitoUserPool-${StageName}
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_LINK
AdminCreateUserConfig:
AllowAdminCreateUserOnly: true
UnusedAccountValidityDays: 90
InviteMessageTemplate:
EmailMessage: ...
EmailSubject: Welcome to Widgets Co!
SMSMessage: Your username is {username} and temporary password is {####}.
Policies:
PasswordPolicy:
MinimumLength: 8
UsernameAttributes:
- email
Schema:
- AttributeDataType: String
Name: user_id
Mutable: false
DeveloperOnlyAttribute: false
- Name: email
Mutable: false
Required: true
Then, I've defined an API Gateway that uses the Cognito User Pool as its Authorizer:
WidgetsAPI:
Type: 'AWS::Serverless::Api'
Name: WidgetsAPI
Properties:
GatewayResponses:
EXPIRED_TOKEN:
ResponseParameters:
Headers:
Fail-Reason: "'Expired token'"
Access-Control-Allow-Origin: "'*'"
DEFAULT_4xx:
ResponseParameters:
Headers:
Access-Control-Expose-Headers: "'WWW-Authenticate'"
Access-Control-Allow-Origin: "'*'"
DEFAULT_5XX:
ResponseParameters:
Headers:
Fail-Reason: "'Internal server error. Check logs.'"
Access-Control-Allow-Origin: "'*'"
Auth:
DefaultAuthorizer: CognitoAuthorizer
Authorizers:
CognitoAuthorizer:
UserPoolArn: !FindInMap [ StageMap, !Ref StageName, UserPoolArn ]
AddDefaultAuthorizerToCorsPreflight: false
Here my UserPoolArn is inside a Mappings that differs based on the environment we are in (stage or prod). I've defined a bunch of endpoints, but for the sake of demonstration, there's on called /users.
Now when I pass in a GET /prod/users request, with a header of Authorization: THE_ID_TOKEN_FROM_MY_COGNITO_SIGN_IN, I receive a 401 Unauthorized. I can 100% confirm that I am using the right ID token. For instance, when I pass in the same ID token to test via the management console's authorizer screen, I get a 200 response:
Moreover, I've deployed in our company's stack multiple API endpoints, all with the same pattern - we've been using id_token for the past year quite successfully. The only difference is we are trying to move our managed user pools into CloudFormation templates, so the most recent User Pool (including this problematic one) were created via aws sam build.
Why is this happening? I know I may not have provided enough details, but happy to add any other pieces of code or configs that are relevant. It's hard for me to reproduce this error since in all my other environments (stage, my other microservices API endpoints), the API Gateway and Cognito User Pool work perfectly after being deployed via AWS SAM.
In fact, I've deployed the exact same version of this infrastructure in our staging environment, and it works perfectly (the only difference between our stage and prod environments is the Stage parameter that we pass into our template YAML stack).
I've found that if I go into the management console and manually deploy the API again, everything works great (I get a 200 response with the expected response output):
Note - there's several posts (here, here, and here) about this topic, but none of them describe my exact problem, which is why my API Gateway returns 401 Unauthorized errors upon first applying my Cloudformation stack, and only works properly after I manually deploy from the console the entire API Gateway stage.
Add a AWS::ApiGateway::Deployment resource to your template.
ApiGatewayDeployment:
DependsOn:
- Function1Name
- Thing2Name
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref WidgetsAPI
StageName: !Sub ApiGateway-${ApiGatewayStageName}
now sam deploy will have your api deploy to "ApiGateway-loremipsum"
I'm trying to add a new endpoint to an existing service, and while it shows on the API Gateway Resources page, it's not showing on the Stages page. Hitting the endpoint returns the header x-amzn-ErrorType as IncompleteSignatureException and an error that includes the JWT token and not a valid key=value pair (missing equal-sign) in Authorization header.
Our setup uses a swagger file to define the endpoints, and uses x-amazon-apigateway-integration to link the endpoint to the lambda function. On first deployment of the new endpoint, we had some problems as this integration was setup wrong, but has since been corrected to match the others (suspect this is where the problem lies).
I've tried making sure that the new endpoint config matches that of other working endpoints; I've removed the endpoint, redeployed, added it back and redeployed; I've renamed the lambda. None of these have worked.
The swagger file looks like:
/companies/{id}/info:
get:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: successful operation
headers:
Access-Control-Allow-Origin:
type: string
x-amazon-apigateway-integration:
uri:
Fn::Join:
- ""
- - Fn::Join:
- ""
- - "arn:aws:apigateway:"
- Ref: AWS::Region
- ":lambda:path/2015-03-31/functions/"
- Fn::GetAtt:
- GetCompanyInfoLambdaFunction
- Arn
- "/invocations"
httpMethod: POST
type: aws_proxy
post:
#... this method works fine with same config but different lambda
Any ideas as to how to fix this? Thanks
At the moment I have an architecture in mind with AWS ApiGateway + Lambda for server HTML based on if a user is properly authenticated or not. I am trying to achieve this Cognito and a custom Lambda Authorizer. I'd like my Lambda to always return HTML and based on the cookie that is passed, generate HTML for a logged in / logged out state. In my mind that would be ideal to have a separate authorizer that does the token validation and pass a header to the HTML generating Lambda.
How can one achieve this?
I'm using AWS Sam template to define my CF stack. See my current template:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: A Lambda function for rendering HTML pages with authentication
Resources:
WebAppGenerator:
Type: 'AWS::Serverless::Function'
Properties:
Handler: app.handler
Runtime: nodejs12.x
CodeUri: .
Description: A Lambda that generates HTML pages dynamically
MemorySize: 128
Timeout: 20
Events:
ProxyRoute:
Type: Api
Properties:
RestApiId: !Ref WebAppApi
Path: /{proxy+}
Method: GET
WebAppApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
DefaultAuthorizer: WebTokenAuthorizer
Authorizers:
WebTokenAuthorizer:
FunctionArn: !GetAtt WebAppTokenAuthorizer.Arn
WebAppTokenAuthorizer:
Type: AWS::Serverless::Function
Properties:
CodeUri: .
Handler: authorizer.handler
Runtime: nodejs12.x
In my authorizer (Typescript) I was thinking of generating a policy that always has an 'allow' effect. But if an authorization token (not cookie-based yet) is missing, it's already returning a 403.
See:
function generatePolicy(principalId: string, isAuthorized: boolean, resource): APIGatewayAuthorizerResult {
const result: APIGatewayAuthorizerResult = {
principalId,
policyDocument: {
Version: '2012-10-17',
Statement: []
}
};
if (resource) {
result.policyDocument.Statement[0] = {
Action: 'execute-api:Invoke',
Effect: 'Allow',
Resource: resource
};
}
result.context = {
isAuthorized
};
return result
}
With Custom Authorizer, I'm not sure whether the functionality you mentioned is directly possible to achieve.
Can you check whether you can define a mapping template with content type text/html, following this guide? (Make sure your Lambda integration is not a proxy integration)
However, there are two alternative approaches that would work, if it's an option to you.
Use AWS Cloudfront, infront of API Gateway and configure error responses to show a HTML based on error status code.
Use Lambda Layers to authorize and decide on the response.
You cannot change the headers directly in the Authorizer Lambda... I achieved this using a Middleware in the lambdas and catching the "After" event...
You can check a popular middleware for lambdas: Middy