Créer son propre plugin source GatsbyJS pour consommer une API REST — 1 : Création du plugin source

Mélanie Paque
Publicis Sapient France
10 min readFeb 20, 2023

Vous avez probablement entendu parler, et peut-être même déjà éprouvé, le framework Gatsby, cette solution JAMstack permettant la génération de sites statiques. Il est notamment plébiscité pour les performances possibles apportées vis à vis du chargement de pages dans le navigateur, du temps de construction des pages ou encore pour sa flexibilité d’utilisation.

Nous allons nous intéresser à l’une des composantes de cette flexibilité : le système de plugin de Gatsby, qui permet de personnaliser et d’étendre les fonctionnalités d’un site web et plus particulièrement des plugins de source qui permettent de connecter des sources de données pour pouvoir les exploiter.

Dans cet article, nous allons voir comment créer un plugin source Gatsby. À partir d’une API REST distante, nous allons créer une source de données que nous pourrons exploiter et afficher dans un site généré par Gatsby.

Pour notre démonstration, nous réaliserons un petit projet qui consommera une API public. Cette API, REST Countries, nous fournira des informations sur des pays.

L’objectif sera de créer une source de données consommable par Gatsby depuis cette source distante, de consommer ces données et de les afficher sur un site.

Avant de commencer, abordons rapidement la notion de plugin source.

Qu’est ce qu’un plugin source Gatsby?

Un plugin source est un plugin permettant de créer des sources de données dans un format exploitable par Gatsby à partir d’emplacements locaux ou distants.

Gatsby base sa gestion de données sur GraphQL. On y retrouve ainsi les concepts de schema avec des nœuds typés.

Pour en savoir plus sur ces concepts, vous pouvez consulter cet article.

Dans le plugin source, nous utiliserons Gatsby pour rapatrier des données d’une source externe et de créer à partir de celle-ci des entités de données sous format de nœud qui vivront au sein d’un graphe, chaque nœud pouvant avoir des liens entre eux.

Un plugin prend la forme d’un package npm standard, et comprend au minimum un fichier gatsby-nodes.js dans lequel sont implémentés les API Node de Gatsby via lesquelles nous créerons les nœuds.

Créer son plugin source

Nous réaliserons notre petit projet en quatre étapes :

  • la création d’un site Gatsby
  • l’initialisation du plugin
  • l’appel à l’API distante et la création de nœuds
  • la consommation et l’affichage des données du plugin source sur le site

Le programme étant annoncé, lançons-nous!

Créer un site Gatsby

Pour commencer ce petit projet, nous allons créer un nouveau site Gatsby. Nous pourrons par la suite y visualiser les données que nous récupérerons via notre plugin source.

Nous le générons via la commande suivante :

npm init gatsby

Configurez votre site selon vos besoins.

Ci-dessous la configuration choisie pour notre petit projet :

Un fois le projet initialisé, vous pouvez aller dans le répertoire de ce dernier, ici ./website-countries, et lancer la commande :

npm run develop

Vous avez désormais votre site web lancé en local :

Cette page correspond au rendu de votre page index qui se trouve dans ./src/pages/index.js.

Entrons maintenant dans le vif du sujet : la création du plugin.

Initialiser le plugin

Nous ajoutons un dossier ./plugin à l’intérieur de ./website-countries. C’est dans ce dernier que nous allons créer notre plugin.

À l’intérieur de ./plugin, nous initialisons notre nouveau plugin avec le starter gatsby-starter-plugin.

Nous l’appelons gatsby-source-countries par exemple, conformément à la convention de nommage qui veut que le nom d’un plugin source commence par “gatsby-source-”

Lançons la commande suivante:

npx gatsby new gatsby-source-countries https://github.com/gatsbyjs/gatsby-starter-plugin

Une fois généré, nous pouvons y observer un ensemble de fichiers, dont le fichier gatsby-node.js.

C’est dans ce fichier que nous allons accéder aux APIs de Gatsby que nous pouvons utiliser, notamment lors du cycle de vie de la construction du site.

Pour cela, nous exportons les APIs que nous souhaitons utiliser.

Ces APIs mettent à disposition un ensemble de helpers, qui nous permettent d’accéder à des méthodes et de réaliser des actions comme la création de page, la création de nœuds etc.

Pour l’exemple, nous allons utiliser l’API onPreInit pour afficher un message en console, onPreInit s’exécutant dès que le plugin est chargé.

Nous ajoutons ainsi dans le fichier gatsby-node.js :

./plugins/gatsby-source-countries/gatsby-node.js

exports.onPreInit = () => console.log("Loaded gatsby-source-countries")

Si nous lançons dès maintenant le build, nous ne verrons pas ce message car nous n’avons pas encore ajouté le plugin à la configuration du site.

Nous ajoutons ainsi dans ./website-countries/gatsby-config.js :

./plugins/gatsby-source-countries/gatsby-config.js

module.exports = {
plugins: [
{
resolve: 'gatsby-source-countries'
}
]
}

