Deploy to AWS Lambda?

Hi @phoebus!

First of all, welcome to the community, itā€™s great to see that you have decided to take over this quest :blush:

Iā€™ve been taking a look to our roadmap (is not public yet, we are working to publish it in the next weeks) and support for AWS-Lambdas is scheduled. We are right now working on the roadmap in order to publish it during the next weeks, so it will be easier for everybody to see what are we working on.

Iā€™ve also found this thread Deploy to Netlify where they mention an approach to deploy on lambdas.

By the way, we would love to know what kind of project are you working on. Can you tell us a bit about what you are building?

1 Like

Thanks Iā€™ll check out the thread and anticipate the soon to come info. In the meanwhile if I figure out anything on my own Iā€™ll come back and post about it.

My project is a simple revamp of my personal blog, I always hated how Wordpress themes work and once I learned about headless CMS I knew that was the way to go. I also wanted to learn React after several years of not doing any development. The React components that the themes are built on seem to be very easy to adapt and understand for a first time React user so that went great. I found it was very easy to mix up my own components with the Mars and Twenty Nineteen themes to make it unique to my situation.

1 Like

That sounds great :blush:

Glad to hear this as well!

As soon as you have something to show, we would love to take see your project live! In addition, @Reyes is actively looking for showcases for the newsletter.

1 Like

@phoebus if you are still interested in testing AWS Lambdas let me know and Iā€™ll add the handler to our server.

Itā€™s something thatā€™s been on our roadmap for a while but we didnā€™t do it due to a lack of time. If you are willing to help us testing it out, Iā€™ll add the handler right away :slight_smile:

Hey guys, @ luisherranz really appreciate the good work. I have a leading news portal project in India for which Iā€™m looking for a similar solution for which I could deploy this solution on AWS Lambda. Currently Iā€™m just testing out building my new blog. I already have setup a headless wordpress cms on AWS. I have done some modifications with existing theme on Frontity framework and seems working fine running on localhost. I have created a build with server.js analyze and static folders. Now Iā€™m stuck on deploying to AWS lambda. I tried to import server.js as a lambda function and deploy it. Could you please help me out with this ?. kindly help with the steps.

Could you please try using this wrapper around the server.js file?

Something like this:

// lambda.js
const awsServerlessExpress = require("aws-serverless-express");
const app = require("./build/server.js").default;
const server = awsServerlessExpress.createServer(app);

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

Let me know if it works and if it does, weā€™ll add it Frontity itself so you donā€™t have to use a wrapper.

Thanks @luisherranz. I managed to deploy the app as lambda on aws as you mentioned above. It works fine. Now home page of my sample blog is loading fine. If I navigate to menus ( wordpress categories - /category/xyz) I get 'ā€œmessageā€:ā€œForbiddenā€}" as response from the lambda. This is happening because aws API gateway require ā€˜stageā€™ to be appended at the end of API endpoint so that /dev is appended at the end of api endpoint. It seems either i need to provide path without forward slash ā€˜/ā€™ within menu settings in frontity.settings.js. For users, map to a custom domain though. Is this correct ?. Thanks again. :slight_smile:

Hey, thatā€™s great. I am glad itā€™s working! :slightly_smiling_face:


Youā€™re right about that, the URL path is wrong. Could you please try this other library? https://github.com/dougmoscrop/serverless-http

It seems to support a basePath option, although in our case that would be "/" so I am not sure if that is going to work. Something like this:

// lambda.js
const serverless = require("serverless-http");
const app = require("./build/server.js").default;

module.exports.handler = serverless(app, {
  basePath: "/",
});

More info about the basePath option here: https://github.com/dougmoscrop/serverless-http/blob/master/docs/ADVANCED.md

Let me know what you find.

Nope. it doesnā€™t work for me. I receive an 500 internal server error. :frowning:

Does it work without the basePath?

Continuing the discussion from Deploy to AWS Lambda?:

Removing basePath still gives me internal server error. What i finally did is hosted my wordpress on a subdomain pointing to an ec2 instance and configured with free letā€™s encrypt certificate. And hosted the app itself on aws lambda using serverless express. Iā€™m new to serverless and configuring aws services in general. I created a subdomain certificate on amazon ( domain was bought through amazon ) and pointed to wordpress. It seems working fine for me now. I have few questions about performance and efficiency ( response times etc ) in using lambda for serving web frontend through Frontity nodejs server. I have generally seen using normal rest api services deloyed on lambda. Because as far is know lambda functions are active only when request is processed and doesnā€™t remain ā€˜openā€™ until request is received through API gateways. What about serving through amazon container services ? Could you please shed some :bulb: into this ?. Do you have some resources that i can go through ?. I apologize, too many questions i know. This thing really helped me learn something new :). Thanks :slightly_smiling_face:

@vimalkodoth Iā€™ve merged the topic you opened in another thread to keep the conversation in the same post

You should be able to continue the conversation. If you have any problems send us a message (moderators ā†’ @juanma or @mburridge) or mention us in your reply, and we will try to solve it

Thanks for moving this @juanma.


Iā€™m glad you solved it @vimalkodoth, although maybe itā€™s time we study this ourselves, include the AWS handler in Frontity, and recommend a specific way to set up Frontity with AWS lambdas. What do you think @juanma? Should we add it to our docs roadmap?


The performance of AWS lambdas is not critical because you should be caching the output anyway. If you want to cache it with AWS, you can configure CloudFront. If not, you can use an external service like Fastly or KeyCDN. More info about this here: Moving an Existing WordPress website with over 5000 Articles

