<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: William Beuil </title>
    <description>The latest articles on DEV Community by William Beuil  (@wbeuil).</description>
    <link>https://dev.to/wbeuil</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F165991%2Fd145a861-f758-44ef-8a4b-0a026a25b608.jpeg</url>
      <title>DEV Community: William Beuil </title>
      <link>https://dev.to/wbeuil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wbeuil"/>
    <language>en</language>
    <item>
      <title>À La Recherche Des Analytics</title>
      <dc:creator>William Beuil </dc:creator>
      <pubDate>Thu, 08 Jul 2021 18:04:09 +0000</pubDate>
      <link>https://dev.to/wbeuil/a-la-recherche-des-analytics-24no</link>
      <guid>https://dev.to/wbeuil/a-la-recherche-des-analytics-24no</guid>
      <description>&lt;p&gt;Ami lecteur, si vous êtes en train de lire cet article c'est que vous recherchez un moyen de mesurer le nombre de vues sur votre site internet. Certes, il existe Google Analytics. Malheureusement, bien que gratuit, ce n'est pas du tout un outil facile à utiliser. J'ai personnellement opté pour une alternative et je vais essayer de vous expliquer pourquoi j'ai choisi &lt;a href="https://plausible.io" rel="noopener noreferrer"&gt;Plausible Analytics&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  C'est quoi Plausible ?
&lt;/h2&gt;

&lt;p&gt;Plausible Analytics a été créé et est toujours maintenu par &lt;a href="https://twitter.com/ukutaht" rel="noopener noreferrer"&gt;Uku Taht&lt;/a&gt; et &lt;a href="https://twitter.com/markosaric" rel="noopener noreferrer"&gt;Marko Saric&lt;/a&gt;, originaires d'Estonie. C'est un service assez récent puisqu'il a été commencé en décembre 2018 et a été lancé officiellement en production en Avril 2019. C'est un outil qui semble être une alternative à Google Analytics simple et respectueuse de la vie privée.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ses points forts
&lt;/h2&gt;

&lt;p&gt;Ce que j'apprécie le plus dans Plausible, c'est avant tout un service &lt;strong&gt;open source&lt;/strong&gt;, c'est-à-dire que vous pouvez aller voir directement le code source voire y contribuer sur leur &lt;a href="https://github.com/plausible/analytics" rel="noopener noreferrer"&gt;projet GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;C'est simple à utiliser et à intégrer d'autant plus que le script est petit, &lt;strong&gt;moins de 1 KB&lt;/strong&gt;, et donc très rapide à charger.&lt;/p&gt;

&lt;p&gt;Ils n'utilisent en aucun cas les cookies, contrairement à Google Analytics, ce qui les rend &lt;strong&gt;compatible RGPD et PECR&lt;/strong&gt;. Dites au revoir aux bannières d'autorisations de cookies.&lt;/p&gt;

&lt;p&gt;Il n'y a &lt;strong&gt;aucun tracking des utilisateurs&lt;/strong&gt; entre leurs appareils, ni entre les sites visités et les applications utilisées, ce qui signifie qu'ils ne peuvent pas connaître ou comprendre le comportement de vos utilisateurs.&lt;/p&gt;

&lt;p&gt;Toutes les données collectées sont affichées ci-dessous. Comme vous pouvez le voir, il n'y a &lt;strong&gt;aucune donnée personnelle&lt;/strong&gt; collectée. Au final, Plausible mesure la tendance globale du trafic sur le site internet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwbeuil.com%2Fimages%2Fblogs%2Fplausible-data-points.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwbeuil.com%2Fimages%2Fblogs%2Fplausible-data-points.png" alt="Plausible Data Points" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En dernier, je ne suis pas obligé d'utiliser leur solution SAAS. En effet je peux &lt;strong&gt;l'héberger où je veux&lt;/strong&gt; et le mettre en accès public pour une envie de transparence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Héberger Plausible comme moi
&lt;/h2&gt;

&lt;p&gt;Pour ce petit projet, nous allons utiliser leur &lt;a href="https://github.com/plausible/hosting" rel="noopener noreferrer"&gt;solution&lt;/a&gt; clé en mains.&lt;/p&gt;

