One way I would recommend to solve the issue of API stages and indeed APIs themselves being replaced unexpectedly is to use a Custom Domain name as an abstraction layer. This requires you to have a domain you own and can control which maps to an AWS API Gateway custom domain name (which can easily be provisioned in your template.yaml) which then maps to your API.
(Just as an aside I might also suggest not to make changes directly to your stack via the AWS console (unless essential & then ideally also reflected in the template.yaml) as you may get into 'Drift state' in that your template.yaml and stack have changed, AWS may not know how to resolve these diffs next time you come to deploy changes which may remove some resources in an attempt to resolve the diffs. You are doing this correctly currently via IaC (Infrastructure as Code) and AWS SAM is AWS's recommended method)
The AWS API Gateway custom domain name provisions a load-balancer under the hood which presents a static endpoint which your DNS can point to, AWS manages the mapping of the custom domain name to the API resources, so it doesn't matter if AWS then needs to replace any resources this is mapped to, so you have created an abstraction layer for safety against this issue.
Some more info on custom domain names: https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html
So, the flow will be:
sub-domain on a domain you control -> API Gateway custom domain name -> AWS API Gateway -> lambdas (handling routes)
An example sub-domain under your main Co domain might be: https://some-service-name-api.your-co-website.com or https://api.your-co-website.com/some-api-path (with alternative path mapping)
To map your domain name -> your AWS Custom Domain name resource you will need to create a CNAME record in your Company DNS (eg in route 53 or wherever it is managed).
In order let AWS know you can control that domain (and provide SSL for https) you will need to create a Certificate in AWS Cert Mgr in the same region as your API. You will then be able to add that Cert in your domain DNS as a CNAME to validate you can control the domain
The steps in full:
- Create a certificate in the same region as your stack (do this via the AWS Console for ease). You will need the top level domain and also add any sub-domains you might want to include to that cert. Note - it will say 'Pending validation' until you complete step 2.
- Add the CNAME and CNAME value for the Cert you have just created (may require multiple for sub-domains) as a CNAME record(s) to prove you control the domain, it will look for example like: name:
_6c7353b72be1c1b5d262839c123456789.your-domain.com
value: _0833d6c57a7a5b8123456789.zzhfktmlwt.acm-validations.aws (when done correctly the 'Pending Validation' on the record will change to 'Success', then you can move to Step 3.
- Add the below (with updated names where required of course!) to your template.yaml and deploy the stack
- Once the stack is deployed, goto API gateway and Custom Domain Names, find the newly created custom domain name and you will see the API Gateway domain name (load-balancer name) name that has been provisioned. Add a CNAME record for your sub-domain.your-domain.com -> API Gateway custom domain name
Resources:
protectedApi:
Type: AWS::Serverless::Api
Properties:
StageName: your-stage-name
Auth:
# DefaultAuthorizer: NONE
DefaultAuthorizer: AWS_IAM
Authorizers:
LambdaAuthorizer:
FunctionPayloadType: REQUEST
FunctionArn: !GetAtt LambdaAuthorizer.Arn
Identity:
Headers:
- Authorization
CustomDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
RegionalCertificateArn: arn:aws:acm:eu-west-2:621234567874:certificate/12345678
DomainName: my-api.your-domain.com
SecurityPolicy: TLS_1_2
EndpointConfiguration:
Types:
- REGIONAL
Tags:
- Key: PROJECT
Value: "Example auth API with custom domain name"
- Key: PROVISIONEDBY
Value: "SAM/ CLOUDFORMATION"
DependsOn:
- protectedApi # this ensures that your API is provisioned first
PathMappings:
Type: AWS::ApiGateway::BasePathMapping
Properties:
DomainName: !Ref CustomDomainName
RestApiId: !Ref protectedApi
Stage: your-stage-name
You should now be able to make amendments safely, and your endpoint always remain fixed eg https://my-api.your-domain.com/some-route