How to route requests to right tenant api gateway? - amazon-web-services

I am creating a multi tenant silo mode architecture to support a SAAS application. Following this link.
I am able to register new tenants and create their respective stack like this:
So far so good, the next step is to create each tenant its own domain, for example: tenant1.admin.foo.com, to access the same CloudFront distribution (the web front end must be the same for all). I can make this by creating a record in Route53 *.admin.foo.com that has access to CloudFront
THE PROBLEM:
I need to route every request to their respective tenant stack, for example: tenant1.api.foo.com/whatever should route to the api gateway created for tenant1.
At first I thought of creating an origin in CloudFront that routes to the api gateway, the problem with this is that CloudFront origins are limited to 25.
I was thinking in creating a record in Route53 to point to their respective api gateway, but the problem is that I will have to use custom domain in the api gateway, because they are limited to 120, and I expect to have more tenants than 120.
How can I make this routing?
Here is an illustration of a use case:
PS: Any advice is welcome.

You can setup a distribution with a wildcard (*.api.foo.com) set for the Alternate Domain Name (CNAMEs). If you attach a Lambda#Edge to the Origin Request (Under Cache Behavior settings), you can dynamically modify the host header to point to the appropriate API Gateway host (xxxxxx.execute-api.us-east-1.amazonaws.com).
AWS Blog where they did this, with S3 buckets for the origin.
It should translate fairly closely to APIGateway hostnames instead:
https://aws.amazon.com/blogs/networking-and-content-delivery/dynamically-route-viewer-requests-to-any-origin-using-lambdaedge/

Related

AWS using API Gateway and Cloud Front in the same domain with different path