&lt;p&gt;Nous allons déployer l'infrastructure sur &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt; avec comme outil IaC (Infrastructure as Code) &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Le diagramme ci-dessous résume parfaitement l'architecture :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwbeuil.com%2Fimages%2Fblogs%2Fplausible-diagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwbeuil.com%2Fimages%2Fblogs%2Fplausible-diagram.png" alt="Plausible Diagram" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;D'abord, il nous faut un &lt;a href="https://aws.amazon.com/vpc" rel="noopener noreferrer"&gt;VPC&lt;/a&gt; qui va nous permettre d'y intégrer d'autres ressources dans un réseau privé et isolé.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour permettre au serveur Plausible d'avoir un accès à internet, nous devons le placer dans un sous-réseau (e.g. subnet en anglais) public. Pour ceux qui ne le savent pas, on dit qu'un sous-réseau est public s'il est associé à une table de routage (e.g. route table en anglais) comportant une route vers une passerelle internet (e.g. internet gateway en anglais).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"subnet"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"gw"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_default_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"rt"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default_route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default_route_table_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route"&lt;/span&gt; &lt;span class="s2"&gt;"r"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_default_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;gateway_id&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;destination_cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour sécuriser le serveur, nous avons besoin d'un groupe de sécurité (e.g. security group en anglais) qui agit en tant que pare-feu virtuel pour notre serveur afin de contrôler le trafic entrant, comme les requêtes HTTP, et sortant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_default_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"sg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"i_http"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;from_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;
  &lt;span class="nx"&gt;to_port&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_default_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"e_all"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"egress"&lt;/span&gt;
  &lt;span class="nx"&gt;from_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;to_port&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"-1"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"All protocols"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_default_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous aurons aussi besoin d'un accès SSH sur notre serveur afin de pouvoir configurer correctement Plausible. Pour faire simple on va autoriser toutes les IP à se connecter en SSH, mais sachez qu'en production il faut uniquement autoriser votre adresse IP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"i_ssh"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;from_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
  &lt;span class="nx"&gt;to_port&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_default_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous pouvons, ensuite, créer &lt;a href="https://aws.amazon.com/ec2" rel="noopener noreferrer"&gt;l'instance EC2&lt;/a&gt; dans laquelle nous allons installer Plausible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;                         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0b3e57ee3b63dd76b"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;associate_public_ip_address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"plausible"&lt;/span&gt;
  &lt;span class="nx"&gt;user_data&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"script.sh"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;root_block_device&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;volume_size&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
    &lt;span class="nx"&gt;volume_type&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gp2"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypted&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;delete_on_termination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La clé SSH a été créée avec la commande &lt;code&gt;aws ec2 create-key-pair --key-name plausible --query "KeyMaterial" --output text &amp;gt; plausible.pem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Je me suis permis d'y ajouter un script qui va automatiquement mettre à jour tous les paquets, et installer docker et docker-compose dans l'instance à son initialisation pour gagner du temps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
yum update &lt;span class="nt"&gt;-y&lt;/span&gt;
amazon-linux-extras &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker
service docker start
usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; docker ec2-user
chkconfig docker on
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/docker/compose/releases/latest/download/docker-compose-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/docker-compose
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/docker-compose
reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maintenant, nous avons plus qu'à suivre le &lt;a href="https://plausible.io/docs/self-hosting" rel="noopener noreferrer"&gt;guide officiel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On télécharge en premier leur projet :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/plausible/hosting/archive/master.tar.gz | &lt;span class="nb"&gt;tar &lt;/span&gt;xz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hosting-master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puis, nous devons ajouter les variables d'environnement requis dans le fichier &lt;code&gt;plausible-conf.env&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ADMIN_USER_EMAIL=XXX@gmail.com
ADMIN_USER_NAME=XXX
ADMIN_USER_PWD=XXX
BASE_URL=http://1.2.3.4:8000 = l'adresse IP publique de votre instance EC2
SECRET_KEY_BASE=XXX = une clé secrete aléatoire en base64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comme j'aimerais utiliser mon compte Gmail pour leur fonctionnalité d'emailing, il faut créer un &lt;a href="https://support.google.com/accounts/answer/185833?hl=fr" rel="noopener noreferrer"&gt;mot de passe d'application&lt;/a&gt; et ajouter les variables d'environnement suivantes :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SMTP_HOST_ADDR=smtp.gmail.com
SMTP_HOST_PORT=465
SMTP_USER_NAME=XXX@gmail.com
SMTP_USER_PWD=XXX = votre mot de passe d'application
SMTP_HOST_SSL_ENABLED=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enfin, nous pouvons lancer le serveur Plausible avec &lt;code&gt;docker-compose up -d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nous devrions pouvoir aller sur &lt;code&gt;http://1.2.3.4:8000&lt;/code&gt; dans notre navigateur préféré et voir la page d'authentification de Plausible. Une fois l'enregistrement terminé, nous devrions avoir le script à rajouter dans le tag &lt;code&gt;head&lt;/code&gt; de notre site pour commencer à mesurer le nombre de vues.&lt;/p&gt;

&lt;p&gt;Sur un site Next.js, vous pouvez mettre le script dans le fichier &lt;code&gt;_document.js&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyDocument&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getInitialProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInitialProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;initialProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;defer&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://1.2.3.4:8000/js/plausible.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Head&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NextScript&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/body&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Html&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parfait ! Nous pouvons maintenant retourner sur &lt;code&gt;http://1.2.3.4:8000&lt;/code&gt; et voir les vues s'accumuler.&lt;/p&gt;

&lt;h2&gt;
  
  
  En résumé
&lt;/h2&gt;

&lt;p&gt;Nous avons installé, configuré et déployé notre serveur Plausible sur AWS en utilisant Terraform. N'hésitez pas à suivre leur &lt;a href="https://plausible.io/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; si vous voulez davantage configurer votre serveur.&lt;/p&gt;

&lt;p&gt;Merci d'avoir lu l'article ! Si vous avez aimé ou s'il vous a aidé d'une certaine manière, pensez à le partager. Probablement que j'écrirai dans une deuxième partie comment y intégrer le SSL.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>terraform</category>
      <category>aws</category>
    </item>
    <item>
      <title>Make Analytics Great Again</title>
      <dc:creator>William Beuil </dc:creator>
      <pubDate>Thu, 01 Jul 2021 18:01:35 +0000</pubDate>
      <link>https://dev.to/wbeuil/make-analytics-great-again-67h</link>
      <guid>https://dev.to/wbeuil/make-analytics-great-again-67h</guid>
      <description>&lt;p&gt;Dear readers, if you are reading this article, it means you're looking for a way to measure the number of views of your website. You could easily use Google Analytics, even though it's free, but it's really hard to fully understand and use it. I decided to go with an alternative and I will try to explain why I chose &lt;a href="https://plausible.io" rel="noopener noreferrer"&gt;Plausible Analytics&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Plausible?
