How can I use AWS CloudFront and API Gateway side by side for the same domain?

Solution 1:

I run multiple web apps exactly with your proposed design, and I extracted gofaas, an educational Go and Lambda app, to share the techniques.

You need two separate domains, e.g. www.gofaas.net for S3 + CloudFront and api.gofaas.net for API Gateway + Lambda.

Then you can let your static site interact with the API with an API Gateway CORS configuration and some JavaScript:

fetch(`https://api.gofaas.net/work`, {
    method: "POST",
    mode: "cors",
    headers: {
        "Accept": "application/json",
        ...
    },
    body: JSON.stringify(...)
})
    .then(function(response) {
        return response.json();
    })
    .then(function (json) {
        // use response
    })
    .catch(function (err) {
        console.log("fetch error", err);
    });

Here are some guides for setting all this up:

Static Websites with S3, CloudFront and ACM

API Security with Lambda, API Gateway, CORS and JWT

Solution 2:

You can create a lambda function, setup API gateway, and then configure CloudFront to forward certain paths (e.g. /rest/*) to API gateway, and serve everything else from a S3 bucket.

Here is a complete walk through showing how to do this: https://www.codeengine.com/articles/process-form-aws-api-gateway-lambda/

Solution 3:

From the connection point of view "something" needs to answer your requests (GET, POST, PUT, everything). First of all you have a TCP connection and "something" needs to make sure it is understanding layer 7 and making sense out of the bytes the client is sending. Only at this point it is possible to handle GET requests differently than POST requests or one URL than another URL. So in the end you need a service which is capable of understanding and routing HTTP. The following services are capable of doing this: CloudFront ELB/ALB API Gateway (limitation comes later)

API Gateway uses CloudFront internally (without giving you the chance to actually configure anything on the CloudFront level) - that means there is no way to run CloudFront and API Gateway side-by-side as in the end this would mean you run CloudFront with CloudFront side-by-side.

CloudFront gives you the chance to select different origins based on patterns - but you can only select S3 or ELB/ALBs as an origin - not Lambda functions (besides the Lambda@Edge functionality).

ALB/ELB can only use EC2 instances as a backend - no Lambda or S3 here.

The only ways I can think of which might do what you want to do are these:

  • You use API Gateway and route a specific "asset"-path to a Lambda function which does kind of of a reverse proxy for S3 (so piping the static assets through lambda) - be aware of the costs for Lambda here!
  • You could do the same but instead of piping the asset through Lambda just generate an signed URL within Lambda an redirect directly to S3 for serving (might be more cost efficient)
  • Using different subdomains for your assets than the rest of your application - this is a very common pattern as you can easily split out on the DNS level and use different services for the different use cases (CloudFront for assets and API Gateway for the non-static parts)

So my call would be the last option - but that means you need to point the clients/browsers to a separate subdomain for all static assets (or for all POST requests).

It sounds like you want to have a look at technologies like AngularJS or React to build a truly API-driven application in the browser. With this approach you're running a real API which is handling all the "dynamic" requests with an API Gateway and delivering the application itself from S3 as an static asset. Maybe looking at those might help you to find your way - even if you don't use them, the architectural pattern on how to build things like this is what you're asking for imho.