Cette fois, lorsque nous lançons le build, nous voyons bien apparaître en console le message que nous avions défini dans onPreinit:

Notre plugin est bien chargé.

Appel API et création de nœuds

Pour cette démonstration, nous allons utiliser comme source distante de donnée une API REST publique gratuite : https://restcountries.com/

Cette API renvoie des informations sur des pays : nom, langue, capital, drapeau etc.

Nous allons faire de ces données des nœuds exploitables par Gatsby.

Revenons à notre site. Sur notre serveur en local, nous accédons à l’interface GraphQL : http://localhost:8000/___graphql. Nous pouvons y explorer les données et leur schéma.

Vous remarquerez que pour le moment c’est assez limité :

Nous allons l’enrichir.

Pour ce faire, nous allons utiliser l’API sourcesNodes, qui met à disposition des helpers nous permettant de créer des nœuds.

Pour approfondir, la documentation de sourceNodes est ici

Dans notre fichier gatsby-node.js, nous ajoutons ainsi :

./plugins/gatsby-source-countries/gatsby-node.js

exports.sourceNodes = async ({
actions,
createNodeId,
createContentDigest,
reporter,
}) => {
try {

}catch (e) {
reporter.error(e.message)
process.exit()
}
}

Cette structure sera notre point de départ.

C’est dans le try que nous allons récupérer les données de l’API et créer des nœuds à partir de ces dernières.

Dans le catch, nous avons ajouté une gestion d’erreur : si une erreur se produit, le helper reporter affichera en console un message d’erreur. Nous faisons également échouer le build.

Nous allons ensuite récupérer les données depuis l’API http://restcountries.com .

./plugins/gatsby-source-countries/gatsby-node.js

const fetch = require('node-fetch')

exports.sourceNodes = async ({
actions,
createNodeId,
createContentDigest,
reporter,
}) => {
try {

const response = await fetch(`https://restcountries.com/v3.1/all`, {
method: 'GET',
headers : {
'Content-Type': 'application/json',
},
})
const countries = await response.json()

}catch (e) {
reporter.error(e.message)
process.exit()
}
}

Nous effectuons une requête sur l’API Restcountries, sur la route /all, et récupérons un JSON avec l’ensemble des données de tous les pays que nous stockons dans la variable countries.

Notre variable countries contient alors un tableau d’objets, où chaque objet correspond à un pays avec ses données tel que le nom, la langue, la devise, etc.

Nous voulons créer pour chacun de ces objets un nœud, avec tous les champs requis pour que celui-ci soit consommable par Gatsby, tel qu’un node ID et un content digest (utilisé notamment par Gatsby pour détecter un nœud changé ou erroné).

Nous parcourons donc notre tableau de pays, et allons pour chacun d’eux créer un nouvel objet construit avec la structure attendu par Gatsby, et que nous utilisons pour créer les nœuds et où :

  • country : comprend les données du pays, ces données déjà structurées devant être ajoutées au niveau supérieur de l’objet nœud
  • nodeMeta : comprend les champs requis propres aux nœuds

./plugins/gatsby-source-countries/gatsby-node.js

const fetch = require('node-fetch')

exports.sourceNodes = async ({
actions,
createNodeId,
createContentDigest,
reporter,
}) => {

const { createNode } = actions

try {

const response = await fetch(`https://restcountries.com/v3.1/all`, {
method: 'GET',
headers : {
'Content-Type': 'application/json',
},
})
const countries = await response.json()

for (const country of countries) {
const nodeContent = JSON.stringify(country)
const nodeMeta = {}
createNode( Object.assign({}, country, nodeMeta))
}

}catch (e) {
reporter.error(e.message)
process.exit()
}
}

Il nous faut ensuite compléter l’objet nodeMeta :

Pour créer un nœud à partir de createNode, nous devons lui passer en paramètre un objet comportant un certain nombre de champs requis tel que :

  • id : l’identifiant unique du nœud
  • parent : l’id du parent du nœud, si le nœud est dérivé d’un autre.
  • children : un tableau d’id de nœuds enfants, si des nœuds en sont dérivés
  • internal :
    — type : le type Graphql du nœud
    — mediaType : indique que le nœud a un contenu brut pouvant être transformé par le plugin transformer, dans notre cas le format JSON (facultatif)
    — content : contient le contenu brut (facultatif)
    — contentDigest : le digest du contenu du nœud. Il permet à Gatsby de détecter les changements et de ne pas réaliser des actions supplémentaires sur des données n’ayant pas changé.

Pour plus d’informations sur la structure de l’objet nœud, vous pouvez consulter la documentation de createNode ici.

Nous obtenons finalement :

./plugins/gatsby-source-countries/gatsby-node.js

const fetch = require('node-fetch')

exports.onPreInit = () => console.log("Loaded gatsby-source-countries")