&lt;/h2&gt;

&lt;p&gt;Plausible Analytics was created and is still maintained by &lt;a href="https://twitter.com/ukutaht" rel="noopener noreferrer"&gt;Uku Taht&lt;/a&gt; and &lt;a href="https://twitter.com/markosaric" rel="noopener noreferrer"&gt;Marko Saric&lt;/a&gt; from Estonia. It is a fairly new web analytics service since it started in December 2018 and launched in April 2019. They say that it is a simple and privacy-friendly alternative to Google Analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Its strengths
&lt;/h2&gt;

&lt;p&gt;One of the main features I like about Plausible is that it is fully &lt;strong&gt;open source&lt;/strong&gt;. You can see exactly what the code looks like and even contribute to it if you want in their &lt;a href="https://github.com/plausible/analytics" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is so easy to integrate and since the script is &lt;strong&gt;only 1 KB&lt;/strong&gt;, it's lightning-fast!&lt;/p&gt;

&lt;p&gt;They do not use cookies whatsoever, compared to Google Analytics, which makes them &lt;strong&gt;GDPR and PECR compliant&lt;/strong&gt;. Say goodbye to the cookie banner.&lt;/p&gt;

&lt;p&gt;They're &lt;strong&gt;not tracking people&lt;/strong&gt; across devices nor across websites and apps which means they can't know or understand the behavior of your users.&lt;/p&gt;

&lt;p&gt;All collected data can be found below. As you can see there are &lt;strong&gt;no personal data&lt;/strong&gt; or personally identifiable information collected. At the end of the day, Plausible only tracks the overall trends in my website traffic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsye287qsyuxgtqwvcbm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsye287qsyuxgtqwvcbm.png" alt="Plausible Data Points" width="800" height="597"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Last but not least, I can &lt;strong&gt;self-host it wherever I want&lt;/strong&gt; and make it public so that everyone can see what I have access to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-host Plausible like me
&lt;/h2&gt;

&lt;p&gt;For this little project, we will use their up and running &lt;a href="https://github.com/plausible/hosting" rel="noopener noreferrer"&gt;template&lt;/a&gt; that has everything we need to boot up our Plausible server.&lt;/p&gt;

&lt;p&gt;We're gonna use &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; as our IaC (Infrastructure as Code) tool and we will deploy our infrastructure to &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What we're gonna build can be summarized with this diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcs35njrodpbjbvr7e4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqcs35njrodpbjbvr7e4u.png" alt="Plausible Diagram" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we need a &lt;a href="https://aws.amazon.com/vpc" rel="noopener noreferrer"&gt;VPC&lt;/a&gt; that lets us launch other AWS resources in an isolated virtual network that we can define as wanted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, to enable Plausible to have access to the internet we need to place it in a public-facing subnet. For those who don't know, if a subnet is associated with a route table that has a route to an internet gateway, it's known as a public subnet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"subnet"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"gw"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_default_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"rt"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default_route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default_route_table_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route"&lt;/span&gt; &lt;span class="s2"&gt;"r"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_default_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;gateway_id&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;destination_cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To help secure the web server, we need a security group with rules which allow the web server to respond to inbound HTTP requests from the internet while simultaneously allowing all outbound IPv4 traffic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_default_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"sg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"i_http"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;from_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;
  &lt;span class="nx"&gt;to_port&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_default_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"e_all"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"egress"&lt;/span&gt;
  &lt;span class="nx"&gt;from_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;to_port&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"-1"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"All protocols"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_default_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we need to configure the web server with several credentials, we need SSH access to it. To make it easy for this example, allow all IPV4 access to SSH but bear in mind that you should always allow only your IP address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"i_ssh"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;from_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
  &lt;span class="nx"&gt;to_port&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH"&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_default_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create now the &lt;a href="https://aws.amazon.com/ec2" rel="noopener noreferrer"&gt;EC2 instance&lt;/a&gt; where the web server will be launched into.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;                         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0b3e57ee3b63dd76b"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;associate_public_ip_address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"plausible"&lt;/span&gt;
  &lt;span class="nx"&gt;user_data&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"script.sh"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;root_block_device&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;volume_size&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
    &lt;span class="nx"&gt;volume_type&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"gp2"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypted&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;delete_on_termination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SSH key has been created with &lt;code&gt;aws ec2 create-key-pair --key-name plausible --query "KeyMaterial" --output text &amp;gt; plausible.pem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I created a specific script to automatically update all packages and to install docker and docker-compose in our EC2 instance to make it quicker to launch our Plausible server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
