# Serverless Framework: Setting up a custom domain to API Gateway

When we deploy our APIs using the `Serverless Framework`, it provides endpoints like, `https://api-id.execute-api.region.amazonaws.com/stage`. By using a custom domain we can make the endpoint name much more meaningful. 


![before-after-custom-domain.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1632242572972/4vR3pSK1s.png)

According to AWS docs,

> Custom domain names are simpler and more intuitive URLs that you can provide to your API users.

With `Serverless Framework`, it is very easy and straightforward to set up a custom domain by adding some configuration in our `serverless.yml` file.

### Prerequisite

Before we go further, we have to ensure,

* AWS CLI is installed in local machine
* Serverless framework is installed in local machine
* Node.js v12.13.0 in installed in local machine
* A hosted zone is setup `Route 53` with a domain
* An SSL certificate is installed for the domain and its sub-domains

### Create a Sample Project

We will use a popular serverless-typescript boilerplate. First, create a project,

```bash
serverless install --url https://github.com/AnomalyInnovations/serverless-typescript-starter --name my-serverless-project
```

Go to the project root directory and install the dependencies,

```bash
cd my-serverless-project
npm install
```

Update the region from the `serverless.yml` file. Since, I am in `ap-south-1` region, in my case, under `provider` the `region` should be `ap-south-1`.

Deploy the project to `AWS` by,

```bash
sls deploy
```

As output, we should get an endpoint like, `https://9pmx5phid6.execute-api.region-name.amazonaws.com/dev/hello`


![stack-output.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1632242614342/zg_V3yuYM.png)

If we go to the link, we will see a success message that indicates, out API and lambda function is working.


![raw-endpoint-output.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1632242688910/-KIvedpPn.png)

### Creating Custom Domain

To create a custom domain using the serverless framework, we will need a plugin named [serverless-domain-manager](https://www.npmjs.com/package/serverless-domain-manager).

Install the `serverless-domain-manager` in the project as a dev dependency,

```bash
npm install -D serverless-domain-manager
```

Now add the plugin to the `serverless.yml` file.


![plugin.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1632242746987/7oyl47cW0.png)

Now, we have to create a new section in the `serverless.yml` file, named `custom`,

```yml
custom:
  customDomain:
    domainName: my-sub-domain.my-domain.com
    basePath: "v1"
    stage: ${self:provider.stage}
    certificateName: original-domain
    createRoute53Record: true
```

Here,

**domainName**, the new domain name, that will replace the arbitrary auto created endpoint.

**basePath**, This will replace the stage name.

**stage**, This is the stage name, will be replaced by `API version` or `basePath`.

**certificateName**, SSL certified domain or sub-domain

**createRoute53Record**, Will automatically create a record in the hosted zone in `Route53`

Our final `serverless.yml` file should be very much be like the following,

```yml
service: host-service

package:
  individually: true

plugins:
  - serverless-bundle # Package our functions with Webpack
  - serverless-offline
  - serverless-dotenv-plugin # Load .env as environment variables
  - serverless-domain-manager

provider:
  name: aws
  runtime: nodejs12.x
  stage: dev
  region: ap-south-1

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get

custom:
  customDomain:
    domainName: host-api.my-tour-assistant.com
    basePath: "v1"
    stage: ${self:provider.stage}
    certificateName: my-tour-assistant.com
    createRoute53Record: true
```

Create the domain,

```bash
sls create_domain
```

Now deploy the app again and it will do the mapping,

```bash
sls deploy
```

After deployment is completed, if we go to the `https://domainName/basePath/hello`, we should see the success message as JSON format as expected.


![new-url-domain.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1632242810802/k_Tzcaegx.png)

In my case, the new URL becomes `https://host-api.my-tour-assistant.com/v1/hello`.

Our custom domain is now integrated with the API-Gateway. 

### Handle Multiple Stage with Serverless Domain Manager

There could be a scenario, where we want to do the mapping for the production and development environment.

We want to run the dev API in `dev.domainName/basePath` and prod API in `domainName/basePath`.

In this case, our `custom` section of the `serverless.yml` file should be,

```yml
custom:
  domain:
    dev: tour-api-dev.my-tour-assistant.com
    prod: tour-api.my-tour-assistant.com
  customDomain:
    domainName: ${self:custom.domain.${opt:stage, 'dev'}}
    basePath: "v1"
    stage: ${self:provider.stage}
    certificateName: my-tour-assistant.com
    createRoute53Record: true
```

In this case, when we create the domain, we have to specify the stage. For example, create domain,

```bash
sls create_domain --stage dev # for dev API
sls create_domain --stage prod # for prod API
```

Similarly, when we deploy the app to AWS, we have to specify the stage also,

```bash
SLS_DEBUG=* sls deploy --stage dev -v # Deploy the app to the development environment
sls deploy --stage prod -v # Deploy the app to the production environment
```

For any queries or clarifications, please let me know.

For more AWS articles, take a look at my [AWS Series](https://blog.shams-nahid.com/series/aws).

### References

* [Install Serverless Framework](https://www.serverless.com/framework/docs/getting-started/)
* [Create Template](https://www.serverless.com/framework/docs/providers/aws/cli-reference/create)
