AWS API Gateway, VPC Private Links, and NLBs
Start course
2h 11m
In this advanced course, we take a legacy monolithic .Net application and re-architect it to use a combination of cloud services to increase scalability, performance, and manageability. 
Learning Objectives

This course will enable you to:
  • Understand the principles and patterns associated with microservices
  • Understand the principles and patterns associated with Restful APIs
  • Understand important requirements to consider when migrating a monolithic application into a microservices architecture
  • Understand the benefits of using microservices and associated software patterns and tools to build microservice based applications at speed and scale
  • Understand tradeoffs between different architectural approaches
  • Become familiar and comfortable with modern open source technologies such as Dotnet Core, Docker, Docker Compose, Linux, Terraform, Swagger, React
  • Become familiar with Docker and Container orchestration runtimes to host and run containers, such as Docker Compose, Amazon ECS using Fargate, and Amazon EKS


  • A basic understanding of software development
  • A basic understanding of the software development life cycle
  • A basic understanding of Devops and CICD practices
  • Familiarity with Dotnet and C#
  • Familiarity with AWS
Intended audience
  • Software Developers and Architects
  • DevOps Practitioners interested in CICD implementation
  • Anyone interested in understanding and adopting Microservices and Restful APIs within their own organisation
  • Anyone interested in modernising an existing application
  • Anyone interested in Docker, and Containers in general
  • Anyone interested in container orchestration runtimes such as Kubernetes

Source Code


Welcome back. In this lecture we're going to go ahead and use the AWS console and complete the changes that we discussed in the previous lecture. In particular, we're gonna set up Amazon API Gateway, we're going to connect it to a VPC private endpoint, and we're gonna set up new workload balances, but in the reverse order. So let's begin. We'll start by heading into the EC2 centers. Here we'll create two NLBs, one for the account service, and the other for the inventory service. Clicking Load Balances, we click the Create Load Balancer button, and now we're gonna create the first of our two network load balances. Giving it a name, NLBAccountService. It'll be internal facing. 

The load balancing protocol will be TCP. We'll select our production VPC. And then we need to allocate the subnets that it will be deployed into, which will be our private subnets. Under tags, I'll give it a name. And then we click next. Here we'll call it Target Group Account Service. Protocol will be TCP on port 80. The target type will be IP address. We then click Next, Register Targets. So at this stage we need to know the private IP's of the tasks that we're going to register into this NLB. So what we'll do here is we'll head over into Route 53. And then we'll navigate into the service discovery private zone that we established earlier on. And then we need to register these two private IP addresses. So we'll take the first one, enter, Add to list, you can see that it discovers the network interface. And then we repeat it for the other task. Paste, add to list. Again, it's discovered the elastic network interface. Clicking the Review button, we then click Create. So that will create both the network load balancer and the target group. Click close, and then we'll repeat this for the second network load balancer, which is where our inventory server is. 

Give it a name, NLBInventoryService. It will also be internal facing. The load balancer protocol will be TCP. We'll place it into our production VPC. We specify the private subnets. And then we'll also set a tag name. Here we create the target group. We'll call it Target Group Inventory Service. It will be protocol TCP on port 80. The target type will be IP. And we'll click Register Targets. And again we need to register the IP addresses for the inventory service. So heading over to Route53, we'll take the first address, paste, Add to list, the ENI has been discovered, we'll go back, grab the second address, paste, add to list, and again we've discovered the ENI. Okay, clicking review, click create, and we've successfully created our NLB load balancer for the inventory service and target group. Okay, having skipped the demo ahead we can see that both NLB's are in a active state which is great. Let's now look at the associated target groups. Clicking the account service target group, we can see that our registered targets are both showing up as healthy, as we want. Likewise, the target group for the inventory service both registered targets are in a healthy status. We'll now use API Gateway to set up our two API's. The first one, for the account API, and the second one for the inventory API. Clicking the Get Started button, we'll import from Swagger.

 To do so, we need to jump over into our terminal and start up our local environment. We do so by running docker-compose up. This will bring up our local development environment and from which we can browse locally to our service components. Back within our browser we'll brose to http localhost port 8081 Swagger index.html. So this brings up the Swagger interface that we built into our account service API. Clicking on the Swagger.json link, we get access to our definition of the API. We'll select it and copy and then back within API Gateway we'll paste it, scroll down, ignore warnings, and finally import. So this will go ahead and create the API automatically withinn API Gateway. Here we can see that we have the same structure as we designed back within Virtual Studio. Now to keep the demo moving along quickly we'll only implement one method, that is the GET method, here. So everything else we'll delete. 