yum update &lt;span class="nt"&gt;-y&lt;/span&gt;
amazon-linux-extras &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker
service docker start
usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; docker ec2-user
chkconfig docker on
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/docker/compose/releases/latest/download/docker-compose-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/local/bin/docker-compose
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/docker-compose
reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can follow their &lt;a href="https://plausible.io/docs/self-hosting" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We need to download their repository first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/plausible/hosting/archive/master.tar.gz | &lt;span class="nb"&gt;tar &lt;/span&gt;xz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;hosting-master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need to add the required environment variables to &lt;code&gt;plausible-conf.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ADMIN_USER_EMAIL=XXX@gmail.com
ADMIN_USER_NAME=XXX
ADMIN_USER_PWD=XXX
BASE_URL=http://1.2.3.4:8000 = the public IP of your EC2 instance
SECRET_KEY_BASE=XXX = a random base64 secret key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since I want to use my Gmail account, I need to create a &lt;a href="https://support.google.com/accounts/answer/185833" rel="noopener noreferrer"&gt;Google App Password&lt;/a&gt; and then add below environment variables to the configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SMTP_HOST_ADDR=smtp.gmail.com
SMTP_HOST_PORT=465
SMTP_USER_NAME=XXX@gmail.com
SMTP_USER_PWD=XXX = your Google App Password
SMTP_HOST_SSL_ENABLED=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can start our Plausible server with &lt;code&gt;docker-compose up -d&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We should now be able to hit &lt;code&gt;http://1.2.3.4:8000&lt;/code&gt; in our favorite browser and see the login page of our Plausible server. At the end of the registration process, we should have the script that we would need to add to our website to start gathering page views.&lt;/p&gt;

&lt;p&gt;In a Next.js website, you can add the script in the &lt;code&gt;_document.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyDocument&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getInitialProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInitialProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;initialProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;defer&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://1.2.3.4:8000/js/plausible.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Head&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NextScript&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/body&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Html&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! We can now check back our Plausible server at &lt;code&gt;http://1.2.3.4:8000&lt;/code&gt; to see whether we're getting any visits to our website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;We've installed, configured, and deployed our Plausible server in AWS with Terraform. Don't hesitate to follow their &lt;a href="https://plausible.io/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to further configure your Plausible server.&lt;/p&gt;

&lt;p&gt;Thanks so much for reading! If you liked this post or it helped you in any way please consider sharing it. I will probably make a second blog post to upgrade our Plausible server to enable SSL requests.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>terraform</category>
      <category>aws</category>
    </item>
    <item>
      <title>Comment J'ai Fait Mon Blog Bilingue</title>
      <dc:creator>William Beuil </dc:creator>
      <pubDate>Fri, 11 Jun 2021 23:25:39 +0000</pubDate>
      <link>https://dev.to/wbeuil/comment-j-ai-fait-mon-blog-bilingue-dmo</link>
      <guid>https://dev.to/wbeuil/comment-j-ai-fait-mon-blog-bilingue-dmo</guid>
      <description>&lt;p&gt;C'est le tout premier article sur mon site, et pour commencer je vais vous expliquer comment j'ai créé mon blog. Je vais essentiellement parler des technologies utilisées et comment j'ai internationalisé le site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js
&lt;/h2&gt;

&lt;p&gt;En tant que développeur React, je suis parti sur le framework &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; pour mon blog. Cela fait plusieurs mois que je travaille avec et c'est juste incroyable. Plus besoin de passer du temps sur &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt;, les performances sont folles (e.g. regardez les statistiques sur &lt;a href="https://github.com/wbeuil/wbeuil.com/pull/26#issuecomment-846141619" rel="noopener noreferrer"&gt;ma dernière PR&lt;/a&gt;), mes pages sont dynamiquement générées en HTML, je peux utiliser les API Routes pour des besoins spécifiques comme du rendu d'images à la demande.&lt;/p&gt;

&lt;p&gt;Bref, je suis totalement amoureux de ce framework ! D'ailleurs, n'oubliez pas de vous inscrire pour la &lt;a href="https://nextjs.org/conf" rel="noopener noreferrer"&gt;Next.js Conf&lt;/a&gt; qui se tiendra en ligne.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSG
&lt;/h3&gt;

&lt;p&gt;Une des forces de Next.js c'est de pouvoir choisir sa méthode de récupération de données. Soit vous les récupérez au moment de build votre site (e.g. Static Site Generation), soit à la demande à chaque requête du site (e.g. Server Side Rendering).&lt;/p&gt;

&lt;p&gt;J'ai choisi la première pour des raisons simples. Toutes les pages de mon site sont statiques. On peut donc les créer en amont et gagner en performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetStaticProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`../locales/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;lngDict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  API Routes
&lt;/h3&gt;

&lt;p&gt;Laissez-moi vous dire que les &lt;a href="https://nextjs.org/docs/api-routes/introduction" rel="noopener noreferrer"&gt;API Routes&lt;/a&gt; c’est juste le &lt;strong&gt;feu&lt;/strong&gt; ! Connaissant plutôt bien &lt;a href="https://aws.amazon.com/lambda" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt;, la tendance serverless est réelle et on devrait tous tester des outils comme les Lambdas ou les &lt;a href="https://workers.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudfare Workers&lt;/a&gt;. Du coup, le simple fait d’intégrer ce genre d’outil c’est la cerise sur le gâteau, à consommer abondamment.&lt;/p&gt;

&lt;p&gt;Mes différentes façons d’utiliser cet outil :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Création d’un fichier SVG affichant les scores Lighthouse : &lt;a href="https://github.com/wbeuil/lh-svg" rel="noopener noreferrer"&gt;lh-svg&lt;/a&gt; ;&lt;/li&gt;
&lt;li&gt;Une API pour l’équipe FIFA Club Pro du FCSilmi : &lt;a href="https://github.com/wbeuil/fcsilmi" rel="noopener noreferrer"&gt;fcsilmi&lt;/a&gt; ;&lt;/li&gt;
&lt;li&gt;Authentification Spotify : &lt;a href="https://github.com/wbeuil/currently-playing" rel="noopener noreferrer"&gt;currently-playing&lt;/a&gt; ;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sur mon site, j’utilise les API Routes pour 2 choses. La première, cela me permet de faire des requêtes aux serveurs de &lt;a href="https://github.com/wbeuil" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; pour récupérer mes projets préférés et le graphique de contribution. La deuxième, cela me permet de créer à la demande une image pour mes articles de blog avec &lt;a href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;puppeteer-core&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getBlogs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;blogs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;mdx/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not Found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateBlogImageHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`image/png`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`public, immutable, no-transform, s-maxage=31536000, max-age=31536000`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not Found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vercel
&lt;/h3&gt;