exports.sourceNodes = async ({
actions,
createNodeId,
createContentDigest,
reporter,
}) => {
const { createNode } = actions

try {
const response = await fetch(`https://restcountries.com/v3.1/all`, {
method: 'GET',
headers : {
'Content-Type': 'application/json',
},
})
const countries = await response.json()

for (const country of countries) {
const nodeContent = JSON.stringify(country)
const nodeMeta = {
id: createNodeId(`restcountries-country-${country.name.common}`),
parent: null,
children: [],
internal: {
type: `RestcountriesCountry`,
mediaType: `application/json`,
content: nodeContent,
contentDigest: createContentDigest(country),
},
}
createNode( Object.assign({}, country, nodeMeta))
}
}catch (e) {
console.error(e)
reporter.error(e.message)
process.exit()
}
}

Relancez votre render local et aller sur l’interface graphql : http://localhost:8000/___graphql

Dans l’explorer, vous remarquez deux query supplémentaires, correspondant à l’internal type que nous avons défini lors de la construction des nœuds :

  • restcountriesCountry
  • allRestcountriesCountry

Ça y est, vous avez la base de votre plugin source Gatsby !

Consommer et afficher les données créées par le plugin source

Nous allons maintenant consommer les données que nous avons grâce au plugin.

Tout d’abord nettoyons un peu notre page index en ne gardant que le minimum utile du code initialement généré :

./src/pages/index.js

import * as React from "react"

// styles
const pageStyles = {
color: "#232129",
padding: 96,
fontFamily: "-apple-system, Roboto, sans-serif, serif",
}

const IndexPage = () => {
return (
<main style={pageStyles}>

</main>
)
}

export default IndexPage

Récupérons nos données dans notre page index.

Pour cela nous ajoutons un query graphql à notre fichier index.js et récupérons les données dans IndexPage :

./src/pages/index.js

import * as React from "react"
import { graphql } from 'gatsby'

// styles ...

const IndexPage = ({
data: {
countries
},
}) => {
return (
<main style={pageStyles}>

</main>
)
}

export const query = graphql`
query PageIndex {
countries: allRestcountriesCountry(limit: 20) {
nodes {
id
region
name {
common
}
flags {
svg
}
}
}
}
`
export default IndexPage

Vous remarquerez que nous avons ajouté à notre query graphql un argument limit. Sans ce dernier, nous récupérions la totalité des pays, soit plus d’une centaine, ce dont nous n’avons nullement besoin pour notre exemple.

Nous ne récupérons ainsi que 20 pays, dont nous allons maintenant afficher les données.

Afin de s’épargner le développement de composants supplémentaires et d’ajout de style, nous allons utiliser material-ui, une bibliothèque de composants.

Nous ne verrons pas ici de fonctionnement des composants et autres fonctionnalités de material ui. Vous pouvez consulter la documentation ici si besoin.

Installons Material UI et ses packages associés via npm :

npm install @mui/material @emotion/react @emotion/styled

et utilisons les composants Grid et List, auquel nous ajoutons un peu de style, afin de mettre en forme nos données :

./src/pages/index.js

import * as React from "react"
import { graphql } from 'gatsby'
import Grid from '@mui/material/Grid';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import ListItemAvatar from '@mui/material/ListItemAvatar';
import { grey } from '@mui/material/colors';

// styles
const pageStyles = {
color: "#232129",
padding: 96,
fontFamily: "-apple-system, Roboto, sans-serif, serif",
}

const IndexPage = ({
data: {
countries
},
}) => {
return (
<main style={pageStyles}>
<Grid container rowSpacing={2} columnSpacing={2}>
{ countries.nodes.map((country) =>
<Grid item xs={6} md={4} key={`grid-country-${country.id}`}>
<ListItem disablePadding sx={{
border: `1px solid ${grey[200]}`,
borderRadius: '5px'
}
}>
<ListItemAvatar style={{display:"flex", justifyContent:"center"}}>
<img width="80px" width="40px" src={country.flags.svg}/>
</ListItemAvatar>
<ListItemText primary={country.name.common} secondary={country.region} sx={{
backgroundColor: grey[100],
margin: 0,
padding: '5px 10px'
}
}/>
</ListItem>
</Grid>
)}
</Grid>
</main>
)
}

export const query = graphql`
query PageIndex {
countries: allRestcountriesCountry(limit: 20) {
nodes {
id
region
name {
common
official
}
flags {
svg
}
}
}
}
`

export default IndexPage

Dans le navigateur nous pouvons visualiser le résultat : une liste de pays avec leur nom, région et drapeau.

Pour conclure

Nous avons réalisé la première étape de création d’un plugin source Gatsby.

Si celui-ci fonctionne, il n’est pas encore optimal.

Nous verrons comment l’améliorer dans le prochain article, dans lequel nous aborderons la notion de cache et l’usage des nœuds persistant.

Ressources :

Vous pouvez retrouver l’ensemble du code du tutoriel sur github ici.

Gatsby starter plugin

Gatsby — sourcesNodes

Gatsby — createNode

Documentation REST Countries API

Material UI

--

--