We'll delete this resource. We'll delete the PUT method. And the DELETE method. The POST method. And the GET method directly under consumers. Excellent. So then going back to API's we'll do the same thing for the inventory API. Import from Swagger, this time we'll browse on 8082. We'll copy the API definition. Back into API Gateway, we'll paste. Ignore warnings, and finally import. Again, we'll only implement the one method that is being used by our front end to keep the demo moving quickly. Therefore we'll remove this resource. We'll delete this method. And finally we'll delete this resource. Okay, the next thing we need to do is set up our VPC private links. We need to create two, one for each API. Okay, the first one we'll create for the Account Service. And we'll set the target NLB to be the NLB Account Service. Create. Again we create the next one. This one will be called VPCLinkInventoryService. And for this one we select the NLB Inventory Service. Clicking Create. Now the VPC Private Links take a few minutes to create, so we'll skip the demo ahead to here. Okay, so both VPC links are now showing up with a status of Available. So we'll jump back into the first of the API's. And then for the one method that we need to set up, we'll select VPC Link for Integration type. We'll enable the use of Proxy Integration. The method will be set to GET.

 And the VPC Link here will be VPCLinkAccountService. The Endpoint URL will be http. We should be able to use this address. We'll copy it. This is the address that our service discovery has automatically created for us for the account API Tasks. So going back to API Gateway, we'll enter it here, followed by API followed by consumers, and we need to pass the path perimeter which is ID. Okay, when we go to save this, this won't work as per the message given here. Now, I believe this problem is legacy in the sense that when API Gateway was first released you couldn't actually route two private endpoints. So the workaround here is if we go back into Route53, we'll copy this record, now we need to navigate into a public zone. 

And so we've got one here called So within our public zone we'll create a record set, and we're gonna create a CNAME pointing to our API-account.microservices.private record. And we'll call it dash internal. We'll copy that and we'll click create. So we've created a CNAME record pointing to our service discovery record. Okay, back into API Gateway, we now change the host to the one we just created,, and therefore the validation on this should now pass. Excellent. As it has. So then we can test this. So jumping on the test client, we'll set the ID to 5, and then we'll click the test button. The execution has succeeded, we've got a valid response back, we got a status code of 200, we've got the consumer response for consumer ID five, and if you scroll down on the logs if you look closely, we should be able to see that we're getting a response from our Nginx reverse proxy embedded within the account service microservice. Okay, so we need to repeat this for the inventory service. So clicking on Inventory Service, clicking on the GET method, we set the integration type to VPC Link, we enable proxy integration, we set the method to GET, and then under VPC link, this time we use VPC link inventory service. We enter http, and this time, Click save. Now before we test it, we need to update Route53. 