&lt;p&gt;Mon site est déployé sur &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;. Au tout début, j’étais parti du postulat qu’étant à la création de Next.js, ce sont les mieux armés pour créer la meilleure intégration avec leur framework.&lt;/p&gt;

&lt;p&gt;Après utilisation avec plusieurs sites, et après avoir testé d’autres intégrations (e.g. Netlify notamment), j’ai maintenant la certitude que c'est la meilleure option d'hébergement pour Next.js. En 2 cliques vous pouvez héberger, non seulement votre projet Next.js, mais aussi d’autres projets (e.g. Vue, Angular). Avec &lt;a href="https://nextjs.org/blog/next-10" rel="noopener noreferrer"&gt;Next.js version 10&lt;/a&gt;, on peut avoir gratuitement, pour un projet, les &lt;a href="https://nextjs.org/analytics" rel="noopener noreferrer"&gt;Next.js Analytics&lt;/a&gt;. On peut y intégrer des noms de domaine. Leur outil de gestion de variables d’environnements secrètes fonctionne à merveille et sans couac. Et on peut faire des connexions avec des outils tiers comme des GitHubs Apps. Essayez par vous-mêmes, vous n’allez pas le regretter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwbeuil.com%2Fimages%2Fblogs%2Fblog-analytics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwbeuil.com%2Fimages%2Fblogs%2Fblog-analytics.png" alt="blog-analytics" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;J’ai découvert &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; en septembre dernier et depuis je ne peux m’en passer. Dès que je le peux, je l’utilise sans modération. Voilà mon humble avis sur le sujet :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Je peux rapidement créer une UI avec leurs utilitaires clés en main ;&lt;/li&gt;
&lt;li&gt;J’ai drastiquement diminué la taille de mon bundle CSS malgré mon faible niveau en CSS ;&lt;/li&gt;
&lt;li&gt;Je suis simplement plus productif avec ;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  MDX
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mdxjs.com/" rel="noopener noreferrer"&gt;MDX&lt;/a&gt;, c’est ce qui fait que l’expérience en tant qu’auteur est si parfaite. C’est une extension du Markdown qui nous permet d’écrire du JSX dans des fichiers Markdown. Plus simplement, on peut écrire et bénéficier des composants React dans ces fichiers.&lt;/p&gt;

&lt;p&gt;Pour un développeur comme moi qui écrit du Markdown à longueur de journée, c’est tellement bien. Pour ceux qui ne le savent pas, si vous utilisez régulièrement GitHub et GitLab, pour les pulls requests ou pour les issues, vous en écrivez.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Comment écrire le paragraphe du dessus en Markdown:&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;MDX&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://mdxjs.com/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;, c’est ce qui fait que l’expérience en tant qu’auteur est si parfaite. C’est une extension du Markdown qui nous permet d’écrire du JSX dans des fichiers Markdown. Plus simplement, on peut écrire et bénéficier des composants React dans ces fichiers.

Pour un développeur comme moi qui écrit du Markdown à longueur de journée, c’est tellement bien. Pour ceux qui ne le savent pas, si vous utilisez régulièrement GitHub et GitLab, pour les pulls requests ou pour les issues, vous en écrivez.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ci-dessous, un exemple de MDX copié du site officiel :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Hello, *world*!&lt;/span&gt;

Below is an example of JSX embedded in Markdown. &lt;span class="nt"&gt;&amp;lt;br&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="gs"&gt;**Try and change the background color!**&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;padding:&lt;/span&gt; &lt;span class="err"&gt;'20&lt;/span&gt;&lt;span class="na"&gt;px&lt;/span&gt;&lt;span class="err"&gt;',&lt;/span&gt; &lt;span class="na"&gt;backgroundColor:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;tomato&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;This is JSX&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  mdx-bundler
&lt;/h3&gt;

&lt;p&gt;Afin de faire fonctionner correctement Next.js avec des fichiers MDX, il faut pouvoir les compiler afin de les rendre utilisables en tant que composants React. Pour réaliser aux mieux cette tâche, j’utilise la nouvelle librairie &lt;a href="https://github.com/kentcdodds/mdx-bundler" rel="noopener noreferrer"&gt;mdx-bundler&lt;/a&gt; du célèbre &lt;a href="https://kentcdodds.com/" rel="noopener noreferrer"&gt;Kent C. Dodds&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Je n’ai pas vraiment d’expériences avec les autres librairies (e.g. &lt;a href="https://github.com/hashicorp/next-mdx-enhanced" rel="noopener noreferrer"&gt;next-mdx-enhanced&lt;/a&gt;, &lt;a href="https://github.com/hashicorp/next-mdx-remote" rel="noopener noreferrer"&gt;next-mdx-remote&lt;/a&gt;) donc je ne me permettrais pas de juger les pour et les contre de chacun.&lt;/p&gt;

