How to deploy multiple micro-services under one API domain with Serverless
Tags: microservices, api, serverlessframework, serverless
Published: 2022-04-01 14:29:57

Originally posted at [Serverless](https://www.serverless.com/blog/api-gateway-multiple-services/)

In this post, I’ll show you how to put multiple Serverless services on the same domain name. This is the [most requested issue in the Serverless repo](https://github.com/serverless/serverless/issues/3078) right now but is a tricky feature to implement directly within the Framework.

Using the power of Serverless and the [serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager) plugin, we can use API Gateway’s [base path mappings](http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html#how-to-custom-domains-mapping-console) to handle this. Follow the instructions below to deploy your two services to the same domain.

*Addendum: Many users asked about how to deploy to different domains based on the stage, e.g. staging-api.mycompany.com when deploying to staging. I've added a section on [Working with multiple stages](https://www.serverless.com/blog/api-gateway-multiple-services/#working-with-multiple-stages) below.*

If you already have your own services set up and just want the simple instructions, skip to the [TL;DR section](https://www.serverless.com/blog/api-gateway-multiple-services/#tldr) below.

### Getting Started

To get started, you’ll need the [Serverless Framework](https://serverless.com/framework/docs/providers/aws/guide/quick-start/) installed.

You should also have your desired domain name registered through AWS. Read the documentation on that [here](http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/registrar.html). You should also register a certificate for that domain through the AWS Certificate Manager. If you need help with that, read the *Getting a certificate for your domain *section of my previous post on [using API Gateway with Serverless](https://serverless.com/blog/serverless-api-gateway-domain/).

### Deploying your two services

Before we dive it, let’s discuss exactly what we’re trying to build. Imagine you have an e-commerce store which is a single-page application that consumes a backend REST API. Your REST API is hosted at api.mycompany.com, and you have two services: users and products.

You would like all users requests to be accessed at api.mycompany.com/users while all products requests would be accessed at api.mycompany.com/products. Further, you would like to separate these two services so they could be deployed independently -- changes to a products endpoint wouldn't require a redeploy of all users functions as well.

For this example, do the following:

First, create a new directory for your application:
{% gist https://gist.github.com/richardgrantserverless/094f77cd5dbbbcbe96ca2e79faf257ae.js %}
Then, create a directory for your users service:
{% gist https://gist.github.com/richardgrantserverless/93f396b8aeb0b24a032dc43256df6c84.js %}
In your users-service directory, add the following serverless.yml file:
{% gist https://gist.github.com/richardgrantserverless/d5b68f70809344d7a3282b50112f560c.js %}
Then add the following as handler.py:
{% gist https://gist.github.com/richardgrantserverless/360509a2d8c9abc6521bf4fa82050ee4.js %}
This is a super simple service with a single endpoint (/hello) that will return the name of the service. To test it, deploy the service:
{% gist https://gist.github.com/richardgrantserverless/bc173048291158f6eda5c3bdba537688.js %}
Copy and paste the endpoint into your browser, and you should see the following message:

![](https://cdn-images-1.medium.com/max/2788/0*lGTWEIfGgUqWoj9T.png)

As you can see, the URL isn’t very friendly. We’ll fix that during this walkthrough. To finish our setup, let’s make a products service as well. Move up a level in your directory structure, then copy the users-service into a products-service directory:
{% gist https://gist.github.com/richardgrantserverless/6e2fcf20870a615dd491c66f139670f4.js %}
Then edit the name of the service in your products-service serverless.yml:
{% gist https://gist.github.com/richardgrantserverless/bc99c2c99af4f775a917021b25f38687.js %}
Run sls deploy to deploy the products-service, and make sure it's working in your browser:

![](https://cdn-images-1.medium.com/max/2728/0*fqLXTREnPy7csRO-.png)

Again, it’s an ugly URL, which we’re going to change soon.

### Adding your services to your custom domain

Now that we have our two services set up, let’s add them to a custom domain. You should still be in your products-service. Let's install the [serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager) plugin:
{% gist https://gist.github.com/richardgrantserverless/f0eb06f284d38347799921c5adf0af30.js %}
Then add the configuration to your serverless.yml:
{% gist https://gist.github.com/richardgrantserverless/bddd013154e9d8535fe8fea476017244.js %}
We’ve added two sections to the serverless.yml. First, we registered the serverless-domain-manager in the plugins block. Then, we configured the plugin via the customDomain section of the custom block.

Note the basePath attribute that we're configuring. This will be prefixed to every route in our products-service. Thus, our route that is registered as /hello will actually be located at products/hello.

If you haven’t previously registered this domain with [API Gateway](https://serverless.com/amazon-api-gateway/), you’ll need to register it:
{% gist https://gist.github.com/richardgrantserverless/55522d442c75ca8f19a214b6621513d1.js %}
As the output notes, it can take up to 40 minutes to provision this in AWS. This is a one-time setup cost.

Once your domain is set up, deploy your service with sls deploy. Once the deploy is done, your endpoint will be available at api.mycompany.com/products/hello:

![](https://cdn-images-1.medium.com/max/2420/0*L0xsHu5g1C4yqwgI.png)

That’s a much cleaner URL!

Let’s do the same with our users service. Change into that directory:
{% gist https://gist.github.com/richardgrantserverless/5a14e9b6261fc2616a4c2db0e95bbef0.js %}
and follow the same steps as above. Install the serverless-domain-manager plugin:
{% gist https://gist.github.com/richardgrantserverless/de7def5047965ecd993ecdd645c3865f.js %}
and add the config to your serverless.yml:
{% gist https://gist.github.com/richardgrantserverless/3a180ff7735485912f3e30a082ecf3ed.js %}
Note that the basePath in this one is users, which will be prefixed to all routes in the users-service.

You don’t need to run serverless create_domain again. Because you created the domain already, it is available for any services that want to use it.

Run sls deploy to deploy the users service, then check it in your browser:

![](https://cdn-images-1.medium.com/max/2416/0*zX3oQ0ukSOzUMvGE.png)

That’s it! Now you easily separate your functions into services while still keeping them on the same domain. You’re not limited to two services on this domain — as you add additional services, just use a new basePath to add it to your domain.

### Working with multiple stages
>  *Addendum: A few people have asked about how to handle this with multiple stages (prod, staging, dev). I’ve added this section to show how to structure your project to handle this.*

When working with Serverless services, you’ll often have multiple stages for your project. In this section, I’ll show you how to set up your project to deploy to custom domains for different stages.

To set the scene, let’s imagine we have three stages:

* prod, which is accessible at api.mycompany.com;

* staging, which is accessible at staging-api.mycompany.com; and

* dev, which is accessible at dev-api.mycompany.com.

The first thing you need to do is get certificates for each of these domains in Amazon Certificate Manager. Please read the *Getting a certificate for your domain* section of my previous post on [using API Gateway with Serverless](https://serverless.com/blog/serverless-api-gateway-domain/).

Once you’ve done that, use the following config for your custom block in serverless.yml:
{% gist https://gist.github.com/richardgrantserverless/268971081a48b0e99857b5f763c85b26.js %}
Pay particular attention to this line:

domainName: ${self:custom.domains.${self:custom.stage}}"

We’re using the Serverless Framework’s powerful [variable system](https://serverless.com/framework/docs/providers/aws/guide/variables/) to infer the domain name based on the stage. I’ve set up my three stages in the domains block of the custom section. This will use my given stage to determine which domain to use.

Once this is set up, you’ll need to create your custom domain *for each of your stages*. This is a one-time setup step. If you use the stages I gave above, you would run:
{% gist https://gist.github.com/richardgrantserverless/83c68acb150c86dff14e23a496867331.js %}
Once your domains are set up, you can deploy to your proper stages! Use sls deploy --stage prod to deploy to api.mycompany.com and the other stages to deploy to their respective domains.

### TL;DR

If you already have multiple services set up and are looking to add them to the same domain, follow these steps.

Before you begin, you’ll need to get a certificate for your domain with the AWS Certificate Manager and register your domain with API Gateway. To do that, follow the steps in my previous post on [using a custom domain with API Gateway and Serverless](https://serverless.com/blog/serverless-api-gateway-domain/). Stop after the step that says sls create_domain.

In each service, install the [serverless-domain-manager](https://github.com/amplify-education/serverless-domain-manager) plugin:
{% gist https://gist.github.com/richardgrantserverless/94b01cfdde5b347b4718cdaf6d1d191e.js %}
Then, add the following configuration to your serverless.yml:
{% gist https://gist.github.com/richardgrantserverless/c09b2dcc2c04ad0e873db60a7eeb6b65.js %}
Make sure you change the domainName value to the domain name you want to use. Change the basePath value to the prefix you want for your routes in that service. For example, if you want your routes to start with /products/, the basePath value should be products.

Then, run sls deploy to get your service deployed to your custom domain with a base path!