Table des matières
Ouvrir table des matières
- WordPress : une hérésie pour le développement web moderne?
- Mes besoins techniques
- Mes besoins en termes d’hébergement
- Les fonctionnalités à développer frontend/backend
- Mon choix technique pour la partie Frontend : Astro
- Un Backend sur mesure en TypeScript/Node.js
- Les différents hébergements que j’ai choisi
- Le package serverless
- Migration des données WordPress vers le nouveau blog
- Epilogue
WordPress : une hérésie pour le développement web moderne?
WordPress n’est plus à présenter. C’est un CMS mondialement connu, conçu sous PHP/MySQL pour son moteur et bien évidemment HTML/CSS/JavaScript (coucou jQuery) pour le rendu.
Bien sûr, WordPress est accessible pour les développeurs, mais il a été conçu pour que des non-développeurs puissent concevoir leur blog eux-mêmes en mode clic-clic.
D’ailleurs, au début, WordPress était un moteur pour produire des blogs et aujourd’hui il est possible de le transformer en sites internet complets et complexes grâce à la partie thèmes et plugins qu’il nous offre.
Que ce soit WordPress ou un autre CMS, l’objectif premier est de se concentrer sur la production de contenu plutôt que sur la partie technique.
Je me suis tourné naturellement vers WordPress pour sa popularité et avec pour objectif de rester concentré uniquement sur le contenu que je voulais partager et de ne pas m’embêter avec la technique (un comble pour un développeur, me direz-vous ?).
Les années passent, j’ai refait le design du blog plusieurs fois, installé de plus en plus de plugins dessus… Jusqu’à prendre conscience que WordPress est devenu une hérésie aujourd’hui.
Wordpress est-il une hérésie technique ?
Quand vous créez un site internet ou un blog, qu’est-ce que vous voulez obtenir ?
Un résultat élégant, rapide, accessible, optimisé et performant. Il est difficile aujourd’hui d’obtenir ce résultat avec un site sous WordPress à moins de développer son propre thème et ses propres extensions pour les besoins de son site.
Mais même avec ça, on est obligé d’avoir recours à une extension de génération de cache client (WP Rocket) qui va en réalité générer une version statique de votre blog à intervalles réguliers.
On peut même le combiner avec du cache serveur (Varnish) pour améliorer encore la vitesse.
En résumé, quand vous utilisez WordPress avec un thème et des extensions, puis que vous mixez le tout avec un WP Rocket : vous obtenez à la fin une version statique de votre site internet avec un DOM monstrueux et loin d’être optimal.
C’est le constat que j’ai remarqué avec mon propre blog et je me suis posé cette question :
Pourquoi ne pas produire directement cette version statique du blog mais de manière élégante, rapide, accessible, optimisée et performante ?
C’est le choix que j’ai donc fait : produire directement la version statique de mon blog de manière innovante.
Mes besoins techniques
Pour bien faire ma migration de WordPress, j’ai décidé de passer au crible mon WordPress pour voir dans le détail toutes les fonctionnalités réellement utiles.
J’en ai fait un MindMap en catégorisant les plugins et fonctionnalités à garder (en vert) et celles à jeter (en rouge).
Comme vous pouvez le voir, la moitié est à jeter 😄.
Bien entendu, je n’ai pas listé les fonctionnalités classiques mais essentielles d’un blog, par exemple l’édition de nouveaux articles, le SEO, une barre de recherche, un système de catégories/tags, un sitemap, un flux RSS, etc…
Mes besoins en termes d’hébergement
Actuellement, mon WordPress est hébergé chez l’hébergeur O2Switch 🔗. C’est un hébergement partagé qui offre un cPanel pour :
- héberger son WordPress en PHP/MySQL
- des applications en Python ou Ruby ou Node.js
- héberger ses emails
- gérer ses noms de domaine
- héberger des bases de données MySQL/PostgreSQL
- et bien d’autres fonctionnalités que je n’ai pas eu l’occasion de tester.
Le coût de l’hébergement me coûte environ 100 euros par an + 10 euros pour le nom de domaine = 110 euros. Et je dois le payer annuellement.
En quittant WordPress, je me suis fixé comme objectif de trouver un hébergement évolutif, avec lequel je ne suis pas “marié”, et, cerise sur le gâteau, moins cher que O2Switch.
Les fonctionnalités à développer frontend/backend
J’ai décidé de mettre en place une architecture Front/API en reprenant mon état des lieux de WordPress pour évaluer les fonctionnalités que je dois développer côté backend et côté frontend.
Il y a trois fonctionnalités à développer pour le backend :
- La réception d’un formulaire de contact qui est utilisé par le blog et mon autre site, gaetancottrez.dev 🔗, avec un envoi d’e-mail.
- L’inscription à ma newsletter via le service ActiveCampaign.
- Re-créer un système équivalent à Revive Old Post de WordPress pour repartager mes articles sur Facebook.
Pour le frontend, il y en a un peu plus :
- Captcha où j’ai utilisé le reCaptcha de Google.
- What Would Seth Godin Do.
- Social Share.
- Related Article.
- Buy me a Coffee.
- Popup ActiveCampaign.
- Ribbon ActiveCampaign.
- Widget ActiveCampaign.
- Formulaire de contact.
- Intégration de Commentbox.io.
Laissez-moi maintenant vous expliquer en détail mes choix techniques.
Mon choix technique pour la partie Frontend : Astro
Depuis ces dernières années, de nouvelles technologies et de nouveaux frameworks ont émergé.
Ses nouvelles technologies reviennent à l’essentiel : générer une version statique (SSG) d’un site internet ou d’un blog de manière simple et efficace tout en intégrant les standards du web.
C’est ce que je recherchais dans ma quête de migration de mon blog WordPress.
J’aurais pu me tourner comme beaucoup l’ont fait vers Gatsby 🔗. Mais n’étant pas un grand fan de React, je ne voulais pas d’un framework qui a été bâti autour cette librairie.
Pour la partie Front, j’ai décidé de me diriger sur Astro 🔗.
Alors, pourquoi AstroJS ? Voici quelques points intéressant qui ont retenu mon attention:
- Il peut générer des sites web statiques, ce qui signifie que le site peut être pré-construit et servi sous forme de fichiers HTML, CSS et JavaScript basique, ce qui le rend plus rapide et plus sécurisé.
- Il est conçu pour construire rapidement des sites web et des petites applications web.
- Markdown to HTML : on produit son contenu avec des fichiers Markdown et à la compilation on a des fichiers HTML propres et optimisés
- Il offre une approche unique en combinant le rendu côté serveur (SSR) et le rendu côté client (CSR) pour offrir des performances optimales.
- Il inclut des optimisations intégrées pour les performances, telles que la division du code, le tree shaking et le préchargement automatique des ressources
- Il n’embarque aucun JavaScript côté client par défaut. Uniquement que côté serveur pour la compilation
- Mais grâce au “Bring Your Own Framework” (BYOF), il est possible d’utiliser React, Svelte, Vue ou n’importe quel framework JavaScript moderne avec lequel vous êtes à l’aise pour créer des composants ayant besoin du JavaScript.
- Il intègre tous les outils essentiels pour produire un site web : robot.txt, sitemap, compression & optimisation des fichiers HTML/CSS/JavaScript/medias et redirectionIl existe des thèmes Astro officiel et fourni par la communauté qui fournisse une première base pour son site mais également d’autres outils intéressant pour un site internet : accessibilité, SEO, GTM, OpenGraph, Breadcrumb, Table des matière.
Bien entendu, j’ai pris en compte d’autres points qui sont essentiels dans le choix de cette techno.
Si vous voulez savoir comment je m’y prends pour choisir une nouvelle techno sans suivre les autres comme un mouton, c’est par ici.
Petite aparté : comme je l’ai dit plus haut, je ne porte pas particulièrement React dans mon coeur et ceux depuis sa création. Néanmoins j’ai voulu me faire un réel avis sur cette librairie en l’utilisant dans AstroJS pour mon blog. Si vous êtes intéressé par mon avis sur React, n’hésitez pas à me le faire savoir en commentaire.
J’ai d’ailleurs choisi le thème Astro paper 🔗 qui a été la base de mon blog et où j’ai développé toutes les fonctionnalités que j’ai listé un peu plus haut.
J’ai donc créé quelques composants en React (où le thème présentait certains composants déjà développé en React) histoire de me faire un avis sur cette librairie.
Un Backend sur mesure en TypeScript/Node.js
C’est mon domaine de prédilection et c’est celui où je m’amuse le plus : le backend. J’ai mis en pratique toute mon expérience et mes connaissances dans la réalisation de cette architecture backend.
J’ai structuré le code du backend sous la forme de package grâce à turbo 🔗.
├── packages
│ ├── domain
│ ├── application
│ ├── infrastructure
│ ├── cron
│ ├── api
│ ├── serverless
Chacun de ces dossiers a un rôle bien défini autour d’une architecture hexagonale.
Pourquoi je suis parti sur cette architecture ?
La question est légitime sachant que, côté backend, je n’ai que trois fonctionnalités à développer, mais comme je suis un fervent défenseur de la partie backend, j’aime faire les choses correctement.
Je ne sais pas où je vais aller avec mon backend. Il y aura certainement de nouvelles fonctionnalités à implanter, et probablement beaucoup plus complexes que celles que j’ai développées.
L’une de mes attentes est de ne pas être dépendant d’une techno en particulier. Ainsi, je suis parti pour la couche API sur un NestJS, mais si je veux en changer demain, pas de problème : il me suffit de changer la techno de l’API, d’implanter mes routes et les appels du code métier sans tout recoder.
De même, mon code métier pourra s’exécuter sur des infrastructures différentes (dans mon cas, l’API et la partie cron).
Explication de l’architecture
Tout d’abord, nous avons les trois couches de l’architecture hexagonale qui fonctionnent ainsi :
domain ├──> application ├──> infrastructure
- Le domain fonctionne de manière autonome et il contient l’ensemble des règles métiers. Il ne connait que lui-même.
- L’application orchestre ses règles métiers par des
uses-cases
. Elle implémente le domaine mais ne connait pas l’infrastructure. - L’infrastructure est la partie qui va implanter l’application (et indirectement le domaine sans le savoir).
Sauf que dans mon cas, ce n’est pas vraiment ça.
La couche d’infrastructure va s’occuper de répondre aux contrats fixés par l’application et va faire fonctionner tout ce petit monde dans une condition de réalité d’infrastructure.
Ainsi, j’y définis :
- les
repositories
qui seront utilisés par l’application - mes drivers/connexion aux bases de données :
MongoDB
- mes différents services externes :
Nodemailer
,ActiveCampaign
,Partage sur Facebook
Les dossiers cron
et api
auraient pu être placés dans le dossier infrastructure
, mais j’ai trouvé beaucoup plus simple et cohérent de les séparer en des dossiers distincts.
api ├──> infrastructure ├──> application
cron ├──> infrastructure ├──> application
Le package api démarre une fois que le package infrastructure a bien démarré pour ensuite utiliser le package d’application. Et c’est la même chose pour le package cron.
On veut du code !
Voici le code du package API qui permet de démarrer par le package infrastructure :
async function CreateApp(httpAdapter = {}) {
let app = await NestFactory.create<NestExpressApplication>(
AppModule,
httpAdapter
);
app = app.enableVersioning({
type: VersioningType.URI,
defaultVersion: CURRENT_VERSION,
});
// Express
app.disable("x-powered-by");
app.setGlobalPrefix("");
app.useStaticAssets(join(__dirname, "..", "public"));
app.setViewEngine("hbs");
app.use(text());
// Swagger
const options = new DocumentBuilder()
.setTitle(container.resolve(tokens.TITLE_API))
.setDescription("")
.setVersion(CURRENT_VERSION)
.addBasicAuth()
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup("", app, document);
return app;
}
async function bootstrap() {
const app = await CreateApp();
await app.listen(container.resolve(tokens.PORT) ?? 3000);
return app;
}
infra().then(bootstrap);
C’est justement ce petit bout de code tout simple qui permet de démarrer l’infrastructure et l’API :
infra().then(bootstrap);
Et voici, en partie, le code de la fonction infrastructure :
const infra = async function () {
const startTime = Date.now();
const infraLogger = new Logger("INFRA");
infraLogger.info("Loading configuration from remote mongo");
dotenvConfig({ debug: true, path: resolve(__dirname, "../../../.env") });
await config(infraLogger);
infraLogger.info(
`${container.resolve(tokens.TITLE_API)} : ${container.resolve(
tokens.API_BASE_URL
)}`
);
infraLogger.debug("Registering services");
for (const clazz of Object.values(services)) {
if (
!container.isRegistered(
clazz as new (...args: any[]) => Partial<IService>
)
) {
continue;
}
const instance: Partial<IService> = container.resolve(
clazz as new (...args: any[]) => Partial<IService>
);
if (instance.start) {
infraLogger.debug(`Starting service: ${instance.constructor.name}....`);
await instance.start();
infraLogger.debug(`Service started: ${instance.constructor.name}`);
}
}
for (const clazz of Object.values(repositories)) {
if (!container.isRegistered(clazz)) {
continue;
}
container.resolve(clazz as new (...args: any[]) => any);
}
infraLogger.info(`Infrastructure is setup in ${Date.now() - startTime}ms`);
};
Rien de bien compliqué en soi ! Simple et efficace, comme vous le voyez !
Les différents hébergements que j’ai choisi
Mon blog a, de façon modeste, un petit trafic (environ 200 visites uniques par jour). Je recherche donc un hébergement évolutif que je pourrais ajuster facilement en cas de pics dans mon trafic.
Je pars du principe que je peux déployer n’importe où et n’importe quand. Si en plus cela peut être rapide, facile et pas cher sans être lié à l’hébergeur, c’est encore mieux.
Je me suis donc tourné vers Vercel 🔗 pour déployer mon blog Astro. J’aurais pu utiliser Netlify 🔗 mais j’ai voulu tester Vercel pour me faire un avis.
Le moins que l’on puisse dire c’est que la version gratuite sera amplement suffisante.
Pour la partie backend, j’ai choisi d’utiliser les Lambda d’AWS 🔗. L’un des avantages de Lambda, c’est le coût. On paie uniquement à l’exécution (on a le droit à un million d’exécutions par mois pendant 12 mois).
Comme le backend sera utilisé principalement pour soumettre un formulaire de contact ainsi que mon formulaire de newsletter.
Autant dire qu’au début mon API ne sera pas énormément sollicitée, donc si je peux avoir un coût dérisoire, voire proche du nul, alors je prends !
Vous l’aurez donc deviné, mais je baisse drastiquement le coût de mon hébergement à 0 euro par an pour le moment.
J’ai fait un petit schéma pour vous montrer comment tout ce petit monde va communiquer ensemble :
Le package serverless
Il y a un dernier package sur lequel je n’ai volontairement pas parlé lors de mes explications sur mon backend.
Sachez d’abord que le package api et le package cron peuvent fonctionner de manière classique.
Je peux les faire tourner localement, bien sûr, mais ils peuvent également tourner n’importe où, comme sur fly.io 🔗, Heroku 🔗, Scalingo 🔗 ou Render 🔗.
Cependant, comme j’ai choisi d’héberger mon backend en mode lambda sur AWS, les packages actuels ne peuvent pas fonctionner dans l’état.
Pourquoi ? Parce que les lambdas fonctionnent de base de manière autonome, c’est-à-dire que tout doit être dans un seul et même fichier, y compris les dépendances.
J’ai dit “de base” parce que dans la pratique, il est possible d’externaliser les dépendances node_modules dans un bucket S3, par exemple, et de dire à la lambda où elles se trouvent, mais c’est assez fastidieux comme manipulation.
Bien entendu, la plupart des build en TypeScript/JavaScript ne sont pas en un seul fichier. Si l’on prend mon API NestJS, on obtient un dossier dist qui contient beaucoup de fichiers et qui fait référence au dossier node_modules:
Transformer son build en un fichier
Heureusement, il existe une solution open source de Vercel baptisée ncc 🔗 pour réaliser cette opération.
Et je dois dire que cette solution est assez impressionnante car elle ne se contente pas de prendre votre code et de le mettre tout en un seul fichier avec les node_modules.
Elle va beaucoup plus loin en minifiant, mais surtout elle n’importe que les dépendances que vous utilisez réellement dans votre code (un peu comme le cherry-pick de git). Vous obtenez ainsi un build optimisé et prêt à l’emploi pour votre lambda.
La lambda pour l’API
Transformer le build de l’API en un seul fichier ne suffit pas. Il faut exécuter l’API dans du code que les lambdas AWS peuvent comprendre.
Rien de bien sorcier en soi. Il suffit d’utiliser les bons packages AWS :
import { Handler, Context } from "aws-lambda";
import { Server } from "http";
import { createServer, proxy } from "aws-serverless-express";
import { eventContext } from "aws-serverless-express/middleware";
import { ExpressAdapter } from "@nestjs/platform-express";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const express = require("express");
import infra from "@backend/infrastructure";
import { CreateApp } from "@backend/api";
const binaryMimeTypes: string[] = [];
let cachedServer: Server;
async function bootstrapServer(): Promise<Server> {
if (!cachedServer) {
await infra();
const expressApp = express();
const nestApp = await CreateApp(new ExpressAdapter(expressApp));
nestApp.use(eventContext());
await nestApp.init();
cachedServer = createServer(expressApp, undefined, binaryMimeTypes);
}
return cachedServer;
}
export const handler: Handler = async (event: any, context: Context) => {
cachedServer = await bootstrapServer();
return await proxy(cachedServer, event, context, "PROMISE").promise;
};
On importe nos packages api et infrastructure (qui ont déjà été build dans leur dossier respectif), on bootstrape un peu différemment notre API pour l’utiliser dans notre handler, et le tour est joué !
Une fois que ce code est prêt, il suffit de le compiler avec la commande ncc :
ncc build -m -o dist/api src/api.ts
On obtient à la sortie un build prêt à être utilisé en mode lambda sur AWS.
Déploiement de la lambda API sur AWS
Pour faciliter le déploiement, c’est ma GitHub Action qui s’en occupe, mais elle fait appel à un utilitaire bien pratique nommé : https://www.serverless.com 🔗.
Le package npm serverless permet de faciliter le déploiement de la Lambda, mais également de tout l’environnement dont vous avez besoin pour faire fonctionner la lambda (Scheduled Task, API Gateway, etc.).
Utilisez Serverless, c’est un gain de temps considérable pour déployer dans AWS, mais aussi pour gérer ses environnements. Grâce à lui, vous pouvez déployer des stacks AWS pour vos previews, staging, ou production.
La configuration de votre stack et surtout de comment votre lambda doit se déployer se fait avec une configuration serverless.yml :
functions:
api:
handler: dist/api/index.handler
timeout: 30
events:
- http:
method: any
path: /{proxy+}
cors:
headers: "*"
origin: "*"
allowCredentials: true
Cette configuration me permet de spécifier que je veux, pour ma stack AWS, une API Gateway qui agira uniquement comme un proxy pour déclencher ma lambda API qui s’occupera de traiter les requêtes.
Et voici la partie de ma GitHub Action qui déploie en production, par exemple :
- name: Deploy Serverless (production)
run: npx serverless deploy --stage prod
working-directory: packages/serverless
if: needs.stage.outputs.stage_name == 'production'
C’est la commande npx serverless deploy
qui va permettre de déployer tout ce que je vous ai montré sur AWS.
Bien entendu, je ne me voyais pas récupérer tous mes articles, médias et commentaires manuellement. Il existe quelques dépôts qui permettent de récupérer le contenu de WordPress dans d’autres formats. Moi, je me suis tourné vers ce dépôt : https://github.com/lonekorean/wordpress-export-to-markdown 🔗.
Le principe est simple : exporter un zip de tout le blog WordPress, puis le lancer dans la moulinette de ce repo.
Résultat : on obtient un dossier qui contient tous les médias utilisés dans les articles et chacun des articles au format markdown, tout simplement !
Pour les commentaires de tous les articles, je me suis basé sur la documentation de CommentBox pour gérer l’importation.
Migration des données WordPress vers le nouveau blog
Bien entendu, je ne me voyais pas récupérer tous mes articles, médias et commentaires manuellement. Il existe quelques dépôts qui permettent de récupérer le contenu de WordPress dans d’autres formats.
Migration des articles
Moi, je me suis tourné vers ce dépôt : https://github.com/lonekorean/wordpress-export-to-markdown. 🔗
Le principe est simple : exporter un zip de tout le blog WordPress, puis le lancer dans la moulinette de ce repo.
Résultat : on obtient un dossier qui contient tous les médias utilisés dans les articles et chacun des articles au format markdown, tout simplement !
Migration des commentaires
Pour les commentaires de tous les articles, je me suis basé sur la documentation de CommentBox 🔗 pour gérer l’importation.
Migration des redirections
Le système de redirection est déjà intégré dans Astro, mais il faut que je récupère celles que j’ai gérées dans WordPress.
Comme je n’avais pas énormément de redirections à migrer, j’ai décidé de les gérer manuellement et cela se passe au niveau du fichier de config d’Astro :
redirects: {
"/debuter-guide-7-erreurs-a-eviter-pour-programmer": {
status: 301,
destination:
"/debuter-guide-7-erreurs-a-eviter-pour-bien-debuter-en-programmation",
},
"/category/non-classe": {
status: 301,
destination: "/category/methodologie",
},
"/7-erreurs-a-eviter-bien-debuter-programmation": {
status: 301,
destination: "/7-erreurs-a-eviter-pour-bien-debuter-en-programmation",
},
"/debuter-guide-7-erreurs-a-eviter-pour-bien-debuter-en-programmation": {
status: 301,
destination: "/debuter-en-programmation",
},
"/m3-journal-un-outil-anti-procrastination": {
status: 301,
destination: "/m3-journal-outil-anti-procrastination",
},
"/faut-il-devenir-un-developpeur-backend-frontend-ou-fullstack": {
status: 301,
destination: "/devenir-developpeur-backend-frontend-fullstack",
},
"/versionner-son-code-2": {
status: 301,
destination: "/versionner-son-code-git",
},
"/tag/developpement-weeb": {
status: 301,
destination: "/tag/developpement-web",
},
"tester-envoi-e-mails-dans-son-application-local": {
status: 301,
destination: "/tester-envoi-e-mails-application-locale-guide",
},
"/principales-erreurs-en-tant-que-developpeur": {
status: 301,
destination: "/principales-erreurs-developpeur-guide",
},
"/6-raisons-de-devenir-programmeur": {
status: 301,
destination: "/devenir-programmeur-raisons",
},
"/guide-des-principaux-langages-de-programmation": {
status: 301,
destination: "/principaux-langages-de-programmation",
},
"/github-copilot-ai": {
status: 301,
destination: "/github-copilot",
},
"/stage-de-developpeur-astuces": {
status: 301,
destination: "/stage-de-developpeur",
},
"/derniers-articles": {
status: 301,
destination: "/posts",
},
},
Epilogue
Il me reste une dernière chose à vous parler: la mise en ligne de mon nouveau blog.
Une fois le back et le front déployé, il manquait juste 4 choses à faire :
- Changer les liens sitemap dans ma Google Search Console
- Faire pointer mon nom de domaine dans Vercel
- Revoir ma séquence d’e-mails dans ActiveCampaign
- Vérifier que les commentaires s’affichent bien sur le site
Si vous avez des questions sur mon article, n’hésitez pas à me les soumettre.
Je me ferais une joie à vous répondre !