We hebben een open source webhook ontwikkeld die je Gatsby webapplicatie opnieuw opbouwt zodra je content in Sanity published. Als je direct aan de gang wil met de webhook, scroll dan naar het einde van het artikel.
Bij Humanoids zijn we sinds kort bezig met het revampen van onze website. Hierbij maken we gebruik van het Gatsby framework, wat we hebben gekoppeld aan een Content Management System (CMS) genaamd Sanity. Het CMS stelt ons in staat om via het Sanity dashboard eenvoudig content op onze website en blog aan te passen. Gatsby stelt ons in staat om razendsnelle performance te garanderen, doordat Gatsby eenmalig statische pagina's gegenereerd die vliegensvlug aan de eindgebruiker kunnen worden getoond.
So far so good. What’s the catch?
De opbouw van statische pagina’s door Gatsby gebeurt niet automatisch wanneer content wordt aangepast via ons Sanity CMS. Daarnaast is het natuurlijk niet wenselijk om bij iedere content update handmatig Gatsby aan te moeten slingeren om onze website opnieuw te laten bouwen met de aangepaste content. Wat er ontbrak was dus een geautomatiseerde connectie tussen ons CMS en onze Gatsby website, die er voor zou zorgen dat onze website automatisch zou updaten wanneer er een CMS update plaatsvond. En wat doen we bij Humanoids als we er achter komen dat er nog geen bestaande oplossing bestaat voor onze digitale proble- pardon, uitdagingen? Precies ja: dan bouwen we zelf wel een oplossing!
De oplossing: een custom webhook
De essentie van onze uitdaging was dus om (1) onze Gatsby website een seintje te geven zo gauw er een update in ons CMS plaatsvond, en (2) de aangepaste content door Gatsby op te laten halen in ons CMS en deze te verwerken tot een vers aangepaste Humanoids.nl website.
Voor het eerste gedeelte van deze uitdaging vonden we al snel een aanknopingspunt bij het Sanity CMS. Sanity biedt namelijk de optie om een POST-request te maken naar een webhook. De gebruiker kan hierbij een HTTP-eindpunt opgeven bij Sanity, zodat Sanity vervolgens naar dit adres een POST-request verstuurd zo gauw er iets verandert aan de content in het CMS. Deze optie bood hoop, alleen bracht het ons bij de volgende uitdaging: hoe zorgen we er voor dat we op een specifiek HTTP-eindpunt continu luisteren naar een Sanity signaal, om dit vervolgens door te zenden naar onze Gatsby website?
Hoe de webhook werkt?
We gebruiken voor de hosting van de webhook een Express Server in Node.js die met behulp van Dockerfiles en Kubernetes wordt geupload naar een HTTP-endpoint via onze hosting, DigitalOcean. Dat is een flinke mond vol vakjargon waar we reeds over hebben geschreven in dit artikel. Geen paniek als je deze woorden niet direct weet te plaatsen, het is voor het gebruik van de webhook niet belangrijk om te weten.
Zodra het Sanity POST-request binnenkomt in onze webhook checken we eerst of de juiste ‘path parameters’ mee zijn gestuurd vanuit Sanity in de POST-request. Dit heeft als functie dat slechtwillende individuen niet zomaar rebuilds van onze Gatsby website kunnen triggeren door deze webhook te activeren. Wanneer de juiste path parameters zijn verstuurd naar onze webhook is de authenticatie voltooid en stuurt de webhook een POST-request, inclusief geautoriseerd GitHub token in de header, naar de GitHub repository die onze Gatsby website source code bevat.
const response = await fetch(`https://api.github.com/repos/${HUM_GITHUB_ORGANIZATION}/${HUM_GITHUB_REPOSITORY}/dispatches`,{method: 'POST',headers: {Authorization: `Bearer ${HUM_GITHUB_TOKEN}`,'Content-Type': 'application/json'},body: JSON.stringify({event_type: `${dataset}_build`})});
Bouwen met GitHub Actions
Zodra de POST-request vanuit de webhook is binnengekomen op onze GitHub repository vuurt GitHub Actions een actie af genaamd 'Repository Dispatch' die vervolgens een nieuwe Gatsby build triggered met de geupdate content uit Sanity. Naast deze Gatsby build gebruiken we deze trigger ook direct om de Gatsby build online te zetten door middel van Docker, Kubernetes en onze hosting DigitalOcean.
Ok duidelijk, hoe gebruik ik dit zelf?
Om de webhook te gebruiken met je eigen Sanity en Gatsby setup is het nodig om zelf een webhook te hosten en die beschikbaar te maken op een HTTP-endpoint.
Via de webhook image op Docker Hubs kun je direct van start gaan. Hiervoor heb je wel een Docker account nodig.
Belangrijk om mee te nemen is dat de webhook draait op port 3000 en dat er verschillende environment variabelen meegegeven moeten worden:
HUM_GITHUB_ORGANIZATION = # GitHub profielnaam of organisatieHUM_GITHUB_REPOSITORY = # RepositoryHUM_GITHUB_TOKEN = # Een GitHub token dat geautoriseerd is voor het triggeren van repository dispatch events in je repository naar wensHUM_SHARED_SECRET = # De path parameter die je toevoegt aan het eindpunt van je HTTP webhook
Tot slot is het belangrijk dat je in je Sanity dashboard de HUM_SHARED_SECRET meegeeft.
De webhook hosten
Er zijn allerlei manieren om de webhook te hosten, zelf gebruiken we GitHub Actions en een Kubernetes configuratie op DigitalOcean.
Mocht je dit zelf ook via Kubernetes willen doen, dan zal onderstaande Kubernetes configuratie van pas komen. We starten met de deployment.yaml. Hier definiëren we de environment variabelen en de verwijzing naar de image:
# ./kubernetes/deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata:name: sanity-webhook-githubnamespace: productionlabels:app: sanity-webhook-githubspec:replicas: 2selector:matchLabels:app: sanity-webhook-githubtemplate:metadata:labels:app: sanity-webhook-githubspec:containers:- name: sanity-webhook-githubimage: humanoids/sanity-webhook-githubenv:- name : HUM_GITHUB_ORGANIZATIONvalueFrom:secretKeyRef:name: sanity-webhook-github-configkey: HUM_GITHUB_ORGANIZATION- name : HUM_GITHUB_REPOSITORYvalueFrom:secretKeyRef:name: sanity-webhook-github-configkey: HUM_GITHUB_REPOSITORY- name: HUM_SHARED_SECRETvalueFrom:secretKeyRef:name: sanity-webhook-github-configkey: HUM_SHARED_SECRET- name: HUM_GITHUB_TOKENvalueFrom:secretKeyRef:name: sanity-webhook-github-configkey: HUM_GITHUB_TOKENports:- containerPort: 3000imagePullPolicy: AlwaysimagePullSecrets:- name: registry
Vervolgens roepen we de deployment aan vanaf de service:
# ./kubernetes/service.yamlapiVersion: v1kind: Servicemetadata:labels:app: sanity-webhook-githubname: sanity-webhook-githubnamespace: productionspec:ports:- port: 80protocol: TCPtargetPort: 3000selector:app: sanity-webhook-github
Voordat we deze configuraties gaan toepassen is het belangrijk om de environment variabelen als secrets mee te geven:
kubectl create secret generic sanity-webhook-github-config--from-literal=HUM_GITHUB_ORGANIZATION=<HUM_GITHUB_ORGANIZATION>--from-literal=HUM_GITHUB_REPOSITORY=<HUM_GITHUB_REPOSITORY>--from-literal=HUM_SHARED_SECRET=<HUM_SHARED_SECRET>--from-literal=HUM_GITHUB_TOKEN=<HUM_GITHUB_TOKEN>--dry-run -n production -o yaml
En dat was dat: een verandering van de data in je Sanity CMS geeft een seintje aan je custom webhook. Als je autorisatie check groen licht geeft, triggered de webhook een GitHub Action in je GitHub repository naar keuze en kan je losgaan!
We hebben een artikel geschreven over het opzetten van een Kubernetes cluster op DigitalOcean voor als je hier meer over wilt weten.