&lt;p&gt;Afin de rendre l’expérience de lecture la plus agréable possible, j’ai essayé de customiser chaque composant de la page de l’article comme les titres et sous-titres, les images, les blocs de code ou les listes. D’autres composants viendront étoffer la liste au fur et à mesure.&lt;/p&gt;

&lt;p&gt;Je me suis énormément inspiré de merveilleux blogs, notamment :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://leerob.io/" rel="noopener noreferrer"&gt;Lee Robinson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.joshwcomeau.com/" rel="noopener noreferrer"&gt;Josh W Comeau&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/"&gt;DEV.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Frontmatter
&lt;/h3&gt;

&lt;p&gt;Ce blog n’aurait jamais vu le jour sans des meta données pour chaque article de blog. Et pour cela, j’utilise ce qu’on appelle les frontmatter.&lt;/p&gt;

&lt;p&gt;Utiles pour l’UI ou encore le SEO, on y écrit des pairs de clés-valeurs. Personnellement, je m’en sers pour le titre, la description, la date de publication, les tags ou encore la langue utilisée.&lt;/p&gt;

&lt;p&gt;Ci-dessous un exemple pour cet article de blog :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Comment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;J'ai&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Fait&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Mon&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Blog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bilingue"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Un&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tour&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;d'horizon&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;de&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;comment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;j'ai&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;créé&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mon&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;blog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bilingue.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Je&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;vais&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;essentiellement&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;parler&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;des&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;technologies&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;utilisées&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;et&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;comment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;j'ai&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;internationalisé&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;le&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;site."&lt;/span&gt;
&lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fr&lt;/span&gt;
&lt;span class="na"&gt;alternate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/blog/my-multilingual-blog&lt;/span&gt;
&lt;span class="na"&gt;isPublished&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;publishedAt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2021-05-22"&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;react&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nextjs&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tailwind&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mdx&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Le reste du contenu ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Internationalisation
&lt;/h2&gt;

&lt;p&gt;Depuis le début, je me suis juré de faire ce blog à la fois en anglais et en français. Heureusement avec la version 10 de Next.js, le développement de sites internationaux a été énormément facilité.&lt;/p&gt;

&lt;p&gt;On a simplement à ajouter dans le fichier &lt;code&gt;next.config.js&lt;/code&gt; la clé &lt;code&gt;i18n&lt;/code&gt; avec les langues souhaitées. Next.js va alors automatiquement commencer à rediriger les utilisateurs sur les bons chemins du site (e.g /fr pour les francophones et / pour les anglophones) grâce aux headers &lt;code&gt;Accept-Language&lt;/code&gt; de leur navigateur.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Configuration de mon site&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;localeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;J’utilise &lt;a href="https://github.com/StarpTech/next-localization" rel="noopener noreferrer"&gt;next-localization&lt;/a&gt; pour afficher les bonnes traductions dans mes composants React.&lt;/p&gt;

&lt;p&gt;Quand on travaille sur des sites internationaux, il faut énormément penser en amont à comment organiser le SEO (&lt;a href="https://en.wikipedia.org/wiki/Search_engine_optimization" rel="noopener noreferrer"&gt;Search Engine Optimization&lt;/a&gt;). Il faut donc être rigoureux sur la façon dont on organise le tag &lt;code&gt;head&lt;/code&gt; dans son HTML.&lt;/p&gt;

&lt;p&gt;J’ai essayé d’aller au maximum sur l’intégration des différentes langues. J'ai exposé 2 versions de flux RSS et de webmanifests en suivant au mieux les bonnes pratiques de &lt;a href="https://www.w3.org/TR/appmanifest/#internationalization" rel="noopener noreferrer"&gt;W3C&lt;/a&gt; et de &lt;a href="https://developers.google.com/search/docs/advanced/crawling/international-overview" rel="noopener noreferrer"&gt;Google Search Console&lt;/a&gt;. N’hésitez surtout pas à me laisser des commentaires.&lt;/p&gt;

&lt;h2&gt;
  
  
  C’est quoi la suite ?
&lt;/h2&gt;

&lt;p&gt;Je vais continuer à améliorer mon site, du point de vue du lecteur et de l’auteur. Si vous avez des idées de modifications, vous pouvez ouvrir une PR sur GitHub.&lt;/p&gt;

&lt;p&gt;Je viens à peine de commencer mon aventure de créateur de contenus et je remercie tous ceux que je suis sur les réseaux et avec qui j’apprends tous les jours :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/kentcdodds" rel="noopener noreferrer"&gt;Kent C. Dodds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/JoshWComeau" rel="noopener noreferrer"&gt;Josh W. Comeau&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/dabit3" rel="noopener noreferrer"&gt;Nader Dabit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/chantastic" rel="noopener noreferrer"&gt;Michael Chan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrouvez-moi pour du live-coding sur &lt;a href="https://twitch.tv/wbeuil" rel="noopener noreferrer"&gt;Twitch&lt;/a&gt; et bientôt sur &lt;a href="https://www.youtube.com/channel/UCSgiOYU5_eiRyzk4uyxCOZQ" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;. Abonnez-vous évidemment.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>tailwindcss</category>
      <category>markdown</category>
    </item>
    <item>
      <title>How I Made My Multilingual Blog</title>
      <dc:creator>William Beuil </dc:creator>
      <pubDate>Fri, 04 Jun 2021 21:32:12 +0000</pubDate>
      <link>https://dev.to/wbeuil/how-i-made-my-multilingual-blog-1fb8</link>
      <guid>https://dev.to/wbeuil/how-i-made-my-multilingual-blog-1fb8</guid>
      <description>&lt;p&gt;This is my very first blog post on my website, so for this one, I will try to explain how I came up with my blog. I will mostly explain the stack I used and how I tackled the internationalization of the whole website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js
