Gatsby Astro Drupal

Hoe ik Gatsby verruilde voor Astro (maar wel bij Drupal bleef)

Albert Skibinski
Hoe ik Gatsby verruilde voor Astro (maar wel bij Drupal bleef)

Ruim vijf jaar geleden begon mijn vrouw aan een nieuw avontuur met haar eigen bedrijf als coach voor introverten. Natuurlijk had ze ook een website die ik wel even zou bouwen. Altijd een mooi excuus om iets nieuws te proberen en ddstijds werd dat aan de voorkant Gatsby met Drupal als API en CMS om de boel te beheren. Ik heb er nog over geschreven

Op die manier zijn er in de loop der jaren meer dan 250 blog posts op de site verschenen. En hoewel ik er weinig onderhoud aan had werd ik ook nooit echt blij van Gatsby. Ondertussen kreeg ik wel verzoekjes voor aanpassingen maar het werd steeds lastiger die uit te voeren zonder grote updates uit te voeren waar veel refactoring voor nodig was. 

Een kleine aanpassing...

Dus besloot ik eens rond te kijken naar alternatieven voor een nieuwe site. Ik was gecharmeerd van het concept van static site generators maar vond Gatsby te complex en de hele GraphQl data laag te complex. Ik overwoog Nextjs maar ook die viel vanwege onnodige en toenemende complexiteit af. Remix had ik wel goede ervaringen mee maar die ondersteunde geen SSG (hoewel dat lijkt te veranderen) maar ik wilde ook niet vastzitten aan React om mijn opties open te houden naar de toekomst. 

Astro 

Zodoende kwam ik het relatief nieuw Astro tegen, waarmee ik eigenlijk alles kon afvinken: heerlijk eenvoudig met geweldige documentatie en een JSX achtige syntax die voor React developers heel vertrouwd voelt (maar je kan natuurlijk ook "echte" react/lit/vue/svelte of whatever componenten gebruiken). Hieronder voorbeeld van een simpele .astro componentje. Je krijgt dus een hoop flexibiliteit en een heel fijne developer experience.

---
import SomeAstroComponent from '../components/SomeAstroComponent.astro';
import SomeReactComponent from '../components/SomeReactComponent.jsx';
import someData from '../data/pokemon.json';

// Access passed-in component props, like `<X title="Hello, World" />`
const { title } = Astro.props;
// Fetch external data, even from a private API or database
const data = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());
---
<!-- Your template here! -->

Alles bovenaan tussen de twee --- wordt server side uitgevoerd. 

Drupal blijft het CMS en de API

Data fetching is een eitje waarbij ik besloot om custom endpoints in Drupal te maken voor de JSON responses, in plaats van JSON:API of Graphql. Waarom niet JSON:API? Dat kan ook prima, maar mijn ervaring is dat je dan in de frontend vaak complexiteit moet toevoegen in de vorm van filtering van collecties omdat je het niet rechtstreeks kan ophalen. Met Graphql kan je dat dan wel veer verplaatsen naar de backend, maar de Drupal graphql module is een flink beest en overkill voor deze usecase.

Voor dit project heb ik dit veel sneller met een paar controllers gemaakt in PHP/Drupal en blijft de frontend eenvoudig. De graphql module was ook niet meer nodig en daarmee een zwik aan andere modules en patches waardoor uiteindelijk ook de backend heerlijk opgeruimd werd.

Daarbij blijft Drupal als CMS ook prima te gebruiken. Ook hier weer de focus op simple. Mijn ervaring met Layout Builder en Paragraphs is niet altijd positief. En hoewel het voor grotere projecten prima kan werken, is het voor kleinere ook weer vaak overkill.

Daarom maak ik gebruik van 1 CKeditor body veld met daarin embedded content. De backend splitst de body op in "componenten" welke ik vervolgens in de frontend map en render.

Multilingual

De site is - net zoals deze - meertalig. Toch kan de astro structuur eenvoudig blijven.

