Serverless Elder rendering
See original GitHub issueSo, we know Elder is designed to generate static websites 💯 . However my colleague @decrek and I think Elder could be used very well for partially static and partially dynamic websites. You’ve already proven Elder can be used as a server in Elderjs/template > src/server.js. So we want to take it one step further and make it serverless.
@decrek has already done some work on this. Dynamic serverless Elder rendering in a lambda function would look something like this:
const renderPage = require('../lib/render-elder-page.js');
exports.handler = async (event) => {
const user = await getUserFromDatabase(event);
const html = await renderPage({
permalink: '/account/',
data: { user },
});
return {
statusCode: 200,
headers: { 'Content-Type': 'text/html' },
body: html,
}
}
with lib/render-elder-page.js
doing the actual rendering:
const { Elder, Page } = require('@elderjs/elderjs');
const elder = new Elder({ context: 'server' });
module.exports = async function renderPage({ permalink, data = {} }) {
await elder.bootstrap();
const request = elder.serverLookupObject[permalink];
const route = elder.routes[request.route];
const hooks = [
...elder.hooks,
{
hook: 'data',
name: 'addDynamicData',
description: 'Adds dynamic data to data object',
priority: 50,
run: (opts) => ({ ...opts.data, ...data }),
}
];
const page = new Page({ ...elder, hooks, request, route });
return await page.html();
};
The setup is working locally, but we’re still having some issues on Vercel and Netlify. The hardest part in the serverless context is reaching all the relevant source and generated files. The automagic behaviour, mainly inside getConfig
is making this a bit difficult. The other bit is the use of process.cwd()
throughout the code base. To make Elder more flexible to use, 2 things would help a lot:
- add and use a
settings.rootDir
throughout the code base. So for instanceconst srcFolder = path.join(process.cwd(), settings.locations.srcFolder);
would becomeconst srcFolder = path.join(settings.rootDir, settings.srcDir)
.rootDir
could still default toprocess.cwd()
. See #27. - allow passing the entire config to
new Elder(config)
to avoid issues caused by automagic behaviour like cosmiconfig. The automagic behavior can still be used as a default for when no config is passed.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:7 (4 by maintainers)
Hi @nickreese! ! First of all, great work on ElderJS!
I have an update on this issue. I managed to get serverless rendering working with Netlify functions and I wanted to share my example. I had to fork Elder to make some small changes in order to get it working so I want to propose some changes to Elder as well.
Next to that, I can not rely on process.cwd() for the root of the project. In order to fix I’ve added a property to the elder config, which I called
rootDir
, based on NuxtJSrootDir
property. See commit. But I looks like you already added that option?These 2 changes give me more flexibility over configuring Elder in different environments. As you can see these 2 changes overwrite the default behaviour. Meaning passing the config to the constructor is completely optional, falling back to cosmiconfig doing the same thing as before the change. Besides that, not setting a
rootDir
will result in the default value ofprocess.cwd()
.Extending data: As @jbmoelker shared already with the hooks system it is perfectly possible to add data using hooks. Note, that in the simplified example I don’t use this. But you can see the idea here;
Removing SSR routes from static build: Netlify matches 404’s to serverless function so I need to delete the statically generated HTML file. I did this using a afterbuild hook removing routes that have a ssrOnly property in the route object. This works fine, except for the fact that the file is not present in the afterBuild hook, when adding a setTimeout it worked: See here the hook.
I think the possibility to do serverless rendering using Elder is pretty awesome! What do you think about the whole approach and how do you feel about passing the configuration to the Elder constructor?
@decrek Great work, this is exciting.
await
the removal of the directory? I’d do this as defined below.Disabling writing of only specific pages.
elderWriteHtmlFileToPublic
) in yourelder.config.js
.ssrOnly
to therequest
objects.elderWriteHtmlFileToPublic
hook excluding any requests withssrOnly
.