&lt;/h2&gt;

&lt;p&gt;As a React developer, I decided to create my whole website and thus my blog with the &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; framework. It's been several months since I started using Next.js and it's just fantastic. I don't get to work extensively with &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt;, the performance is outstanding (e.g. look at these metrics on &lt;a href="https://github.com/wbeuil/wbeuil.com/pull/26#issuecomment-846141619" rel="noopener noreferrer"&gt;my latest PR&lt;/a&gt;), my pages are automatically generated as static HTML, I can use the API Routes for specific on-demand images.&lt;/p&gt;

&lt;p&gt;Well, I'm just in love with this framework! Don't forget to get your ticket for the online &lt;a href="https://nextjs.org/conf" rel="noopener noreferrer"&gt;Next.js Conf&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSG
&lt;/h3&gt;

&lt;p&gt;One of the main strengths of Next.js is its different methods of data fetching. You could either choose to fetch data at build-time (e.g. Static Site Generation) or on-demand on each request (e.g. Server Side Rendering).&lt;/p&gt;

&lt;p&gt;I chose the former one since it's only a simple website with blogs where pre-rendering at build-time is possible and thus increases the performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetStaticProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`../locales/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;lngDict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  API Routes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/api-routes/introduction" rel="noopener noreferrer"&gt;API Routes&lt;/a&gt; are simply &lt;strong&gt;awesome&lt;/strong&gt;. Coming from &lt;a href="https://aws.amazon.com/lambda" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt;, I think the serverless world should be learned and used extensively. Thus, having the luxury to have inside the Next.js framework a way to create and use easily a serverless function is just the cherry on the cake.&lt;/p&gt;

&lt;p&gt;Several use cases of mine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An endpoint to request an SVG file that displays Lighthouse scores: &lt;a href="https://github.com/wbeuil/lh-svg" rel="noopener noreferrer"&gt;lh-svg&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;An API for the FIFA Club Pro FCSilmi team: &lt;a href="https://github.com/wbeuil/fcsilmi" rel="noopener noreferrer"&gt;fcsilmi&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;Endpoints for Spotify authentication: &lt;a href="https://github.com/wbeuil/currently-playing" rel="noopener noreferrer"&gt;currently-playing&lt;/a&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On my website, I use the API Routes for two things. On one hand, to request my pinned repositories and my contribution graph on &lt;a href="https://github.com/wbeuil" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. On the other hand, to request on-demand images for my blog posts using &lt;a href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer"&gt;puppeteer-core&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blogs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getBlogs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;blogs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;mdx/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not Found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateBlogImageHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`image/png`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`public, immutable, no-transform, s-maxage=31536000, max-age=31536000`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not Found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vercel
&lt;/h3&gt;

&lt;p&gt;My website is deployed on &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;. As being the company behind Next.js, I initially expected them to have the best integration with their framework.&lt;/p&gt;

&lt;p&gt;But after using it for multiple sites, I have several things to say about their product. In 2 clicks, you can deploy not only your Next.js website but also a ton of other javascript-based websites. Since &lt;a href="https://nextjs.org/blog/next-10" rel="noopener noreferrer"&gt;Next.js 10&lt;/a&gt;, you can use freely, for just one website, their &lt;a href="https://nextjs.org/analytics" rel="noopener noreferrer"&gt;Next.js Analytics&lt;/a&gt;. You can configure custom domains, secret environment variables, integrate GitHub Apps to automate aspects of your workflow. Go try it for yourself, you won't be disappointed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwbeuil.com%2Fimages%2Fblogs%2Fblog-analytics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwbeuil.com%2Fimages%2Fblogs%2Fblog-analytics.png" alt="blog-analytics" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;I discovered &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; last September and since then, whenever I can, I use it. Here are my two cents on this CSS framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can rapidly build UI with already existing utility classes;&lt;/li&gt;
&lt;li&gt;I decreased my CSS bundle size which means higher performance overall despite my not-so-good CSS skills;&lt;/li&gt;
&lt;li&gt;I just feel more productive;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  MDX
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mdxjs.com/" rel="noopener noreferrer"&gt;MDX&lt;/a&gt; is what powers this blog in terms of writing experience. It is an extension of the Markdown that lets us seamlessly write JSX in our Markdown files. Basically, we can use and benefit from React components inside a Markdown file.&lt;/p&gt;

&lt;p&gt;For a developer like me, write in Markdown is a daily habit. Indeed, if you use GitHub or GitLab for your pull requests, then you've already written in Markdown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# How I write the above paragraph in Markdown:&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;MDX&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://mdxjs.com/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; is what powers this blog in terms of writing experience. It is an extension of the Markdown that lets us seamlessly write JSX in our Markdown files. Basically, we can use and benefit from React components inside a Markdown file.

For a developer like me, write in Markdown is a daily habit. Indeed, if you use GitHub or GitLab for your pull requests, then you've already written in Markdown.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's an example with MDX copy from the official website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Hello, *world*!&lt;/span&gt;