So within our public zone we're gonna create another record. This time the name will be API-inventory-internal. It will be a CNAME type, pointing to the service discovery inventory microservice record. Click create. Okay, that looks good, go back to API Gateway, we'll click on Test, and here we click the Test button, and excellent, again we get a 200, and we're getting our list of products from our microservices inventory service. Okay, the next thing we need to do to both API's is to enable cross origin requests on them. We do so by navigating to Actions, Enable CORS, we'll enable, we don't have to worry about the errors. And then finally we can deploy the API. We'll select New Stage, and we'll call it prod, and then we click the Deploy button. Okay, we can copy this URL. We can jump back into our terminal. And we can test it by doing a curl dash I slash API slash products. Okay, you need to be careful on this one because the API within API Gateway is case sensitive. So in this case it needs to be uppercase, Products. Excellent, it's worked. So we've got our products coming back, and we've got an HTTP 200. So, jump back into API Gateway. And we'll do the same thing on account service. So again we'll enable CORS first. Ignoring the errors. We then deploy the API. Select New Stage, we'll call it prod. Deploy. Again we'll test the URL. We'll take a copy, curl dash I, paste, slash API, slash uppercase C, consumers, and we'll go for number five. And excellent, again that's worked. So we've got our consumer response, and we've got an HTTP 200. Okay, the next thing we need to do is jump back into API Gateway, and we're gonna set up custom domain names. So here we're gonna set up a custom domain name for each API. Now before we do this let's jump over into Visual Studio. 

And back within our spa.jsx page. You can see here that the AJAX request is going to So this will be the custom domain name that we'll set up. So we'll copy that, we'll click create custom domain name, and we'll paste it. We'll then set the endpoint configuration to be regional, and we'll select ACM provisioned wildcard certificate for We'll click save. And then we'll create another one for the inventory service. So again, back over into Visual Studio, copy the domain name, paste, set the endpoint configuration to regional, selecting the same certificate, click save. We'll update the base path mappings. We click Edit, we click Add mapping, we leave the path ID. For destination we select inventory service, and for stage we select prod. Click save. Likewise, on accounts, we click Edit, Add mapping, under destination we select Account Service and then for stage we select prod. Click Save. Okay. Now we need to take the target domain name copy, and set this up in Route53, under this host name, so back within Route53, create record set, under Value we paste, we set the type to CNAME, and for this one it's API-products. 

We'll click create. And then we'll repeat the same thing for the accounts version. Taking a copy of the target domain name back into Route53. Create record set. The value will be the target domain name. The type is CNAME. And the name will be API-accounts. We'll click create. Okay so at this stage everything is in place. Let's now jump back into our terminal and test this. Type in curl dash I https, and then our new domain name, API dash accounts,, consumers with a capital C, and five. Awesome, so this has worked. So this is important, this is doing a number of things. Firstly, it's using custom domain name. It's doing it over SSL, and it's using our custom wild carded certificate that we've provisioned in ACM. Also, you'll notice here that we no longer need to present the stage name in the URL. Next we'll test the inventory service. Curl dash I, HTTPS, API dash with a capital P, enter. 

Awesome, so we've got our product list coming back, again with an HTTP 200. Okay, so everything is in place. In the final test now, if we bring up a new incognito window to ensure that no location takes place, if we navigate to, and we click enter, we're navigating to the presentation front end as served from the Application load balancer. Now, this is the old styled presentation layer, which is using the razor templates. We need to then go to home/react, which has implemented our new client side based rendering system. We'll click enter. And if all goes well this should be making AJAX calls from API Gateway through our VPC private links through the NLB's and then finally to the microservice components, remembering that the first time that this runs it has to do the compilation of JSX and pure JavaScript, which is a one off task and one you wouldn't normally do in a production system. 

Excellent, that has worked end to end. Here we've got the product listing and in the far right corner we've got the consumer. So both sets of data have come from our AJAX calls to our backing components. Let's navigate into Developer tools, we'll do a reload, and indeed we can see our two AJAX calls. So the first one is going to our Consumers microservice via the API Gateway. If we look at the response, we're getting the expected response. And likewise the AJAX call to the Products microservice component is going via API Gateway, and again, we're getting the response as expected. So that completes this demonstration and is the end of this lecture. Go ahead and close it and we'll see you shortly in the next one.

About the Author
Learning Paths

Jeremy is a Content Lead Architect and DevOps SME here at Cloud Academy where he specializes in developing DevOps technical training documentation.

He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 25+ years. In recent times, Jeremy has been focused on DevOps, Cloud (AWS, Azure, GCP), Security, Kubernetes, and Machine Learning.

Jeremy holds professional certifications for AWS, Azure, GCP, Terraform, Kubernetes (CKA, CKAD, CKS).