question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Serverless Elder rendering

See original GitHub issue

So, 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 instance const srcFolder = path.join(process.cwd(), settings.locations.srcFolder); would become const srcFolder = path.join(settings.rootDir, settings.srcDir). rootDir could still default to process.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:closed
  • Created 3 years ago
  • Reactions:2
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
decrekcommented, Oct 5, 2020

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.

  • Custom Elder config: I need a different elder config during build and during serverless rendering since paths differ. But more importantly, cosmiconfig will not find the elder config during serverless rendering since it has to look up in the folder structure and down in some other folder. The first change I made was the ability to pass the Elder config to the Elder constructor, thus bypassing consmiconfig looking up the Elder config. See commit.

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 NuxtJS rootDir 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 of process.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?

0reactions
nickreesecommented, Oct 5, 2020

@decrek Great work, this is exciting.

  1. Definitely open to accepting a config as shown here. If you want to push a PR for just that, that would be great. 👍
  2. Yep, this is exactly by design. Great usage.
  3. Why not await the removal of the directory? I’d do this as defined below.

Disabling writing of only specific pages.

  1. Disable the hook that writes the html (elderWriteHtmlFileToPublic) in your elder.config.js.
  2. Add ssrOnly to the request objects.
  3. Re-implement the elderWriteHtmlFileToPublic hook excluding any requests with ssrOnly.
Read more comments on GitHub >

github_iconTop Results From Across the Web

Serverless Side Rendering — The Ultimate Guide - Webiny
Serverless Side Rendering. Here at Webiny, we're on a mission to create a platform that empowers developers to build serverless apps.
Read more >
Elder.js, the new kid on the block
Hacky serverless rendering: Elder is primarily designed as static site generator and works as a Node.js server in development.
Read more >
React.js on AWS Lambda in 15 minutes or Less
We're creating a Universal App (aka an isomorphic JavaScript App) with server-side rendering with a dynamic configuration context passed ...
Read more >
Elder.js: A Svelte Framework and Static Site Generator
Elder.js is an opinionated Svelte framework and static site generator used for ... This is a great option for serverless rendering. none no...
Read more >
Serverless Server-side Rendering with Lambda Edge ...
You'll see in my function that I refer to a concept called TRIGGERS to account for the fact that we are using an...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found