-
Notifications
You must be signed in to change notification settings - Fork 14
Infrastructure
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.
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.
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.
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
.