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
TODO
- Do we even need workers for build output?
- if not, how to make a generic solution? (make a pure
executeModule
function and run the worker ourselves when needed in dev mode?)
- if not, how to make a generic solution? (make a pure
- Will need to generate the .netlify/functions folder on-demand / as part of the build instead of hardcoding, likely from manifest.json
- For SSR pages, manual is the only option? That will impact how pages can be built, e.g. manual card, shadow dom, etc and might to be configurable based on if the platform supports
import.meta.url
or not it seems.- we can get around this using our own bundling solution - https://docs.netlify.com/functions/deploy/?fn-language=js#custom-build-2
- How to best manage local dev (runtime "compliance") - mixed support, see caveats section of the plugin's README
- proxy netlify cli dev option?
- should use src/ or public/? depends on dev vs production mode? Interestingly, the manual way only worked deployed when using public/
- if esbuild worked w/
import.meta.url
, we could probably ship unzipped bundles, and then dev would also work?
- Need to provide custom netlify.toml?
- Make sure to spread all headers / response properties in netlify functions adapter output
- SSR pages are bundling into public/api/ directory ?
- Keep it as an experimental feature for 1.0 (or per platform?)