I'm using the API Gateway v2 and Cloud Front on my system, and serverless framework (with compose) to manage everything.
How to expose both services in the same domain (to avoid preflight requests and a few other internal requirements) where each is accessible with a custom path?
Example:
https://foo.bar/app -> points to the Cloud Front application
https://foo.bar/api -> points to the API Gateway
https://foo.bar -> redirects to the Cloud Front in /app initially but later it will have its own SPA landing page.
Anything I can do? The only way we were able to configure this was by creating an edge lambda to handle requests and decide whether the CF or the API would be used but this solution seems to waste resources unnecessarily...
Thanks.
You could use the behavior and origin feature of cloudfront.
Have multiple origins for example S3 bucket and another api gateway.
Then based on behavior you can route to specific origin.
Like Default(*) behavior will point to S3.
/api/* behavior will point to api gateway.
https://kuchbhilearning.blogspot.com/2022/10/add-cloudfront-behavior-and-origin.html code.
Much detail explanation
https://kuchbhilearning.blogspot.com/2022/10/api-gateway-and-cloud-front-in-same.html

AWS API Gateway with custom domain and CORS

I have an AWS API Gateway API with CORS enabled and a custom domain set-up. Direct API access via the execute-api endpoints is working and I get no CORS issues in the browser. Access via the custom domain api.example.com which I also configured, is working in principle, but not in the browser, where I get CORS:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
When setting up the custom domain, API Gateway configures its own CloudFront distribution which is somewhat internal and I do not see how to configure it or if this is even possible, it does not show up in my list of CloudFront distributions, which is expected. From the AWS docs:
The CloudFront distribution created by API Gateway is owned by a
region-specific account affiliated with API Gateway
I found these answers https://stackoverflow.com/a/52435619/4556546 and https://stackoverflow.com/a/51463965/4556546 that describe how to solve the problem with a normal CloudFront distribution. I am, however wondering:
Have I missed something in configuring the custom domain and its
associated CloudFront distribution?
In what circumstances would I choose the custom domain with its internal CloudFront distribution over running my own CloudFront distribution
that I can configure? (I already have a CloudFront distribution that I use for other parts of my app.) Are there pricing implications?
Your suggestion to remove the stage or "/dev" from my new custom domain worked like a charm.
api.example.com/dev/some_endpoint Incorrect
api.example.com/some_endpoint Correct
Hopefully this answer can help anyone else trying to add a custom domaim to AWS API Gateway.
simply removing /dev from the end of my custom domain in my amplify config nailed it! I want to hide under the sheets everytime I see that cors error, in this case I came out on top, thanks!

Routing POST requests from CloudFront to Lambda

I have a website distributed with CloudFront, with S3 as an origin. I've written a Lambda function that takes a contact form submission and sends the email along with SES. The Lambda test out just fine : )
But, I'm clueless when it comes to routing POST requests from CloudFront to that backend Lambda function. How do I do this?
Update: Okay, I've got the API Gateway test triggering the Lambda function just fine, but I can't seem to call it from CloudFront (or rather using a curl command to my domain set up with CloudFront).
Do I need to list my domain as a custom domain in API Gateway?
If I list the path /api/* in my CloudFront Behaviors, do I have to mirror that in my API Gateway set up? So, does my API Gateway need to start with /api before I add specific resources?
Update 2 I think I needed to leave or remove the /dev off the end of the API Gateway URL. dev being my stage.
Update 3 Okay, it feels one step away now. I've got everything hooked up. The test request hits cloudfront, it forwards to api gateway, gateway calls lambda (at this point I'm shaking my head at how complicated we've made all this), and lambda sends back success or failure to api gateway, and we're peachy. Except, I get MethodNotAllowed when I do it from curl or the browser. Do I need to add an IAM role to CloudFront to access API Gateway?
Update 4 Still not working. And now, I'm back to getting my usual 404 error page that my Default Origin (S3). Seems like serverless is a fading dream.
Update 5 Trying a different approach, recommended here: https://serverfault.com/a/839368 The idea is to use API Gateway's Custom Domain name features with a subdomain like api.example.com and then use a Route53 Alias record to direct subdomain traffic to API Gateway. This could work. Then CloudFront would handle traffic to example.com and www.example.com, and API Gateway would get requests to api.example.com. Now the challenging bit is that in HTML forms the action attribute will have to go to a different subdomain. Let's see what kinds of errors and crazy behavior we get : (
First you would setup API Gateway in front of your Lambda function so it can be called via a POST request. It sounds like you may already have that part done?
Then if you want the POST to go through CloudFront you would add a second origin in CloudFront that points to your API Gateway.
This is all possible, but its tricky to configure. To help I created an open-source boilerplate app that correctly sets up:
A static site with CloudFront and S3
An API with API Gateway and Lambda
CORS between the static site and API
Optional OAuth 2.0 and JWT cookie for the static site
See this static JavaScript app for an example of a static site POSTing to an API backed by Lambda.
Depends on what you're using as your backend (which language, framework, etc.), there are different ways, but 'em all about one thing: Invoke
Kind-of the most generic call - HTTP is right there, the API call examples by language are referenced at the end of the doc.

AWS API Gateway Custom Domain + AWS CloudFront

I have an AWS API Gateway endpoint(Invoke URL),
I created a Custom Domain, to map the Domain with my API Gateway as the Invoke URL is made of non user friendly characters,
I mapped the Custom Domain with the API Gateway,
I followed these steps -
http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-custom-domains.html
Both the Default Invoke URL and Custom Domain endpoint are responding correct data,
So far so good.
On further testing I found out that as my default Invoke URL had Caching enabled on it,
I enabled API Gateway cache by following this -
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html
The response was in miliseconds,
Weirdly the Custom Domian mapped endpoint is responding slower and looks like it is not Caching the previous responses, even though Caching is properly enabled on the API Gateway,
I need to Enable Caching on the Custom Domian as well,
Do I need to add CloudFront in front of the API Gateway or something?
How do I achieve this?
I am not able to find my Invoke URL in CloudFront origin,
I couldn't understand these solutions either -
1. http://www.davekonopka.com/2016/api-gateway-domain.html
2. How do you add CloudFront in front of API Gateway

Deploying AWS Global infrastructure with API Gateway, Lambda, Cognito, S3, Dynamodb

Let say I need an API Gateway that is going to run Lambdas and I want to make the best globally distributed performing infrastructure. Also, I will use Cognito for authentication, Dynamodb, and S3 for user data and frontend statics.
My app is located at myapp.com
First the user get the static front end from the nearest location:
user ===> edge location at CloudFront <--- S3 at any region (with static front end)
After that we need to comunicate with API Gateway.
user ===> API Gateway ---> Lambda ---> S3 || Cognito || Dynamodb
API Gateway can be located in several regions, and even though is distributed with CloudFront, each endpoint is pointing to a Lambda located at a given region: Let say I deploy an API at eu-west-1. If a request is sent from USA, even if my API is on CloudFront, the Lambda it runs is located at eu-west-1, so latency will be high anyway.
To avoid that, I need to deploy another API at us-east-1 and all my Lambdas too. That API will be pointing to those Lambdas
If I deploy one API for every single region, I would need one endpoint for each one of them, and the frontend should decide which one to request. But how could we know which one is the nearest location?
The ideal scenario is a single global endpoint at api.myapp.com, which is going to go to the nearest API Gateway which runs the Lambdas located in that region too. Can I configure that using Route 53 latency routing with multiple A records pointing to each api gateway?
If this is not right way to do this, can you point me in the right direction?
AWS recently announced support for regional API endpoints using which you can achieve this.
Below is an AWS Blog which explains how to achieve this:
Building a Multi-region Serverless Application with Amazon API Gateway and AWS Lambda
Excerpt from the blog:
The default API endpoint type in API Gateway is the edge-optimized API
endpoint, which enables clients to access an API through an Amazon
CloudFront distribution. This typically improves connection time for
geographically diverse clients. By default, a custom domain name is
globally unique and the edge-optimized API endpoint would invoke a
Lambda function in a single region in the case of Lambda integration.
You can’t use this type of endpoint with a Route 53 active-active
setup and fail-over.
The new regional API endpoint in API Gateway moves the API endpoint
into the region and the custom domain name is unique per region. This
makes it possible to run a full copy of an API in each region and then
use Route 53 to use an active-active setup and failover.
Unfortunately, this is not currently possible. The primarily blocker here is CloudFront.
MikeD#AWS provides the info on their forums:
When you create a custom domain name it creates an associated CloudFront distribution for the domain name and CloudFront enforces global uniqueness on the domain name.
If a CloudFront distribution with the domain name already exists, then the CreateCloudFrontDistribution will fail and API Gateway will return an error without saving the domain name or allowing you to define it's associated API(s).
Thus, there is currently (Jun 29, 2016) no way to get API Gateway in multiple regions to handle the same domain name.
AWS has no update on providing the needful since confirming existence of an open feature request on July 4, 2016. AWS Form thread for updates
Checkout Lambda#Edge
Q: What is Lambda#Edge? Lambda#Edge allows you to run code across AWS
locations globally without provisioning or managing servers,
responding to end users at the lowest network latency. You just upload
your Node.js code to AWS Lambda and configure your function to be
triggered in response to Amazon CloudFront requests (i.e., when a
viewer request lands, when a request is forwarded to or received back
from the origin, and right before responding back to the end user).
The code is then ready to execute across AWS locations globally when a
request for content is received, and scales with the volume of
CloudFront requests globally. Learn more in our documentation.
Usecase, minimizing latency for globally distributed users
Q: When should I use Lambda#Edge? Lambda#Edge is optimized for latency
sensitive use cases where your end viewers are distributed globally.
Ideally, all the information you need to make a decision is available
at the CloudFront edge, within the function and the request. This
means that use cases where you are looking to make decisions on how to
serve content based on user characteristics (e.g., location, client
device, etc) can now be executed and served right from the edge in
Node.js-6.10 without having to be routed back to a centralized server.