I have a simple API gateway definition in AWS.
It has a single Method for "/" with a "GET" HTTP type.
I've updated the "Integration Request" to be a "Mock" (return a response purely with API Gateway mappings ...).
There's nothing fancy, all requests will be passed through.
For the response, I've updated the default Integration Response ( response status 200, no regex, model etc. Default mapping).
It has an "application/json" Mapping template, and in here i have just added the static text "hello world" (creative, i know).
Now, the question is, can i define this in a SAM template ?
I can readily find plenty of exmaples to create a lambda function behind this and have it do the response and all sorts of other things.
I can run the SAM cli locally to do "sam deploy" giving the sam template, and have a whole new AWS Stack created in my account, with the API Gateway created, the lambda python or javascript or etc uploaded to s3 and created and linked in to the API (through implicit sam definition, no external oas/swagger looked at yet) and roles etc created.
And it all works beautifully.
But if I want to do the same thing, but simpler - with just API gateway only and a mock integration response (no lambda) - what would the SAM template look like for that ?
As of yet, i am at a loss.
I can see from the serverless model that a lot of the integrations are supported in sam (x-amazon-apigateway-integration response).
I can see what the oas version looks like by using "api-gateway getexport" via the aws cli.
But I can not see a SAM template with implicit API creation that has no lambda linked in, just a simple Mock and static integration response.
Any help appreciated !!
SAM is a extension of Cloudformation, so yeah, it is possible, if you create a sam application and add the bellow template.yaml file, and run sam deploy --guided you will create what you need. In this case you will be running a simple cloudformation template that can be deployed with the following command:
aws cloudformation deploy --stack-name mock-api --template-file template.yaml
The main idea of SAM is facilitate the deploy of Serverless aplications, creating a possibility of invoke Lambda functions locally, improve debugging and so on, while provisioning your resources.
As we can see in the AWS documentation:
A serverless application is a combination of Lambda functions, event sources, and other resources that work together to perform tasks. Note that a serverless application is more than just a Lambda function—it can include additional resources such as APIs, databases, and event source mappings
So, if you are not using Lambda and you want provision your resources, you can simply use Cloudformation or another IaC tool as Terraform.
template.yml example (of what you need):
AWSTemplateFormatVersion: '2010-09-09'
Description: AWS API Gateway with a Mock Integration
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
ApiKeySourceType: HEADER
Description: An API Gateway with a Mock Integration
EndpointConfiguration:
Types:
- EDGE
Name: mock-api
ApiGatewayResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt ApiGatewayRestApi.RootResourceId
PathPart: 'mock'
RestApiId: !Ref ApiGatewayRestApi
ApiGatewayMethod:
Type: AWS::ApiGateway::Method
Properties:
ApiKeyRequired: false
AuthorizationType: NONE
HttpMethod: POST
Integration:
ConnectionType: INTERNET
IntegrationResponses:
- ResponseTemplates:
application/json: "{\"message\": \"OK mock response\"}"
SelectionPattern: '2\d{2}'
StatusCode: 200
- ResponseTemplates:
application/json: "{\"message\": \"Internal Server Error mock response\"}"
SelectionPattern: '5\d{2}'
StatusCode: 500
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
application/json: "{\"statusCode\": $input.json('$.statusCode'), \"message\": $input.json('$.message')}"
Type: MOCK
TimeoutInMillis: 29000
MethodResponses:
- ResponseModels:
application/json: !Ref ApiGatewayModel
StatusCode: 200
- ResponseModels:
application/json: !Ref ApiGatewayModel
StatusCode: 500
OperationName: 'mock'
ResourceId: !Ref ApiGatewayResource
RestApiId: !Ref ApiGatewayRestApi
ApiGatewayModel:
Type: AWS::ApiGateway::Model
Properties:
ContentType: 'application/json'
RestApiId: !Ref ApiGatewayRestApi
Schema: {}
ApiGatewayStage:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId: !Ref ApiGatewayDeployment
Description: Mock API Stage v0
RestApiId: !Ref ApiGatewayRestApi
StageName: 'v0'
ApiGatewayDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn: ApiGatewayMethod
Properties:
Description: Mock API Deployment
RestApiId: !Ref ApiGatewayRestApi
To do this another way with SAM - i have
manually created a simple API gateway with a single GET and a mock response of static text in the integration response.
exported that with aws apigateway get-export
remove the servers and policy blocks (those i'll have in my sam template)
refer to the OAS spec in your SAM template in the API properties with DefinitionBody
upload the OAS spec
use SAM deploy
the SAM specifications.
For that approach thanks to valdeci for this answer, and Jens Eickmeyer for this post
Related
I'd like to be able to configure API Gateway Responses via serverless framework...
This is what my serverless.yml looks like:
#Deploy an api gateway with custom responses.
---
service: test-apigw
frameworkVersion: ">=3.20"
provider:
name: aws
stage: dev
region: us-east-1
stackName: ${self:service}
apiName: test-apigw
endpointType: REGIONAL
functions:
hello:
handler: handler.endpoint
events:
- http:
path: /hello-world
method: ANY
private: true
package:
individually: true
excludeDevDependencies: false
Now I read somewhere that one can configure resources in a serverless file, however by using CFN code I'd need the ARN or the Ref of an already deployed API Gateway or, declare the API Gateway with CFN code (which I want to avoid)...
Is there a way to configure these Gateway Responses (not integration responses!!) using the same api gateway that my serverless.yml is deploying? i.e. the one with name test-apigw?
It turns out the response I quoted actually works for me.
In the same serverless.yml file, put the GatewayResponses you want to configure.
resources:
Resources:
Unauthorized:
Type: "AWS::ApiGateway::GatewayResponse"
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
ResponseTemplates:
"application/json": ${file(gateway-responses/responses.js):unauthorized}
ResponseType: UNAUTHORIZED
RestApiId: !Ref ApiGatewayRestApi
StatusCode: '401'
At least for version 3.20 of serverless framework, the created API name is ApiGatewayRestApi, this will only work of course, if you're only creating one API Gw in that file and I believe, the API Gateway has to be already deployed.
The correct way to do this is, as the official serverless documentation states, to create the API Gateway with Cloudformation code.
I am trying to deploy the api and lambda function using SAM. But the Method Response and Integration Response are empty. I am aiming to configure the api-gateway like below picture. In this I have manually configured the api-gateway and its working but when I use the SAM to generate the api-gateway it doesn't shpes the Method Response and Integration Response.
Without Sam:
With Sam:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 500
MemorySize: 5000
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
GatewayResponses:
DEFAULT_4xx:
ResponseParameters:
Headers:
Access-Control-Expose-Headers: "'WWW-Authenticate'"
Access-Control-Allow-Origin: "'*'"
InferenceFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
Events:
Inference:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /zero-shot-classification
Method: post
RestApiId: !Ref MyApi
Metadata:
Dockerfile: Dockerfile
DockerContext: ./zero-shot-classification
DockerTag: zero-shot-classification
The difference you see is the difference between how the payload is sent to the lambda and how API gateway processes the response from the lambda. Seeing as in your CloudFormation you do not specify a specification for your API endpoints, AWS SAM will construct this based on the events from your functions. However, AWS SAM will always use the LAMBDA_PROXY integration type, while you seem to be wanting to use the LAMBDA integration type.
I'd suggest you take your manually created API gateway and export it to an openapi/swagger definition. This can then be used as DefinitionBody in you MyApi. That way you can specify it uses the LAMBDA integration instead of the LAMBDA_PROXY integration. Don't forget to update the swagger definition so it uses the created function in your cloudformation and not the function that existed when you exported the API.
That being said, the LAMBDA_PROXY way is more widely used and also removes the need for template mappings in your API Gateway. It is also the only integration type for lambdas that is supported in the HTTP API (API Gateway v2), which is a lot cheaper than the classic REST API.
I have built out an API Gateway that integrates with Dynamodb using the AWS Console and have it up and running as a proof of concept. Now I want to take that API Gateway and re-produce it via CloudFormation for actual usage.
I've been able to get most of it deployed using my CFT but I'm stuck on the AWS::ApiGateway::Method resource. Specifically, the Integration property.
ResourceHttpGet:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationScopes:
- openid
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: {'Fn::ImportValue': !Sub '${ProductName}-${TargetEnvironment}-authorizer-customerpool' }
HttpMethod: GET
OperationName: GetFoo
RequestModels:
"application/json": {'Fn::ImportValue': !Sub '${ProductName}-${TargetEnvironment}-apigw-foomodel' }
Integration:
Credentials: !Ref ApiGatewayDynamoDbRole
IntegrationHttpMethod: POST
RequestTemplates:
application/json: !Sub '{ "TableName": "${ProductName}-${TargetEnvironment}-dynamo-primaryapi", "KeyConditionExpression": "UserId = :val", "Limit": 50, "ExpressionAttributeValues": { ":val": { "S": "$context.authorizer.claims.sub" } } }'
Type: AWS
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:dynamodb:action/Query"
ResourceId: !Ref FooResource
RestApiId: {'Fn::ImportValue': !Sub '${ProductName}-${TargetEnvironment}-apigw-primaryapi' }
When I upload this into the CloudFormation Console and run it the CF Stack fails with the following error on the ResourceHttpGet resource:
Invalid ARN specified in the request (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: e6767c61-96dc-461a-ab7c-fd625344b59f; Proxy: null)
I can comment out the Integration property and the Stack successfully deploys. I've used a couple of different resources to help validate the ARN I'm using is accurate. One being a blog post I found on API Gateway/Dynamodb integration and a second being here on Stackoverflow
Can someone point out what I'm doing wrong here? Why doesn't it like the Uri I provide when it confirms to the specification/API requirements below:
arn:aws:apigateway:region:subdomain.service|service:path|action/service_api. For example, a Lambda function URI follows this form: arn:aws:apigateway:region:lambda:path/path.
I've also removed the !sub function usage and just hard-coded the Region into the Uri in order to test if the usage of intrinsic functions caused the issue but that didn't make a difference.
Totally confused and would appreciate any guidance someone can give to me on this.
If you're trying to create a CloudFormation file based on an API Gateway setup that you've created manually, I would take a look at AWS SAM. Which is much less verbose and easier to set up. Specifically, I would have a look at the DefinitionBody property of the AWS::Serverless::Api type. This property allows you to pass in an openapi body definition of your API.
As a starting point, you can also export your current api using the console or one of the other methods indicated in the documentation.
Combining those two things would make it a lot easier to construct your API Gateway in CloudFormation.
I'm trying to work my way through AWS sam and setting up a new ApiGateway. I want the lambdas to run without the 'use lambda proxy integration' setting. I've been trying for ages now, and haven't made much progress.
A simplified version of my template.yaml
Resources:
MyLambda:
Type: AWS::Serverless::Function
Properties:
#snip#
Events:
PostEvent:
Type: Api
Properties:
Path: /Some/Path
Method: Post
RestApiId:
Ref: MyApi
MyApi:
Type: AWS::Serverless::Api
Properties:
Name: Some-Api
StageName: Prod
As you can see, I haven't made any progress at all. I'm simply getting lost in the configuration here, and hoping that anyone can point me in the right direction
Sicne you have opted out of integration proxy, the request and response may need mapping.
Have you tried updating the integration response - mapping template, try below,
AWS::Serverless::Api
GatewayResponses
I successfully deployed a Cognito User Pool and used it to authenticate against a Method that I set up to proxy to my API in API gateway, and now I'm creating a Cloudformation template of the same stack. Using Cloudformation, I set up my API Gateway and the Authorizer that uses my User Pool. Works fine. when I try to deploy a Method that uses the Authorizer, it fails saying:
Invalid authorizer ID specified. Setting the authorization type to CUSTOM
or COGNITO_USER_POOLS requires a valid authorizer.
This is the relevant part of the Cloudformation stack:
TestMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref TestResource
HttpMethod: POST
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId: !Ref ApiAuthorizer
Integration:
Type: HTTP_PROXY
IntegrationHttpMethod: POST
Uri: https://api.example.com/test
ApiAuthorizer:
Type: "AWS::ApiGateway::Authorizer"
Properties:
AuthorizerResultTtlInSeconds: 300
IdentitySource: method.request.header.Authorization
Name: CognitoDefaultUserPoolAuthorizer
ProviderARNs:
- !ImportValue DefaultUserPool::Arn
RestApiId: !Ref RestApi
Type: "COGNITO_USER_POOLS"
The Authorizer deploys fine and I can see its ID in Cloudformation, and the method deploys fine without authorisation added to it. The same problem happens if I state the ID directly instead of referencing the Authorizer.
The Authorizer docs for Cloudformation say that a Ref! should return the ID, so I'm really puzzled as to what's going on here.
Seems like a bug to me but maybe I'm missing something?
AWS have fixed this now. It works as per my original posted example.