9

I'm putting that static assets of my website on S3, and setting up CloudFront to distribute them. These essentially holds the content users would need for any GET request on my site, to existing paths that is, with a catchall for errors.

I also have some POST requests I need to handle. Form submissions, sending emails, notifications, interacting with the database.

How can I set up Lambda (or API Gateway) side by side with CloudFront for the same domain so that CloudFront handles GET requests, and API Gateway handles requests with a body or POST requests. Or can I do it by individual URL somehow?

Costa Michailidis
  • 225
  • 1
  • 2
  • 9

4 Answers4

6

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/

Grodriguez
  • 242
  • 1
  • 2
  • 15
2

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.

Osterjour
  • 825
  • 8
  • 12
2

I have the same setup. Static assets on S3, Lambda functions served through API gateway, and they share the same domain name.

I go with API gateway which already uses CloudFront and exposes some of its functionalities such as caching. Then I configure URIs which map to static assets. In API Gateway, a resource can be a Lambda function, an AWS function, a mock, or another URL. I have them point to my S3 URLs.

The URIs can be set to glob up subpaths as well e.g. /assets/*.

  • So the part that gives me trouble is deploying the API. It usually deploys without the leading path, in your case `/assets/*`. I have to delete the deployment, and right click on the `/assets/*` path and deploy from there. – Costa Michailidis Oct 18 '17 at 16:54
  • 1
    I should dig into the command line tools, and learn how to create and edit api's and lambda's from there. – Costa Michailidis Oct 18 '17 at 16:55
2

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

Noah Zoschke
  • 136
  • 1
  • Testing the site always becomes interesting here. It's tough to replicate AWS infrastructure locally so that you can do integration tests locally. I use a route instead of a subdomain. That helps part of the testing. Also eliminates CORS challenges. Then, API Gateway becomes an origin for CloudFront for that route. – Costa Michailidis Mar 30 '18 at 16:01