Skip to content

Commit 8da7252

Browse files
initial IaC example with SST
1 parent 49bb4eb commit 8da7252

File tree

1 file changed

+134
-15
lines changed
  • src/pages/guides/hosting

1 file changed

+134
-15
lines changed

src/pages/guides/hosting/aws.md

Lines changed: 134 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,24 @@ tocHeading: 2
88

99
# AWS
1010

11-
Greenwood can be automatically deployed to [**AWS**](https://aws.amazon.com/) for static hosting using [**S3**](https://aws.amazon.com/s3/) and [**Cloudfront**](https://aws.amazon.com/cloudfront/) with GitHub Actions.
11+
Greenwood projects can be deployed to [**AWS**](https://aws.amazon.com/) for static hosting ([**S3**](https://aws.amazon.com/s3/) / [**CloudFront**](https://aws.amazon.com/cloudfront/)) and dynamic serverless hosting of SSR pages and API routes (on [**Lambda**](https://aws.amazon.com/lambda/)). Although static hosting is fairly trivial, for full-stack applications and when leveraging additional AWS services to compliment your application, we recommend leveraging [IaC (Infrastructure as Code)](https://en.wikipedia.org/wiki/Infrastructure_as_code) tools, as we will demonstrate later in this guide.
1212

1313
> You can see a complete hybrid project example in our [demonstration repo](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws).
1414
1515
## Static Hosting
1616

17-
In this section, we'll share the steps for up S3 and Cloudfront together for static web hosting.
17+
If you only need static hosting (SSG, SPA), then you may benefit from just a little manual configuration to set up an S3 bucket and CloudFront distribution for your project.
1818

19-
1. Configure S3 by following [these steps](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html)
20-
1. Once you have followed those steps, run `greenwood build` in your project and upload the contents of the _public/_ directory to the bucket
21-
1. Finally, setup Cloudfront to use this bucket as an origin by [following these steps](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html#GettingStartedCreateDistribution):
22-
23-
> Keep an eye out for prompts from AWS to enable IAM rules for your function and make sure to invalidate the Cloudfront distribution between tests, since error pages / responses will get cached.
19+
1. Configure S3 by following [this guide](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html)
20+
1. Once you have followed those steps, run `greenwood build` in your project and upload the contents of the _public/_ directory to the bucket.
21+
1. Finally, setup CloudFront to use the bucket as an origin by following [these steps](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.SimpleDistribution.html#GettingStartedCreateDistribution)
2422

2523
You should now be able to access your site at _http://{your-dist}.cloudfront.net/_! 🏆
2624

2725
Now at this point, if you have any routes like `/search/`, you'll notice they are not working unless _index.html_ is appended to the path. To enable routing (URL rewriting) for cleaner URLs, follow the _Configure Trigger_ section of [this guide](https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/) to attach the Lambda as a [**Lambda@Edge**](https://aws.amazon.com/lambda/edge/) function that will run on every incoming request.
2826

27+
> Keep an eye out for prompts from AWS to enable IAM rules for your function and make sure to invalidate the CloudFront distribution between tests, since error pages / responses will get cached.
28+
2929
Below is a sample Edge function for doing the rewrites:
3030

3131
<!-- prettier-ignore-start -->
@@ -37,7 +37,7 @@ Below is a sample Edge function for doing the rewrites:
3737
const { request } = event.Records[0].cf;
3838

3939
// re-write "clean" URLs to have index.html appended
40-
// to support routing for Cloudfront <> S3
40+
// to support routing for CloudFront <> S3
4141
if (request.uri.endsWith("/")) {
4242
request.uri = `${request.uri}index.html`;
4343
}
@@ -50,21 +50,140 @@ Below is a sample Edge function for doing the rewrites:
5050

5151
<!-- prettier-ignore-end -->
5252

53-
> At this point, you'll probably want to use Route 53 to [put your domain in front of your Cloudfront distribution](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html).
53+
> At this point, you'll probably want to use Route 53 to [put your domain in front of your CloudFront distribution](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html).
5454
5555
## Serverless
5656

57-
Coming soon!
57+
If your Greenwood project has SSR pages and / or API routes that you would like to deploy to AWS Lambda functions, our recommendation is to install [our adapter plugin](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-adapter-aws) and then add it to your _greenwood.config.js_, which at build time will generate Lambda compatible function code for all your dynamic pages and routes.
58+
59+
<!-- prettier-ignore-start -->
60+
61+
<app-ctc-block variant="snippet" heading="greenwood.config.js">
62+
63+
```js
64+
import { greenwoodPluginAdapterAws } from "@greenwood/plugin-adapter-aws";
65+
66+
export default {
67+
plugins: [greenwoodPluginAdapterAws()],
68+
};
69+
```
70+
71+
</app-ctc-block>
72+
73+
<!-- prettier-ignore-end -->
74+
75+
### Adapter Output
76+
77+
Just like Greenwood has its own [standard build output](/docs/reference/appendix/#build-output), this plugin will also generate its own standard adapter output tailored for Lambda, so as to provide a consistent starting point to integrate with your preferred deployment tool of choice.
78+
79+
The adapted functions will be output to a folder called _.aws-output_ with the following two folders:
80+
81+
- `api/` - All API routes will be in this folder, with one folder per route
82+
- `routes/` - All SSR pages will be in this folder, with one folder per route
83+
84+
Here is an example from a directory listing perspective of what the structure of this folder looks like:
85+
86+
```shell
87+
.aws-output/
88+
api/
89+
event/
90+
event.js
91+
index.js
92+
package.json
93+
search/
94+
...
95+
routes/
96+
admin/
97+
...
98+
products/
99+
index.js
100+
package.json
101+
products.route.chunk.jgsTuvlz.js
102+
products.route.js
103+
```
104+
105+
For **_each_** of the folders in the `api` or `routes` directories, it would be as simple as just creating a zip file for each folder / route, or just pointing your IaC tooling to those output folders, as we'll get into in the next section.
106+
107+
### SST (IaC) Example
108+
109+
Given the nature of AWS hosting and the plethora of related services that you can use to compliment your application, the Greenwood AWS adapter is specifically designed to output purely compatible Lambda functions, one per folder, that can be plugging into any IaC tool. (or zipped up and deployed manually, if you prefer)
110+
111+
While there are many options for IaC tooling, [**SST**](https://sst.dev/) is a very powerful tool which let's you entirely define your AWS infrastructure with TypeScript, combining as few or as many AWS service as you may need. Below is a simple
112+
113+
<!-- prettier-ignore-start -->
114+
115+
<app-ctc-block variant="snippet" heading="sst.config.ts">
116+
117+
```ts
118+
// 1) Configure SSR pages and API routes in API Gateway
119+
const api = new sst.aws.ApiGatewayV2("MyApi");
120+
121+
// products page
122+
api.route(`GET /routes/products`, {
123+
bundle: `.aws-output/routes/products`,
124+
handler: "index.handler",
125+
});
126+
127+
// search API
128+
api.route(`GET /api/search`, {
129+
bundle: `.aws-output/api/search`,
130+
handler: "index.handler",
131+
})
132+
133+
// 2) Setup hosting for static content
134+
const frontend = new sst.aws.StaticSite("MyStaticSite", {
135+
path: "./",
136+
build: {
137+
output: "public"
138+
},
139+
})
140+
141+
// 3) Configure CloudFront router with SSR pages, API routes, and static content
142+
const router = new sst.aws.Router("MyRouter", {
143+
routes: {
144+
"/api/*": api.url,
145+
{
146+
url: api.url,
147+
rewrite: {
148+
regex: `^/products/$`,
149+
to: `/routes/products`
150+
}
151+
},
152+
"/*": frontend.url
153+
},
154+
invalidation: true,
155+
});
156+
157+
// 4) Configure SST app
158+
export default $config({
159+
app(input) {
160+
return {
161+
name: "my-app",
162+
removal: input?.stage === "production" ? "retain" : "remove",
163+
protect: ["production"].includes(input?.stage),
164+
home: "aws",
165+
};
166+
},
167+
async run() {
168+
router
169+
},
170+
});
171+
```
172+
173+
</app-ctc-block>
174+
175+
<!-- prettier-ignore-end -->
176+
177+
Although the above example is hardcoded, you'll want use the build output manifest from Greenwood by following the [complete example repo we have](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws) for deploying a full-stack Greenwood application.
58178

59-
> There is no adapter plugin yet for serverless hosting, though it is on [our roadmap](https://github.com/ProjectEvergreen/greenwood/issues/1142).
179+
> We also have an [**Architect**](https://arc.codes/) example [for reference](https://github.com/ProjectEvergreen/greenwood-demo-adapter-aws/tree/feature/arc-adapter) as well.
60180
61181
## GitHub Actions
62182

63-
If you're using GitHub, you can use GitHub Actions to automate the pushing of build files on commits to a GitHub repo. This action will also invalidate your Cloudfront cache on each publish.
183+
If you're using GitHub, you can use GitHub Actions to automate the pushing of build files on commits to a GitHub repo. This can help automate the uploading of your static assets, or in the case of IaC, running your preferred IaC tool to deploy your application for you.
64184

65-
1. In your AWS account, create and / or add an AWS Secret Access Key (`AWS_SECRET_ACCESS_KEY`) and Access Key ID (`AWS_SECRET_ACCESS_KEY_ID`) and add them to your repository as [GitHub secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions).
66-
1. We also recommend adding your bucket name as secret too, e.g. `AWS_BUCKET_NAME`
67-
1. At the root of your repo add a GitHub Action called _.github/workflows/publish.yml_ and adapt as needed for your own branch, build commands, and package manager.
185+
1. In your AWS account, create (or use) an AWS Secret Access Key (`AWS_SECRET_ACCESS_KEY`) and Access Key ID (`AWS_SECRET_ACCESS_KEY_ID`) and add them to your repository as [GitHub secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions).
186+
1. At the root of your repo add a GitHub Action called _.github/workflows/publish.yml_ and adapt as needed for your own branch, build commands, package manager, and tooling.
68187

69188
<!-- prettier-ignore-start -->
70189

0 commit comments

Comments
 (0)