Astro folder structure

Astro ondersteunt dynamische routing en de meeste magic zit in [...path]. Hier worden alle pagina's aangemaakt. 

[...path].astro

---
import Article from "@layouts/Article.astro";
import Page from "@layouts/Page.astro";
import Config from '../../../astro.config.mjs'

export async function getStaticPaths() {

    const locales = Config?.i18n?.locales || ['en'];

    // Initialize an array to hold all paths.
    const paths: { params: { lang: string, path: string }, props: { id: number} }[] = [];

    // For each locale, fetch your Drupal data and build the paths.
    for (const locale of locales) {

      const response = await fetch(
        `${import.meta.env.DRUPAL_API}/${locale}/api/nodes`
      );
      const nodes = await response.json();

      // Create a path for each node.
      nodes.forEach((node: Node) => {
        paths.push({
          params: {
            lang: locale,
            path: node.slug,
          },
          props: {
            id: node.nid,
          },
        });
      });

    }

    return paths;
}

const { id } = Astro.props;

// Fetch the individual node data using the id.
const response = await fetch(`${import.meta.env.DRUPAL_API}/${Astro.currentLocale}/api/node/${id}`);
const node = await response.json();

---

{ node.type === 'article' && (
  <Article post={node} />
)}

{ node.type === 'page' && (
  <Page page={node} />
)}

View Transitions API

Deze API maakt het mogelijk om vloeiende overgangen te maken van verschillende page requests. Hierdoor voelt de site veel meer als een app aan, en met een beetje customization kan je ook subtiele animaties maken. Op deze sites zie je dat bijvoorbeeld als je vanuit het blogoverzicht doorklikt op een blog post: de afbeelding blijft in beeld en wordt groter/schuift naar de juiste plek terwijl de rest van de page eromheen opgebouwd wordt. Over het algemeen denk ik dat je spaarzaam moet zijn met animaties maar ik vindt deze wel nice! En het is met een paar regels in astro geimplementeerd.

Demo of view transitions in Astro

Performance

De oude site had een perfect 100/100/100/100 pagespeed score en dat wilde ik zo houden. Qua styling maak ik al een tijdje gebruik van Tailwind, wat ook qua performance goed werkt omdat uiteindelijk alleen de classes die je gebruikt gebundled worden. Voor de rest doet Astro hierbij het meeste werk voor je, want veel sneller dan statische HTML/CSS wordt het niet. En dankzij prefetching voelen pageloads helemaal instant aan.

Pagespeed perfect score 100
PageSpeed Insights

Mocht je trouwens een site hebben die wel gebruik maakt van third party scripts dan is partytown een aanrader. Hierdoor worden scripts via een web worker ingeladen en niet via de main thread.

Islands architectuur

Wat me in Astro ook erg aanspreekt is het concept van islands. Default stript astro component van alle client side js. Maar mocht je toch een bepaalde embedded js applicatie nodig hebben dan kan je die prima hydraten. Je kan per component aangeven of het een client side island of server side island moet zijn. 

Builds

De site van quietquality.nl build in een paar minuten op een bescheiden linode, maar is ook best een aardige site met rond de 300 pagina's in (x2 talen). De build wordt elk uur getriggerd en vervangt na een succesvolle resultaat de oude build. Dat is prima voor een site zoals deze. Voor grotere sites in productie wil je misschien Incremental Static Regeneration (ISR) die getriggerd wordt vanuit het CMS. Maar je kan dus ook veel dynamische content oplossen met island architectuur.

Anyway, het is heerlijk bouwen op Astro die een soort sweet spot gevonden lijkt te hebben voor veel developers. Ik ben benieuwd wat het over 5 jaar wordt ;)

 

 

Albert Skibinski

Over de auteur

  • Albert Skibinski is een zelfstandig ontwikkelaar en co-founder van Jafix
  • Ik schrijf over web development, lange fietstochten en lekker eten!
Terug naar overzicht