A demonstration repo for deploying a full-stack Greenwood app with Netlify static hosting and Serverless + Edge functions.
To run locally
- Clone the repo
- Run
npm ci
You can now run these npm scripts
npm run dev
- Start the demo with Greenwood local dev servernpm run serve
- Start the demo with a production Greenwood buildnpm run serve:netlify
- Start the Netlify CLI server for testing production Greenwood builds locally (see caveats section in the plugin's README)
👉 Note: If deploying to your own Netlify instance, make sure you set the
AWS_LAMBDA_JS_RUNTIME
environment variable in your Netlify UI to the value ofnodejs18.x
.
This repo aims to demonstrate a couple of Greenwood's features (API Routes and SSR pages) leveraging Netlify's serverless and edge function capabilities, focused on using Web Components (WCC) and Web Standards to deliver the content for the demo.
Feature | Greenwood | Serverless | Edge |
---|---|---|---|
API Routes | ✅ | ✅ | ❓ |
SSR Pages | ✅ | ✅ | ❓ |
The serverless demos include the following examples:
- ✅
/api/greeting?name{xxx}
- An API that returns a JSON response and optionally uses thename
query param for customization. Otherwise returns a default message. - ✅
/api/fragment
- An API for returning fragments of server rendered Web Components as HTML, that are then appended to the DOM. The same card component used in SSR also runs on the client to provide interactivity, like event handling.
- ✅
/products/
- SSR page for rendering Greenwood pages.
All known issue resolved! Information left here for posterity
Note: Solved by bypassing Netlify's bundling and just creating a zip file custom build output
Seeing this issue when creating an "idiomatic" example of a custom element using WCC's renderFromHTML
because Netlify / esbuild does not support import.meta.url
, though hopefully it is coming soon? 🥺
import { renderFromHTML } from 'wc-compiler';
import { getArtists } from '../services/artists.js';
export async function handler(request) {
const params = new URLSearchParams(request.url.slice(request.url.indexOf('?')));
const offset = params.has('offset') ? parseInt(params.get('offset'), 10) : null;
const headers = new Headers();
const artists = await getArtists(offset);
const { html } = await renderFromHTML(`
${
artists.map((item, idx) => {
const { name, imageUrl } = item;
return `
<app-card
title="${offset + idx + 1}) ${name}"
thumbnail="${imageUrl}"
></app-card>
`;
}).join('')
}
`, [
new URL('../components/card.js', import.meta.url)
]);
headers.append('Content-Type', 'text/html');
return new Response(html, {
headers
});
}
The above would be the ideal implementation, so instead have to do something more "manual" for now.
import '../../node_modules/wc-compiler/src/dom-shim.js';
import { getArtists } from '../services/artists.js';
import Card from '../components/card.manual.js';
export async function handler(request) {
const params = new URLSearchParams(request.url.slice(request.url.indexOf('?')));
const offset = params.has('offset') ? parseInt(params.get('offset'), 10) : null;
const headers = new Headers();
const artists = await getArtists(offset);
const card = new Card();
card.connectedCallback();
const html = artists.map((artist) => {
const { name, imageUrl } = artist;
return `
<app-card-manual>
${card.getInnerHTML({ includeShadowRoots: true })}
<h2 slot="title">${name}</h2>
<img slot="thumbnail" src="${imageUrl}" alt="${name}"/>
</app-card-manual>
`;
}).join('');
headers.append('Content-Type', 'text/html');
return new Response(html, {
headers
});
}
Note: Solved by pointing to Greenwood's bundled output in the public/ directory
So although this runs fine locally for /api/fragment-manual
, when run on Netlify, the ERR_REQUIRE_ESM
message is seen.
TODO
TODO