Hi
My app is divided into two projects: a backend (lambdas) and frontend SPA (static files on S3), and I would like to use API Gateway as a proxy for S3.
Follow the documentation, would a SAM template like this:
# https://github.com/awslabs/serverless-application-model/blob/master/HOWTO.md#packing-artifacts
MyApi:
Type: AWS::Serverless::Api
Properties:
DefinitionUri: s3://<mybucket>/<my-openapi-file-path>
With a swagger like Swagger Definitions of the Sample API as an Amazon S3 Proxy
, be the way to go?
So far, I don't see a clear support on SAM for static files (frontend). Is it on the roadmap or isn't SAM meant for that?
wouldn't enabling static website hosting for your s3 bucket work for you?
If your use-case is more complicated what do you expect to achieve by proxying through gateway?
@quarryman I can't set a custom domain and SSL only using S3, I would need CloudFront or API Gateway, unless I'm missing something.
API Gateway seems more flexible than CloudFront, I can make /path/2/ to return path/index.html instead of trying /path/2/index.html.
Also on S3, I didn't find a way to define a S3 resource with SAM, I would need to write a script or CF outside of the SAM scope.
@phstc You can intermix Vanilla CloudFormation with SAM resources. You can define an S3 bucket and other CloudFormation Resources without any issues.
Is there a SAM way of doing this?
I also would love to see a pure SAM approach to this; without a single line of Cloudformation.
While it would be nice to have a 'SAM-native' way of doing this, until that time, this is the CloudFormation that works for me:
Frontend:
Type: AWS::S3::Bucket
Properties:
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: index.html # Note: This is required for handling direct links to paths such as /foo/bar in a React single page app
LoggingConfiguration:
DestinationBucketName: !Ref Logs
LogFilePrefix: "logs-frontend-s3/logs-frontend-s3"
S3FrontendBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref Frontend
PolicyDocument:
Statement:
- Effect: "Allow"
Principal: '*'
Action:
- "s3:GetObject"
Resource: !Sub "${Frontend.Arn}/*"
And then adding CloudFront in front of it all (which becomes quite a bit more verbose..):
CloudFront:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: Frontend
Enabled: true
Origins:
- Id: FrontendS3Origin
DomainName: !GetAtt Frontend.DomainName
S3OriginConfig:
OriginAccessIdentity: ""
# OriginAccessIdentity: !Sub "${FrontendOriginAccessIdentity}" # TODO: This lets us lock s3 to only be accessible through here.. empty=disabled: https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_S3Origin.html
Aliases:
- !Ref FrontendDomain
DefaultRootObject: index.html
CustomErrorResponses:
# Note: This is required for handling direct links to paths such as /foo/bar in a React single page app
- ErrorCode: 403
ResponseCode: 200
ResponsePagePath: "/index.html"
ErrorCachingMinTTL: 5
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: "/index.html"
ErrorCachingMinTTL: 5
DefaultCacheBehavior:
TargetOriginId: FrontendS3Origin
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
DefaultTTL: 5
MinTTL: 0
MaxTTL: 5
ForwardedValues:
QueryString: false
Compress: true
HttpVersion: http2
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-viewercertificate.html
ViewerCertificate:
CloudFrontDefaultCertificate: true
# AcmCertificateArn: !Ref Certificate # TODO: This needs to be in us-east-1 for it to work with CloudFront..
# SslSupportMethod: sni-only
Logging:
Bucket: !GetAtt Logs.DomainName
Prefix: logs-cloudfront-s3
IncludeCookies: false
Thanks @0xdevalias for providing an example. Closing this for now as using CloudFormation in your SAM template (which is totally valid and common) is the way to do this. We are considering adding some SAM magic for SPAs.
Just one question , how will the static file be uploaded into the s3 bucket ? Or do we need to do that step manually?
@turjachaudhuri For my workflow, seperately and mostly manual. I have a step in my Makefile.
Makefile
deploy-frontend:
./deploy-frontend.sh $(APP)
deploy-frontend.sh
#!/bin/sh
set -e
FRONTENDBUCKET=`./getOutputData.sh ${1} FrontendBucket`
echo FRONTENDBUCKET=${FRONTENDBUCKET}
cd ../frontend/build
aws s3 sync . "s3://${FRONTENDBUCKET}" --exclude '.*' --exclude '*/.*' --exclude '*.map' --delete
getOutputData.sh
#!/bin/sh
set -e
JQOUTPUTMAP='.Stacks | .[].Outputs | [.[] | {(.OutputKey): .OutputValue}] | reduce .[] as $item ({}; . + $item)'
JQGETKEY="| .${2} // \"N/A\""
aws cloudformation describe-stacks --stack-name ${1} | jq -e --raw-output "${JQOUTPUTMAP}${JQGETKEY}"
Most helpful comment
Thanks @0xdevalias for providing an example. Closing this for now as using CloudFormation in your SAM template (which is totally valid and common) is the way to do this. We are considering adding some SAM magic for SPAs.