Greenwood v0.28.0

Published: April 8, 2023

What's New

It's release time again and the Greenwood team is excited to share some of the exciting new features available in this version, as we continue to make the experience of writing for the Full-Stack Web easier for everyone! 🙌

Node 18

With this release, Greenwood has set the minimum version of NodeJS to 18. This NodeJS release is especially profound to us as it brings with it (amongst other things) native support for the Fetch API. You can now use fetch directly in your server side code like your SSR pages! 💯

// src/pages/artists.js
import '../components/card.js';

export default class ArtistsPage extends HTMLElement {
  async connectedCallback() {
    const artists = await fetch('https://.../api/artists').then(resp => resp.json());
    const html = artists.map((artist) => {
      const { name, imageUrl } = artist;

      return `
        <wc-card>
          <h2 slot="title">${name}</h2>
          <img slot="image" src="${imageUrl}" alt="Picture of ${name}"/>
        </wc-card>
      `;
    }).join('');

    this.innerHTML = html;
  }
}

As a bonus worth mentioning, JSON imports are now natively available using import assertions.

Embracing Web APIs

What's great about the addition of the Fetch API is that it brings along with it many Web API friends like URL, Request, and Response just to name a few. Greenwood has fully embraced this movement to adopting Web APIs not only throughout its code base, but basing entire user facing APIs around these standards as well. Why invent an API when we get everything we need from the web, in Node, and all documented by MDN!

This was especially beneficial to our Resource Plugin API as it was already modeling this request / response behavior anyway, and so it was a natural fit to adopt these APIs. To give an idea of this transformation, here is a before snippet of Greenwood's internal plugin for handling CSS.

class StandardCssResource extends ResourceInterface {
  constructor(compilation, options) {
    super(compilation, options);
    this.extensions = ['.css'];
    this.contentType = 'text/css';
  }

  async serve(url) {
    const body = await fs.promises.readFile(url, 'utf-8');

    return {
      body,
      contentType: this.contentType
    };
  }
}

And here is what it looks like now, exclusively based on Web APIs. Nothing ad-hoc anymore! ✨

class StandardCssResource extends ResourceInterface {
  constructor(compilation, options) {
    super(compilation, options);
    this.extensions = ['css'];
    this.contentType = 'text/css';
  }

  // defining shouldServe is now required
  async shouldServe(url) {
    const { protocol, pathname } = url;

    return protocol === 'file:' && this.extensions.includes(pathname.split('.').pop());
  }

  async serve(url) {
    const body = await fs.promises.readFile(url, 'utf-8');

    return new Response(body, {
      headers: new Headers({
        'Content-Type': this.contentType
      })
    });
  }
}

This refactor touched most of our plugin APIs, so you'll want to read our release notes to learn about how to migrate any of your own custom plugins.

API Routes

To fully round out Greenwood's server rendering capabilities, API Routes are now available. This API is based on a standard Request / Response model leveraging Web APIs directly as inputs and outputs. Let's take a look at an example.

API routes follow a file based routing convention, just like pages. So this structure will yield an endpoint available at /api/greeting.

src/
  api/
    greeting.js

This example API reads a query parameter of name and returns a JSON response based on that.

export async function handler(request) {
  const params = new URLSearchParams(request.url.slice(request.url.indexOf('?')));
  const name = params.has('name') ? params.get('name') : 'World';
  const body = { message: `Hello ${name}! 👋` };

  return new Response(JSON.stringify(body), {
    headers: new Headers({
      'Content-Type': 'application/json'
    })
  });
}

All web standards, all the time. You know we love to see it. 🤓

What's Next

In addition to the above mentioned features, this release also lays the ground work for our foray into Serverless and Edge runtime support. For out next release, Greenwood team has its sights set on being able to fully embrace running beyond the server with hosting providers like Netlify and Vercel and their serverless and edge offerings, allowing us to push web standards even further down the stack! We see this trend becoming more ubiquitous as more and more hosting providers coalesce around these standards too, so now is a great time to get started!

Stay tuned, join our Slack to be part of the conversation, and we look forward to seeing you in the next release notes! ✌️