@luisherranz Interested in deploying to AWS-Lambda. Is there any official Frontity - AWS integration Template coming on the roadmap? Will keep on eye this threadā€¦

Hi @dejangeorgiev

Regarding this, we have created an issue to have a look at it and try to provide a better explanation on this ā†’ https://github.com/frontity/docs/issues/145

Feel free to comment on the issue to add any specific information you missed from the docs so we can take it into account when we work on this.

Thanks a lot for your feedback

1 Like

Yeah, weā€™d love to add official support :slightly_smiling_face:

The main blocker is that AWS lambdas contain a ā€œstage pathā€ in the URL, for example /dev or /latest, and itā€™s not consistent when you use a custom/non-custom domain.

https://ov8mwmh2xa.execute-api.us-east-1.amazonaws.com/latest

There doesnā€™t seem to be an standard way to get rid of it, but Iā€™ve been investigating and using event.pathParameters.proxy seems to be one of the best ways, so Iā€™d like to try that first.

This scaffold is a bit old, but implements the replacement of pathParameters.proxy:

module.exports = (ctx, next) => {
  ctx.lambdaEvent =
    (ctx.headers["x-apigateway-event"] &&
      JSON.parse(decodeURIComponent(ctx.headers["x-apigateway-event"]))) ||
    {};
  ctx.lambdaContext =
    (ctx.headers["x-apigateway-context"] &&
      JSON.parse(decodeURIComponent(ctx.headers["x-apigateway-context"]))) ||
    {};
  ctx.env = (ctx.lambdaEvent && ctx.lambdaEvent.stageVariables) || process.env;

  // Workaround an inconsistency in APIG. For custom domains, it puts the
  // mapping prefix on the url, but non-custom domain requests do not. Fix it by
  // changing the path to the proxy param which has the correct value always.
  if (ctx.lambdaEvent.pathParameters && ctx.lambdaEvent.pathParameters.proxy) {
    const dummyBase = "zz://zz";
    const url = new URL(ctx.url, dummyBase);
    url.pathname = "/" + ctx.lambdaEvent.pathParameters.proxy;
    ctx.url = url.href.replace(dummyBase, "");
  }
  return next();
};

Apart from that, any ā€œKoa/Express to Lambdaā€ library should do the trick. That scaffold is using aws-serverless-express which seems to be one of the most popular ones, so I guess we can try with that.

Frontity uses Koa, not Express, but exports a req/res function by using app.callback() so thereā€™s not much difference.

This is the code of the scaffold:

const awsServerlessExpress = require("aws-serverless-express");
const app = require("./app");
app.proxy = true;
const server = awsServerlessExpress.createServer(app.callback());
exports.handler = (event, context) =>
  awsServerlessExpress.proxy(server, event, context);

In Frontity, the server.js file has already the app.callback(), so itā€™d be something like this:

const awsServerlessExpress = require("aws-serverless-express");
const frontity = require("./build/server.js").default;
frontity.proxy = true;
const server = awsServerlessExpress.createServer(frontity);
exports.handler = (event, context) =>
  awsServerlessExpress.proxy(server, event, context);

If you want to give it a try let me know what you find out! :slightly_smiling_face:

1 Like

Hi @luisherranz

Thank you so much for the detailed explanation.

Definitely i will give a try at some later stage and let you know my experiences with it.

I also think about to move the backend part ā€œWordPressā€ on AWS and cache the APIā€™s with Cloudfront. There are some great templates, developed by AWS. Check it out here https://github.com/aws-samples/aws-refarch-wordpress

Not sure about the estimated pricing tough, will discuss this with AWS Account Manager.

Thank you so much.
Best,
Dejan

Hi @luisherranz,

Thank you for all of your help with this issue thus far.

In my case, I was able to use serverless to set up a Lambda with an Application Gateway and a custom domain name that all routes to the root of my domain (https://example.com.au/). However, from there I have been unable to get any files served from the static directory to serve alongside my server.js file. This includes the important *.module.js files that should be requested as the page loads in the browser.

Do you have any recommendations that I can try to understand why these additional files will not load alongside my main request to server.js? Does server.js not handle the routing and requesting of files from the /static/ directory?

My lambda.js file looks like this:

// lambda.js
const awsServerlessExpress = require("aws-serverless-express");
const app = require("./build/server.js").default;
const server = awsServerlessExpress.createServer(app);

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

And the important parts of my serverless.yml look like this:

// serverless.yml
service: example

excludeDevDependencies: true

plugins:
  - serverless-domain-manager

custom:
  customDomain:
    domainName: example.com.au
    stage: ${self:provider.stage}
    createRoute53Record: true
    certificateName: "example.com.au"

provider:
  name: aws
  runtime: nodejs12.x

functions:
  app:
    handler: lambda.handler
    events:
      - http: ANY {proxy+}

Thanks,
Sean

Hey @luisherranz,

I was actually able to figure this out. I was able to use https://github.com/activescott/serverless-aws-static-file-handler to handle serving all my static files correctly. Iā€™ve updated my lambda.js as follows:

// lambda.js
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}`);
  }
  return staticFileHandler.get(event, context);
};

// 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);
}

And Iā€™ve also updated my serverless.yml to the following:

// serverless.yml
service: example

excludeDevDependencies: true

plugins:
  - serverless-domain-manager
  - serverless-aws-static-file-handler

custom:
  customDomain:
    domainName: example.com.au
    stage: ${self:provider.stage}
    createRoute53Record: true
    certificateName: "example.com.au"
 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

Hopefully this helps anyone else looking to host their Frontity site on AWS Lambda!

Cheers,
Sean

3 Likes

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.