(Read this article on the blog) Usually, a web application consists of a frontend and a backend. With an SPA, the frontend can be a React app, for example, and built with WebPack. Then the backend can be a Lambda function. The frontend URL is visible to users, so there is a strong push to […]
Usually, a web application consists of a frontend and a backend. With an SPA, the frontend can be a React app, for example, and built with WebPack. Then the
backend can be a Lambda function.
The frontend URL is visible to users, so there is a strong push to bring that to a user-friendly domain (
example.com), for example, using CloudFront. But the
API URL is hidden from users, only visible if they open the DevTools and inspect the outgoing requests. Because of this, it’s easier to use the generated domain
name for the backend.
CloudFrontS3API Gatewayclientexample.comsiteid.execute-api.amazonaws.comThe frontend and the API are on different domains
It works, but there are several problems with it.
The first is performance. When a visitor opens the site, the browser does a DNS resolution for the frontend domain. Then it establishes an HTTPS connection
(TCP + TLS or QUIC) to fetch the assets (html, css, js). When the page is parsed it sends a request to the API, starting the whole process one more time: a DNS
resolution to the API domain, then an HTTPS connection. This adds a noticable latency between showing the page and fetching the data.
Connection handshakes are needed for both the frontend and the APIclientCloudFrontAPI Gatewaydownload frontendDNS resolutionexample.comSYNSYN ACKClientHelloServerHelloFinished, HTTP requestHTTP responseAPI requestDNS resolutionid.execute-api.amazonaws.comSYNSYN ACKClientHelloServerHelloFinished, HTTP requestHTTP response
But the bigger problem is that this setup requires a more complicated configuration.
First, the frontend needs to know the API URL. When the frontend assets are loaded, it needs to send an HTTP request to the backend. The API URL is usually
hardcoded into the app when it is built. This adds one extra parameter that is different between different environments.
S3API Gatewayclient1.example.com2.API: id.execute-api.amazonaws.com3.id.execute-api.amazonaws.comThe frontend needs to know where the API is
Second, the backend needs to know the frontend URL. Since the frontend and the backend are on different domains, when the browser fetches data from the API it
sends a cross-origin request. To get access to the contents, the backend needs to respond with CORS headers, at least with the
This setup gets more complicated when the client needs to send credentials, such as a session cookie that identifies the user. In this case, the backend needs
to include the
Access-Control-Allow-Credentials header, in which case the
Access-Control-Allow-Origin must not be
*. Since allowing all
origins to send requests with credentials is a bad idea, the backend needs to validate that the request came from the frontend domain. In the case of multiple
environments, this adds an extra environment variable to the API configuration.
S3API Gatewayclient1.example.com2.API: id.execute-api.amazonaws.com3.id.execute-api.amazonaws.com4.ACAO: example.comThe API needs to know the frontend URL
The above architecture implements domain-based (or host-based) routing as different domains host the different parts. To use a single domain, you can use
/api=> send the request to the API
API GatewayS3CloudFrontclient/api/**The API and the frontend are on the same domain
With this setup, a single connection is enough to communicate with both the frontend and the backend. One DNS resolution and HTTP handshake is still needed, but
the API connection can reuse the existing one. The net result is that the page will be populated with data faster once it’s loaded.
Also, a single-domain setup makes the requests same-origin. The frontend no longer needs to know where the API is for that specific environment: it’s
/api. And on the backend side, since the requests are not cross-origin, no CORS headers are needed. The backend no longer needs to know where the frontend
There is one thing to look out for: the request path in the API requests. With domain-based routing, the API could expect that all requests are realtive to the
root path (
/). With path-based routing it’s no longer the case.
The solution is to use CloudFront Functions to change the request path.
Source: Advanced Web Machinery