Below is an example of JSX embedded in Markdown. &lt;span class="nt"&gt;&amp;lt;br&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="gs"&gt;**Try and change the background color!**&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;padding:&lt;/span&gt; &lt;span class="err"&gt;'20&lt;/span&gt;&lt;span class="na"&gt;px&lt;/span&gt;&lt;span class="err"&gt;',&lt;/span&gt; &lt;span class="na"&gt;backgroundColor:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;tomato&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;This is JSX&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  mdx-bundler
&lt;/h3&gt;

&lt;p&gt;To make Next.js works with MDX, we need to compile and bundle the markdown files. For this task, I decided to go with the new &lt;a href="https://github.com/kentcdodds/mdx-bundler" rel="noopener noreferrer"&gt;mdx-bundler&lt;/a&gt; library made by the famous &lt;a href="https://kentcdodds.com/" rel="noopener noreferrer"&gt;Kent C. Dodds&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I don't have any experiences with the other libraries (e.g. &lt;a href="https://github.com/hashicorp/next-mdx-enhanced" rel="noopener noreferrer"&gt;next-mdx-enhanced&lt;/a&gt;, &lt;a href="https://github.com/hashicorp/next-mdx-remote" rel="noopener noreferrer"&gt;next-mdx-remote&lt;/a&gt;) so I won't judge the pros and cons of one compared to the others.&lt;/p&gt;

&lt;p&gt;To increase the reader experience, I tried to customize as much as possible every component that renders the final page like the headings with custom anchor link, lists (unordered and ordered), images, codes, inline codes etc... More to come obviously with all the blog posts I want to write.&lt;/p&gt;

&lt;p&gt;I was greatly inspired by awesome blogs I follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://leerob.io/" rel="noopener noreferrer"&gt;Lee Robinson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.joshwcomeau.com/" rel="noopener noreferrer"&gt;Josh W Comeau&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/"&gt;DEV.to&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Frontmatter
&lt;/h3&gt;

&lt;p&gt;What makes the whole blog complete is metadata. One way is the frontmatter.&lt;/p&gt;

&lt;p&gt;I use them for UI and SEO purposes because I can store key-value pairs like title, description, publication date, tags and language.&lt;/p&gt;

&lt;p&gt;Here's what this blog post looks like in terms of frontmatter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;How&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Made&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;My&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Multilingual&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Blog"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;overview&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;how&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;came&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;my&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;multilingual&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;blog.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;will&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mostly&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;explain&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stack&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;used&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;how&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tackled&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;internationalization&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;whole&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;website."&lt;/span&gt;
&lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en&lt;/span&gt;
&lt;span class="na"&gt;alternate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/blog/mon-blog-bilingue&lt;/span&gt;
&lt;span class="na"&gt;isPublished&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;publishedAt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2021-05-22"&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;react&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nextjs&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tailwind&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mdx&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Content here ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Internationalization
&lt;/h2&gt;

&lt;p&gt;Since the beginning, I knew I would like my blog to be both in french and in english. The latest major version, Next.js 10, released in October 2020, makes it even easier to create internationalized websites.&lt;/p&gt;

&lt;p&gt;You just need to add in your &lt;code&gt;next.config.js&lt;/code&gt; file, the &lt;code&gt;i18n&lt;/code&gt; key with your languages and Next.js will start &lt;a href="https://nextjs.org/blog/next-10#subpath-routing" rel="noopener noreferrer"&gt;Subpath routing&lt;/a&gt; your user based on their &lt;code&gt;Accept-Language&lt;/code&gt; header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Configuration for my website&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;locales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;localeDetection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I work with &lt;a href="https://github.com/StarpTech/next-localization" rel="noopener noreferrer"&gt;next-localization&lt;/a&gt; to send the right translations to my child components.&lt;/p&gt;

&lt;p&gt;When you work on an internationalized website, you need to think in terms of SEO (&lt;a href="https://en.wikipedia.org/wiki/Search_engine_optimization" rel="noopener noreferrer"&gt;Search Engine Optimization&lt;/a&gt;) and thus you have to be thorough on what you display in your HTML &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;I tried to push to the extreme the internationalization by having both english and french RSS feeds, webmanifets and to change accordingly in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag the relevant &lt;code&gt;link&lt;/code&gt;. As much as possible, I tried to follow the recommended practices as per &lt;a href="https://developers.google.com/search/docs/advanced/crawling/international-overview" rel="noopener noreferrer"&gt;Google Search Console&lt;/a&gt; or &lt;a href="https://www.w3.org/TR/appmanifest/#internationalization" rel="noopener noreferrer"&gt;W3C&lt;/a&gt;. All feedbacks are welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I will continue to improve my website, both from the writer and from the user perspective. PRs are welcome if you want to be part of this adventure.&lt;/p&gt;

&lt;p&gt;I've just started my creator journey and for that, I'm so thankful to all the creators I follow, from whom I learnt and still learn a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/kentcdodds" rel="noopener noreferrer"&gt;Kent C. Dodds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/JoshWComeau" rel="noopener noreferrer"&gt;Josh W. Comeau&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/dabit3" rel="noopener noreferrer"&gt;Nader Dabit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/chantastic" rel="noopener noreferrer"&gt;Michael Chan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I started to live streaming my developer adventure on &lt;a href="https://twitch.tv/wbeuil" rel="noopener noreferrer"&gt;Twitch&lt;/a&gt;, and hopefully, soon I will release my first video on &lt;a href="https://www.youtube.com/channel/UCSgiOYU5_eiRyzk4uyxCOZQ" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>tailwindcss</category>
      <category>markdown</category>
    </item>
  </channel>
</rss>
