Nope. it doesnāt work for me. I receive an 500 internal server error.
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 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
@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ā¦
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
Yeah, weād love to add official support
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!
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
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
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!
Thatās awesome Congratulations
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.
Thanks for this info
Thatās awesome @Rcls! Thanks for sharing
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