Search code examples
amazon-web-servicesaws-api-gatewayaws-cliamazon-route53aws-sam

How to configure a custom domain for HttpApi using AWS SAM?


I am developing an API using AWS Lambda, AWS API Gateway and aws-sam. I have implemented firebase authentication. I also use nested stacks.

I am trying to use a custom domain for my API endpoints, so I can call like api.mydomain.com/products, api.mydomain.com/sales and so on. I have the domain ready, and a certificate from the AWS Certificate Manager. I even managed to deploy my aws-sam application without the domain configurations and then assign the custom domain and domain mappings manually via the AWS API Gateway web console.

But I need to do that part in the aws-sam itself. Below is what I tried.

Root Stack

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  aws-restapi

  Sample SAM Template for aws-restapi
  
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 5
    VpcConfig:
        SecurityGroupIds:
          - sg-041f215425921e8e
        SubnetIds:
          - subnet-0251b2d


Parameters:
  FirebaseProjectId:
    Type: String
  
  DomainName:
    Type: String
    Default: api.example.com

Resources:
  AuthGatewayHttpApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      Auth:
        Authorizers:
          FirebaseAuthorizer:
            IdentitySource: $request.header.Authorization
            JwtConfiguration:
              audience:
                - !Ref FirebaseProjectId
              issuer: !Sub https://securetoken.google.com/${FirebaseProjectId}
        DefaultAuthorizer: FirebaseAuthorizer

  AppDomainName:
    Type: AWS::ApiGateway::DomainName
    Properties:
      CertificateArn: arn:aws:acm:us-east-1:xxxxxxx43:certificate/1axxxf-3234-xxx2f-a61c-924eeexxxx9
      DomainName: !Ref DomainName

  APIBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      BasePath: "api"
      DomainName: !Ref AppDomainName
      Stage: "default"
      RestApiId: !Ref AuthGatewayHttpApi
  
  AuthFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: aws-restapi/
      Handler: source/testfile.lambdaHandler
      Runtime: nodejs14.x
      Events:
        Gateway:
          Type: HttpApi
          Properties:
            ApiId: !Ref AuthGatewayHttpApi
            Path: /hello
            Method: get

  GetAllAccountingTypesFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: aws-restapi/
      Handler: source/accounting-types/accountingtypes-getall.getallaccountingtypes
      Runtime: nodejs14.x
      Events:
        GetAllAccountingTypesAPIEvent:
          Type: HttpApi # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            ApiId: !Ref AuthGatewayHttpApi
            Path: /accountingtypes/getall
            Method: get

  GetAccountingTypeByIDFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: aws-restapi/
      Handler: source/accounting-types/accountingtypes-byid.getbyid
      Runtime: nodejs14.x
      Events:
        GetAllAccountingTypesAPIEvent:
          Type: HttpApi # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /accountingtypes/getbyid
            Method: get

nested stack

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  aws-restapi

  Sample SAM Template for aws-restapi

Globals:
  Function:
    Timeout: 5
    VpcConfig:
        SecurityGroupIds:
          - sg-041f2459xxxx1e8e
        SubnetIds:
          - subnet-03xxxx2d

Parameters:
  FirebaseProjectId:
    Type: String

Resources:

  AuthGatewayHttpApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      Auth:
        Authorizers:
          FirebaseAuthorizer:
            IdentitySource: $request.header.Authorization
            JwtConfiguration:
              audience:
                - !Ref FirebaseProjectId
              issuer: !Sub https://securetoken.google.com/${FirebaseProjectId}
        DefaultAuthorizer: FirebaseAuthorizer

  GetAllPromotionsFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: aws-restapi/
      Handler: source/promotions/promotions-getall.getAllPromotions
      Runtime: nodejs14.x
      Events:
        GetAllPromotionsAPIEvent:
          Type: HttpApi # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /promotions/getall
            Method: get
            ApiId: !Ref AuthGatewayHttpApi
  SavePromotionsFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: aws-restapi/
      Handler: source/promotions/promotions-save.savePromotions
      Runtime: nodejs14.x
      Events:
        SavePromotionsAPIEvent:
          Type: HttpApi # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /promotions/save
            Method: get

However I cant get this to work. i even tried applying this only for the root stack, then i ended up with the following error.

Mixing of REST APIs and HTTP APIs on the same domain name can only be accomplished through API Gateway's V2 DomainName interface. 
Currently, WebSocket APIs can only be attached to a domain name with other WebSocket APIs. 
This must also occur through API Gateway's V2 DomainName interface. 
(Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 2f44d53b-8175-47f5-8bc8-db5 19aa484e7; Proxy: null)   

How can I successfully configure a custom domain to be used with the API Gateway?


Solution

  • Add the Domain property config, here is an example:

    AuthGatewayHttpApi:
      Type: AWS::Serverless::HttpApi
      Properties:
        Auth:
          Authorizers:
            FirebaseAuthorizer:
              IdentitySource: $request.header.Authorization
              JwtConfiguration:
                audience:
                  - !Ref FirebaseProjectId
                issuer: !Sub https://securetoken.google.com/${FirebaseProjectId}
          DefaultAuthorizer: FirebaseAuthorizer
        Domain:
          DomainName: www.example.com
          CertificateArn: arn-example
          EndpointConfiguration: REGIONAL
          Route53:
            HostedZoneId: Z1PA6795UKMFR9
    

    More info here : https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-httpapi-httpapidomainconfiguration.html#sam-property-httpapi-httpapidomainconfiguration--examples