Deploy to AWS Lambda?

Hey @sean, that is awesome!!

I did an experiment a while ago to embed the static files inside the server.js file, just to know if that is something we could do in the future: https://github.com/luisherranz/embed-files-in-node-server

But I really like your approach. I am not familiar with the Serverless framework to be honest but it looks really cool.

I guess the next step would be to add CloudFront in front of everything. Have you thought about that? Can it be done with the serverless.yaml file as well?

If so, maybe you can make everything immutable (cache for a year even the HTML) and use a plugin like C3 CloudFront Cache by @hideokamoto to invalidate the cache from your WordPress.

Let us know what you find out.

Thereā€™s an issue created (in the docs repository) to create a guide about this

2 Likes

Hey @luisherranz,

Yes, I have been able to cache everything behind CloudFront!

Here is my updated serverless.yml

service: example2020

excludeDevDependencies: true

plugins:
  - serverless-aws-static-file-handler
  - serverless-offline

custom:
  apiGateway:
    binaryMediaTypes:
      - "*/*"

provider:
  name: aws
  runtime: nodejs12.x

functions:
  app:
    handler: lambda.handler
    events:
      - http:
          path: /
          method: any
      - http:
          path: /{proxy+}
          method: any

  static:
    handler: lambda.static
    events:
      - http:
          path: /static/{proxy+}
          method: get

  favicon:
    handler: lambda.favicon
    events:
      - http:
          path: /favicon.ico
          method: get

resources:
  Resources:
    CloudFrontDistribution:
      Type: AWS::CloudFront::Distribution
      Properties:
        DistributionConfig:
          Comment: Example Site
          Aliases:
            - example.com.au
          ViewerCertificate:
            AcmCertificateArn: arn:aws:acm:us-east-1:100000000000:certificate/some-certificate-slug
            SslSupportMethod: sni-only
          DefaultCacheBehavior:
            TargetOriginId: MyFirstOrigin
            ViewerProtocolPolicy: "redirect-to-https"
            Compress: true
            MinTTL: 31536000
            DefaultTTL: 31536000
            ForwardedValues:
              QueryString: false
          Enabled: true
          HttpVersion: http2
          Origins:
            - Id: MyFirstOrigin
              DomainName:
                Fn::Join:
                  - "."
                  - - Ref: ApiGatewayRestApi
                    - execute-api.us-east-1.amazonaws.com
              OriginPath: /dev
              CustomOriginConfig:
                HTTPPort: 80
                HTTPSPort: 443
                OriginProtocolPolicy: https-only

And I also has to modify my lambda.js file to handle setting a Cache-Control header on my static resources:

const awsServerlessExpress = require("aws-serverless-express");
const app = require("./build/server.js").default;
const server = awsServerlessExpress.createServer(app);
const path = require("path");
const FileHandler = require("serverless-aws-static-file-handler");

// Handle routing to Frontity Server
exports.handler = (event, context) => {
  awsServerlessExpress.proxy(server, event, context);
};

// Handle returning all static files located in `/build/static/`
const StaticFilesPath = path.join(__dirname, "./build/static/");
const staticFileHandler = new FileHandler(StaticFilesPath);
exports.static = async (event, context) => {
  if (!event.path.startsWith("/static/")) {
    throw new Error(`[404] Invalid filepath for this resource: ${fname}`);
  }
  let response = await staticFileHandler.get(event, context);
  response.headers = {
    'Content-Type': response.headers["Content-Type"],
    'Cache-Control': 'max-age=31536000',
 }
 return response;
};

// Handle returning `favicon.ico` file in the project root
const RootFilesPath = path.join(__dirname);
const rootFileHandler = new FileHandler(RootFilesPath);
exports.favicon = async (event, context) => {
  return rootFileHandler.get(event, context);
}

Hope this helps!

2 Likes

Thatā€™s awesome :clap: :clap: Congratulations :slight_smile:

Are you caching the HTML requests as well?

I guess you could use a s-maxage=31536000 header to store the HTML in Cloudfront for as long as possible, and then clear the cache from WordPress each time you update/publish a post using this plugin: https://wordpress.org/plugins/c3-cloudfront-clear-cache/

This is an old thread, but I successfully deployed Frontity to AWS Lambda tonight. Wordpress is hosted on Amazon Lightsail. I used the aws-serverless-express as a wrapper and managed to get it working out of the box.

const awsServerlessExpress = require('aws-serverless-express');
// Note the .default. If you miss this your lambda functions will timeout.
const app = require('./build/server').default;

const server = awsServerlessExpress.createServer(app);

exports.handler = (event, context) => {
    awsServerlessExpress.proxy(server, event, context);
};

Your lambda function should contain your build/ folder, the node_modules/ for the aws-serverless-express package, and an index.js with the code mentioned above:

build/
node_modules/
index.js

I have a very basic proxy API Gateway which triggers the Lambda and the only problem is indeed the stage that API Gateway enforces. Every static asset is being served from the root (without stage path). API Gateway returns a Forbidden for any static asset you try to access.

We set up a custom domain quickly and got rid of the issue and I would recommend you do so as well.

I would love to use Serverless framework for this (like @sean) but unfortunately Iā€™m just a contractor for a customer who has an AWS account, so I canā€™t create access keys with high enough permissions. Serverless would help me set up API GW + Lambda and a custom domain without having to set them up via CLI\Console, but it requires so many permissions that I canā€™t use it here.

However, since we just need an API and Lambda, it takes you approximately 10 minutes to set it up via CLI or the console.

1 Like

Thanks for this info :arrow_double_up: :arrow_double_up:

Thatā€™s awesome @Rcls! Thanks for sharing :slightly_smiling_face:

Weā€™ll try to fix that problem once we add this to the core.

I love what you guys have done with Frontity. I am trying to deploy a very simple frontend serverless app that queries an established headless Wordpress API. It works like a charm in my development environment. However, It has been interesting trying all of the cryptic responses to this post while I attempt to configure it serverless. I think you all make a lot assumptions about the knowledge of the person trying to use your fantabulous new tool. Things that I have tried that have not worked include adding a lambda.js file to the project which includes all the variations of code discussed here (it never ends up in the build), and adding a variety of configurations to the serverless.yml, package.json (both of them) and a number of other suggested configurations I have tried and deleted after unsuccessful deploy. The serverless.yml which builds really nicely designed packages for ReactJS just canā€™t quite figure out what to do with Frontity. While I am no newbie to development, I am fairly new to the serverless movement and after several days of trying to decode what you all are talking about, I still donā€™t have anything that actually deploys in Lambda. Can anybody provide a descriptive 1-2-3 set of instructions for this deployment?

@sheila.franek yes, AWS lambdas are not as easy as other serverless services.

We have a Feature Discussion open for this and @cristianbote has already done an Implementation Proposal explaining the changes required in the framework to make it work: AWS Lambda support

If you are interested in this feature, and have some experience developing, would you like to help us with the implementation?

This is our Contribution Guide and if you have problems of course we can help :slightly_smiling_face: