Skip to content

Infrastructure

Jacob Sommer edited this page Dec 9, 2024 · 5 revisions

We use SST to deploy to AWS. SST is a framework that allows defining infrastructure as code and abstracts a lot of the complexity. The code/configuration for this is defined in sst.config.ts.

We have 2 main aspects of the app that need to be deployed, the frontend and the backend.

Frontend

The frontend is served to users via Cloudfront. Cloudfront is a CDN which is basically a network of servers around the world that will cache content, in this case our site, for quick delivery to users. Cloudfront doesn't store the site files directly, it just caches/delivers them. The site build files are stored in S3, a storage service from AWS. We use the sst.aws.StaticSite SST component which will automatically create and configure these services.

Backend

The backend is run on AWS Lambda. Lambda is a serverless compute service. Our backend is built with Express (and tRPC which is another layer on top of it). When running locally, we just run the Express server. For deploying to lambda, we use a serverless Express wrapper that exports the entire Express app as a single handler function for lambda. We use the sst.aws.Function SST component which will automatically create and configure a lambda function.

Routing API Requests

The StaticSite will be automatically configured so that the specified domain (e.g. peterportal.org for prod or staging-###.peterportal.org for sgaging) points to the CloudFront distribution. Setting it up so that peterportal.org/api/* routes to the lambda requires additional configuration.

In sst.config.ts, we configure the lambda function to have a public URL. Then we create a CloudFront origin for this lambda function within the CloudFront distribution created by the StaticSite component. Origins define where content can originate from in CloudFront. For example, an origin for the site files in S3 is automatically created by SST for us.

Next, we create a new behavior on the CloudFront distribution. Behaviors map to certain path patterns and tell the distribution which origin to route to based on the path pattern and how it should be have. A behavior for the S3 origin with the path pattern /* is automatically created and configured for us by SST. We create this additional behavior with the path pattern /api/* that points to the lambda origin. The behavior is configured appropriately to not cache any requests so that the latest data from the API is always returned

Lastly, there are two quirks that need to be resolved within the CloudFront distribution for the backend to function properly. Query strings in the request url containing a / need to be URL encoded since CloudFront can't handle them, and the Host header needs to be forwarded since the lambda function overwrites the host header (which is normally peterportal.org or staging-###.peterportal.org) with its URL, and part of our backend relies on the host header. To resolve these, we set up a CloudFront function on the lambda behavior that runs on each request and rewrites the affected query strings to be encoded and copies the Host header to x-forwarded-host. x-forwarded-host is copied back into the Host header in our Express app within a middleware in app.ts.

Diagram Overview

aws diagram

Clone this wiki locally