<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Entre Compétents blog Blog</title>
        <link>https://entrecompetents.fr/blog/fr/</link>
        <description>Entre Compétents blog Blog</description>
        <lastBuildDate>Mon, 09 Feb 2026 19:25:36 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>fr</language>
        <item>
            <title><![CDATA[Tous les types possibles dans une table SurrealDB avec schéma]]></title>
            <link>https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types</link>
            <guid>https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types</guid>
            <pubDate>Mon, 09 Feb 2026 19:25:36 GMT</pubDate>
            <description><![CDATA[Cet article liste tous les types disponibles dans une table SurrealDB avec schéma et comment les utiliser.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="guide complet surreal db schéma" src="https://entrecompetents.fr/blog/fr/assets/images/surrealdb-types-6ba11cb2c958bfad130a5c56cb566af8.png" width="1200" height="480" class="img_ev3q"></p>
<p>SurrealDB est une base de données puissante, dont la version 1.0 a été lancée en 2023. C'est une nouvelle génération de base de données, qui peut être avec ou sans schéma .Dans cet article, nous nous concentrerons sur la partie avec schéma de SurrealDB.</p>
<p>Même si la documentation officielle de SurrealDB est assez complète concernant les requêtes du langage SurrealQL, elle manque un peu d'informations sur les types disponibles dans une table avec schéma. Cet article vise à combler cette lacune.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="rappel-sur-la-création-dune-table-et-dun-champ">Rappel sur la création d'une table et d'un champ<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#rappel-sur-la-cr%C3%A9ation-dune-table-et-dun-champ" class="hash-link" aria-label="Lien direct vers Rappel sur la création d'une table et d'un champ" title="Lien direct vers Rappel sur la création d'une table et d'un champ">​</a></h2>
<p>Pour créer une table dans SurrealDB, vous devez avoir déjà défini une base de données et un espace de noms. Ensuite, vous pouvez créer une table avec la commande suivante :</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE TABLE product SCHEMAFULL;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Puis, pour définir un champ dans une table, vous pouvez utiliser la commande suivante :</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD my_field ON TABLE product TYPE data_type;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h1>Types disponibles dans une table SurrealDB avec schéma</h1>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="types-de-base">Types de base<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#types-de-base" class="hash-link" aria-label="Lien direct vers Types de base" title="Lien direct vers Types de base">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="any">Any<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#any" class="hash-link" aria-label="Lien direct vers Any" title="Lien direct vers Any">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD my_field ON TABLE product TYPE any;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="booléens">Booléens<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#bool%C3%A9ens" class="hash-link" aria-label="Lien direct vers Booléens" title="Lien direct vers Booléens">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD active ON TABLE product TYPE bool;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="nombres">Nombres<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#nombres" class="hash-link" aria-label="Lien direct vers Nombres" title="Lien direct vers Nombres">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="tout-nombre">Tout nombre<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#tout-nombre" class="hash-link" aria-label="Lien direct vers Tout nombre" title="Lien direct vers Tout nombre">​</a></h4>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD price ON TABLE product TYPE number;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="entier">Entier<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#entier" class="hash-link" aria-label="Lien direct vers Entier" title="Lien direct vers Entier">​</a></h4>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD price ON TABLE product TYPE int;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="flottant">Flottant<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#flottant" class="hash-link" aria-label="Lien direct vers Flottant" title="Lien direct vers Flottant">​</a></h4>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD price ON TABLE product TYPE float;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="décimal">Décimal<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#d%C3%A9cimal" class="hash-link" aria-label="Lien direct vers Décimal" title="Lien direct vers Décimal">​</a></h4>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD price ON TABLE product TYPE decimal;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="null">Null<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#null" class="hash-link" aria-label="Lien direct vers Null" title="Lien direct vers Null">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD price ON TABLE product TYPE null;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="string">String<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#string" class="hash-link" aria-label="Lien direct vers String" title="Lien direct vers String">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD name ON TABLE product TYPE string;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="bytes">Bytes<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#bytes" class="hash-link" aria-label="Lien direct vers Bytes" title="Lien direct vers Bytes">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD icon ON TABLE product TYPE bytes;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="uuid">Uuid<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#uuid" class="hash-link" aria-label="Lien direct vers Uuid" title="Lien direct vers Uuid">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD ext_id ON TABLE product TYPE uuid;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="types-relatifs-à-la-date-et-la-durée">Types relatifs à la date et la durée<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#types-relatifs-%C3%A0-la-date-et-la-dur%C3%A9e" class="hash-link" aria-label="Lien direct vers Types relatifs à la date et la durée" title="Lien direct vers Types relatifs à la date et la durée">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="datetime">Datetime<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#datetime" class="hash-link" aria-label="Lien direct vers Datetime" title="Lien direct vers Datetime">​</a></h4>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD created_at ON TABLE product TYPE datetime;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="duration">Duration<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#duration" class="hash-link" aria-label="Lien direct vers Duration" title="Lien direct vers Duration">​</a></h4>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD duration ON TABLE product TYPE duration;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="point">Point<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#point" class="hash-link" aria-label="Lien direct vers Point" title="Lien direct vers Point">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD location ON TABLE product TYPE point;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="record">Record<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#record" class="hash-link" aria-label="Lien direct vers Record" title="Lien direct vers Record">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD created_by ON TABLE product TYPE record;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="types-composés">Types composés<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#types-compos%C3%A9s" class="hash-link" aria-label="Lien direct vers Types composés" title="Lien direct vers Types composés">​</a></h2>
<p>Les types composés sont formés en combinant des types de base avec <code>composed_type&lt;type&gt;</code>.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="type-optionnel">Type optionnel<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#type-optionnel" class="hash-link" aria-label="Lien direct vers Type optionnel" title="Lien direct vers Type optionnel">​</a></h3>
<p>Par défaut, tous les champs sont obligatoires. Si vous voulez rendre un champ optionnel, vous pouvez utiliser le type <code>optional</code>.</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD description ON TABLE product TYPE option&lt;string&gt;;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="type-multiple">Type multiple<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#type-multiple" class="hash-link" aria-label="Lien direct vers Type multiple" title="Lien direct vers Type multiple">​</a></h3>
<p>Si vous voulez autoriser plusieurs types pour un champ, vous pouvez utiliser le type <code>either</code>.</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD additional_propertie ON TABLE product TYPE string|number;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="tableau">Tableau<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#tableau" class="hash-link" aria-label="Lien direct vers Tableau" title="Lien direct vers Tableau">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD tags ON TABLE product TYPE array&lt;string&gt;;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="set">Set<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#set" class="hash-link" aria-label="Lien direct vers Set" title="Lien direct vers Set">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD tags ON TABLE product TYPE set&lt;string&gt;;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="géometrie">Géometrie<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#g%C3%A9ometrie" class="hash-link" aria-label="Lien direct vers Géometrie" title="Lien direct vers Géometrie">​</a></h3>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DEFINE FIELD location ON TABLE product TYPE geometry&lt;line&gt;;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Dans ce cas, les seuls types autorisés sont ceux définis dans la <a href="https://surrealdb.com/docs/surrealdb/surrealql/datamodel/geometries" target="_blank" rel="noopener noreferrer">documentation de SurrealDB</a>. Ces types respectent le <a href="https://datatracker.ietf.org/doc/html/rfc7946" target="_blank" rel="noopener noreferrer">format GeoJSON (défini dans rfc7946)</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="cours-complet-sur-surrealdb">Cours complet sur SurrealDB<a href="https://entrecompetents.fr/blog/fr/surrealdb-schemafull-types#cours-complet-sur-surrealdb" class="hash-link" aria-label="Lien direct vers Cours complet sur SurrealDB" title="Lien direct vers Cours complet sur SurrealDB">​</a></h2>
<p>Si vous voulez en savoir plus sur SurrealDB, vous pouvez vous abonner à notre newsletter pour être informé du cours que nous préparons. Et bénéficier d'une réduction lors de sa sortie.</p>
<!-- -->
<iframe width="540" height="705" src="https://23a11111.sibforms.com/serve/MUIFAD4V-sjfjj3lAophYwHVb-SESnxq_HWg-plzTaW8rgGeG-yPYbFI8KeEg39tUVNQ0tVKOF2eoSlFHchztAiI2XzH7XA-UDWaIG5-vEuh7julQgBEdrhXjqYn4v46O7UC1yPmaf2cjB_H-wmJKE-QwzAgO3ct-Vq5KanudcV5GMHfGH9Z5mg6j0wa44i9vdecdOB32qdMHcFu" frameborder="0" scrolling="auto" style="display:block;margin-left:auto;margin-right:auto;max-width:100%"></iframe>]]></content:encoded>
            <category>cloud</category>
            <category>dev</category>
            <category>database</category>
        </item>
        <item>
            <title><![CDATA[Comment créer un SaaS nextjs + mongoDB entièrement auto-hébergé]]></title>
            <link>https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft</link>
            <guid>https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft</guid>
            <pubDate>Mon, 09 Feb 2026 19:25:36 GMT</pubDate>
            <description><![CDATA[Comment j'ai créé un SaaS avec NextJS + MongoDB, entièrement auto-hébergé en utilisant Docker et Coolify.]]></description>
            <content:encoded><![CDATA[<p>Je voulais construire un SaaS permettant aux utilisateurs de visualiser, éditer et partager facilement des fichiers docker-compose. Si vous ne connaissez rien à Docker, ce n'est pas grave. Cet article est centré sur la stack technologique du SaaS.</p>
<p>Mes besoins étaient les suivants :</p>
<ul>
<li>Un système utilisateur</li>
<li>Des analyses</li>
<li>Un système de blog</li>
<li>Un système de documentation</li>
<li>Une base de données me permettant de tester rapidement de nouvelles choses</li>
<li>La stack technologique doit être facilement auto-hébergeable</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="choisir-une-base-de-données">Choisir une base de données<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#choisir-une-base-de-donn%C3%A9es" class="hash-link" aria-label="Lien direct vers Choisir une base de données" title="Lien direct vers Choisir une base de données">​</a></h2>
<p>Le choix de la base de données est délicat. Dois-je opter pour SQL ou NoSQL ?</p>
<p>À mon avis, nous devrions choisir une base de données qui s'adapte à la forme de nos données, et non modifier nos données pour les faire rentrer dans la base de données.
Et si vous connaissez un peu les docker-composes, c'est une structure de données très complexe à stocker, car assez incohérente (je veux dire, même si c'est basé sur YAML, il existe tellement de possibilités différentes). Le stocker dans une base de données SQL serait un cauchemar, la seule "solution facile" serait de le considérer comme du texte brut et de le stocker dans une cellule texte (ou octets, peu importe).</p>
<p>Oui, je pourrais utiliser la cellule JSON de PostgreSQL pour créer un moyen de le stocker commodément, mais rappelez-vous de notre besoin : <code>garder les choses simples</code>.</p>
<p>J'ai donc choisi NoSQL pour sa capacité à stocker des données non structurées.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="mais-quelle-base-de-données-nosql-">Mais quelle base de données NoSQL ?<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#mais-quelle-base-de-donn%C3%A9es-nosql-" class="hash-link" aria-label="Lien direct vers Mais quelle base de données NoSQL ?" title="Lien direct vers Mais quelle base de données NoSQL ?">​</a></h3>
<p>Choisir une base de données NoSQL n'est que la moitié du chemin pour choisir une base de données. Il reste encore tellement de questions à se poser.
Mais rappelez-vous toujours ceci quand vous êtes une petite équipe ou un développeur solo : <code>garder les choses simples</code>.</p>
<p>Je vais donc me concentrer sur ces questions :</p>
<ul>
<li>Existe-t-il un driver TypeScript facile à utiliser ?</li>
<li>La base de données est-elle sécurisée ?</li>
<li>Puis-je l'auto-héberger ?</li>
<li>Y a-t-il une grande communauté ?</li>
</ul>
<p>J'ai pensé à SurrealDB et MongoDB. Ils ont tous deux un SDK TS, n'ont pas de nouvelles CVE chaque matin, et sont entièrement auto-hébergeables.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="surrealdb">SurrealDB<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#surrealdb" class="hash-link" aria-label="Lien direct vers SurrealDB" title="Lien direct vers SurrealDB">​</a></h4>
<p>SurrealDB pourrait être une très bonne option, car il peut être utilisé comme une base de données SQL pour ma partie utilisateur et NoSQL pour mon traitement de données et mes tâches.</p>
<p>Mais le grand inconvénient est le manque de maturité. Même si les développeurs sont rapides pour répondre aux problèmes soumis, il n'y a pas beaucoup de ressources sur Stack Overflow ou d'autres espaces de connaissances à son sujet.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="le-choix-final--mongodb">Le choix final : MongoDB<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#le-choix-final--mongodb" class="hash-link" aria-label="Lien direct vers Le choix final : MongoDB" title="Lien direct vers Le choix final : MongoDB">​</a></h4>
<p>J'ai finalement choisi MongoDB car il me permettrait de modifier rapidement le modèle de données de mes entités, et il existe une documentation et des connaissances beaucoup plus importantes à son sujet.</p>
<p>À ce stade, je savais que j'utiliserais NextJS + MongoDB, mais qu'en est-il du système de blog, de la documentation et des analyses ?</p>
<p><img decoding="async" loading="lazy" alt="illustration" src="https://entrecompetents.fr/blog/fr/assets/images/archi-composecraft-1-191983d846af38f73bba71888598d068.png" width="653" height="242" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="la-partie-auto-hébergement">La partie auto-hébergement<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#la-partie-auto-h%C3%A9bergement" class="hash-link" aria-label="Lien direct vers La partie auto-hébergement" title="Lien direct vers La partie auto-hébergement">​</a></h2>
<p>Avant d'aller plus loin, j'ai commencé ma première version bêta avec très peu d'utilisateurs. J'avais donc besoin d'auto-héberger la stack.
Mes deux plus grandes préoccupations étaient : je veux que ce soit peu coûteux et facile.</p>
<p>J'ai choisi hetzner.com car ils sont vraiment peu coûteux et fiables. 4,51 € /mois pour 2 CPU, 4 Go de RAM, 20 To de bande passante et 40 Go de SSD.
Pour gérer mon serveur privé virtuel, j'ai choisi <a href="https://coolify.io/" target="_blank" rel="noopener noreferrer">Coolify</a>, car c'est entièrement open-source et me permet de déployer facilement de nouveaux services.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="les-analyses-le-blog-et-la-documentation-">Les analyses, le blog et la documentation :<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#les-analyses-le-blog-et-la-documentation-" class="hash-link" aria-label="Lien direct vers Les analyses, le blog et la documentation :" title="Lien direct vers Les analyses, le blog et la documentation :">​</a></h2>
<p>Le choix des analyses était très simple pour moi car j'utilise <a href="https://umami.is/" target="_blank" rel="noopener noreferrer">Umami Analytics</a> depuis 2020. C'est entièrement auto-hébergé, open source et conforme au RGPD.</p>
<p>Mes besoins en matière de blog étaient un peu particuliers car je voulais quelque chose de headless avec une interface d'administration et auto-hébergeable.
J'aurais pu opter pour Strapi, mais j'ai finalement choisi Directus sans raison particulière, les deux pouvant parfaitement convenir à mon cas d'utilisation.</p>
<p>Concernant la documentation, mon choix a été guidé par : <code>peu coûteux et rapide</code>. J'aurais donc pu choisir n'importe quelle solution de documentation basée sur Markdown. Voici quelques bons choix :</p>
<ul>
<li>Docusaurus</li>
<li>Hugo</li>
<li>mkDocs</li>
<li>markdoc</li>
</ul>
<p>J'ai opté pour Docusaurus car il est basé sur React, et à l'avenir, si nécessaire, je pourrais facilement ajouter des composants personnalisés.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="ny-a-t-il-pas-de-problèmes-de-référencement-en-utilisant-différentes-technologies-pour-la-documentation-et-le-blog-">N'y a-t-il pas de problèmes de référencement en utilisant différentes technologies pour la documentation et le blog ?<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#ny-a-t-il-pas-de-probl%C3%A8mes-de-r%C3%A9f%C3%A9rencement-en-utilisant-diff%C3%A9rentes-technologies-pour-la-documentation-et-le-blog-" class="hash-link" aria-label="Lien direct vers N'y a-t-il pas de problèmes de référencement en utilisant différentes technologies pour la documentation et le blog ?" title="Lien direct vers N'y a-t-il pas de problèmes de référencement en utilisant différentes technologies pour la documentation et le blog ?">​</a></h4>
<p>Pour éviter les problèmes de référencement associés aux sites web multi-domaines comme <code>composecraft.com</code>, <code>docs.composecraft.com</code>, <code>blog.composecraft.com</code>.</p>
<p>J'ai opté pour une approche différente :</p>
<p>Premièrement, mon système de blog est plus un système de bibliothèque. Directus m'aide uniquement à gérer les entrées, mes données contiennent un champ markdown qui alimente directement mon NextJS, donc pour la partie blog (ou bibliothèque comme vous voulez), tout est sous composecraft.com/library et NextJS le gère avec la bibliothèque next-remote-mdx.</p>
<p>Bien que j'aurais pu utiliser le même modèle pour ma documentation, j'ai choisi Docusaurus. Pourquoi ?
Parce que ma documentation va rapidement évoluer, et je ne veux pas me soucier des questions NextJS lors de la rédaction. Je veux simplement écrire des fichiers markdown sans me préoccuper de la réactivité mobile, des performances ou autre.
Utiliser Docusaurus impose automatiquement à ma documentation d'être structurée, facile à éditer et facile à utiliser par mes utilisateurs.</p>
<p>J'ai donc simplement configuré mon proxy inverse pour rediriger toute requête sous /doc vers le Docker Docusaurus géré par Coolify.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="le-proxy-inverse-">Le proxy inverse :<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#le-proxy-inverse-" class="hash-link" aria-label="Lien direct vers Le proxy inverse :" title="Lien direct vers Le proxy inverse :">​</a></h2>
<p>À mon avis, surtout pour choisir un proxy inverse pour un site web de faible à moyenne charge, choisissez celui que vous aimez. Il n'y a pas de choix "informatique" meilleur entre nginx, traefik, apache ou caddy tant que vous restez sur un serveur simple sans équilibrage de charge ou routage complexe.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="les-sauvegardes-">Les sauvegardes ?<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#les-sauvegardes-" class="hash-link" aria-label="Lien direct vers Les sauvegardes ?" title="Lien direct vers Les sauvegardes ?">​</a></h2>
<p>Comment m'assurer que ma base de données auto-hébergée ne disparaisse pas quand mon serveur va brûler à cause de mes trop nombreux utilisateurs ?</p>
<p>Jusqu'à présent, mon serveur est loin de brûler à cause de mes trop nombreux utilisateurs, mais quoi qu'il en soit, j'ai intégré un mécanisme de sauvegarde. Lequel ? Chaque jour à 3h du matin, une sauvegarde complète de la base de données est programmée et stockée sur un S3 distant chez un fournisseur cloud (différent de mon fournisseur de serveur privé virtuel pour éviter qu'ils ne placent le compartiment S3 dans le même centre de données que mon serveur de production).</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="dernière-préoccupation--le-cdn">Dernière préoccupation : le CDN<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#derni%C3%A8re-pr%C3%A9occupation--le-cdn" class="hash-link" aria-label="Lien direct vers Dernière préoccupation : le CDN" title="Lien direct vers Dernière préoccupation : le CDN">​</a></h3>
<p>En tant que petit produit utilisé dans le monde entier, j'ai été confronté à un problème : mes utilisateurs américains se plaignaient de la latence, et en effet, le serveur est situé en Europe.</p>
<p>Réponse facile : je pourrais avoir une instance aux États-Unis !</p>
<p>Mais... rappelez-vous que c'est un petit projet et je veux que ce soit <code>peu coûteux et facile</code>. J'ai donc choisi de faire appel à Cloudflare avec leur CDN gratuit. Oui, ce n'est pas une stack entièrement auto-hébergée respectueuse de la confidentialité. Mais pour une petite équipe de produit, c'est le meilleur compromis coût/performance que je pouvais faire.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="le-schéma-final-">Le schéma final :<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#le-sch%C3%A9ma-final-" class="hash-link" aria-label="Lien direct vers Le schéma final :" title="Lien direct vers Le schéma final :">​</a></h2>
<p><img decoding="async" loading="lazy" alt="architecture finale" src="https://entrecompetents.fr/blog/fr/assets/images/archi-composecraft-2-2a7b89cda2f3f07b5953ded5090644ab.png" width="1471" height="451" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="quelques-réflexions">Quelques réflexions<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#quelques-r%C3%A9flexions" class="hash-link" aria-label="Lien direct vers Quelques réflexions" title="Lien direct vers Quelques réflexions">​</a></h2>
<p>Aurais-je pu le construire plus simplement ?</p>
<p>Oui, définitivement : si mon client final était totalement non technique, j'aurais travaillé davantage sur la documentation et réutilisé directement Directus pour gérer ma documentation en même temps que mes bibliothèques. Et si le client ne veut absolument pas interagir avec un serveur Linux : utiliser MongoDB Atlas et des solutions cloud pour les analyses, et quelque chose comme Vercel, Netlify ou fly.io pour déployer mon application NextJS sans état.</p>
<p>Mais comme je suis un amateur de Linux et que je n'ai pas peur de mettre la main à la pâte, je pense que c'est la stack la plus économique et la plus facilement maintenable qui me convienne.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="envie-daller-plus-loin-">Envie d'aller plus loin ?<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft#envie-daller-plus-loin-" class="hash-link" aria-label="Lien direct vers Envie d'aller plus loin ?" title="Lien direct vers Envie d'aller plus loin ?">​</a></h2>
<p>Je vais bientôt créer des articles sur le pipeline CI/CD et un autre sur la logique interne de NextJS. Restez informé des nouveaux articles en vous abonnant à la newsletter ci-dessous :</p>
<!-- -->
<iframe width="540" height="705" src="https://23a11111.sibforms.com/serve/MUIFAD4V-sjfjj3lAophYwHVb-SESnxq_HWg-plzTaW8rgGeG-yPYbFI8KeEg39tUVNQ0tVKOF2eoSlFHchztAiI2XzH7XA-UDWaIG5-vEuh7julQgBEdrhXjqYn4v46O7UC1yPmaf2cjB_H-wmJKE-QwzAgO3ct-Vq5KanudcV5GMHfGH9Z5mg6j0wa44i9vdecdOB32qdMHcFu" frameborder="0" scrolling="auto" style="display:block;margin-left:auto;margin-right:auto;max-width:100%"></iframe>]]></content:encoded>
            <category>auto-hébergé</category>
            <category>développement</category>
        </item>
        <item>
            <title><![CDATA[Comment j'ai intégré react-flow avec la manipulation de données complexes et profondément imbriquées]]></title>
            <link>https://entrecompetents.fr/blog/fr/theStackBehindComposeCraftNext</link>
            <guid>https://entrecompetents.fr/blog/fr/theStackBehindComposeCraftNext</guid>
            <pubDate>Mon, 09 Feb 2026 19:25:36 GMT</pubDate>
            <description><![CDATA[Comment j'ai intégré react-flow avec la manipulation de données complexes et profondément imbriquées]]></description>
            <content:encoded><![CDATA[<p>Cet article fait suite à <a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraft">l'article "Comment créer un SaaS Next.js + MongoDB entièrement auto-hébergé"</a>.</p>
<p>Nous allons principalement nous concentrer sur la magie qui se passe au sein de l'application Next.js.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="les-besoins-">Les besoins :<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraftNext#les-besoins-" class="hash-link" aria-label="Lien direct vers Les besoins :" title="Lien direct vers Les besoins :">​</a></h2>
<p>Ce que nous voulons construire est une interface graphique nodale pour gérer les données de Docker Compose.</p>
<p>De ce besoin produit, nous pouvons extraire certaines réponses techniques :</p>
<ul>
<li>Une gestion efficace des données Docker Compose.</li>
<li>Un outil pour nous aider avec l'interface graphique nodale.</li>
<li>Un système de gestion d'état permettant des mutations complexes et profondément imbriquées, de manière efficace.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="choisir-une-bibliothèque-de-données-pour-gérer-docker-compose-efficacement">Choisir une bibliothèque de données pour gérer Docker Compose efficacement<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraftNext#choisir-une-biblioth%C3%A8que-de-donn%C3%A9es-pour-g%C3%A9rer-docker-compose-efficacement" class="hash-link" aria-label="Lien direct vers Choisir une bibliothèque de données pour gérer Docker Compose efficacement" title="Lien direct vers Choisir une bibliothèque de données pour gérer Docker Compose efficacement">​</a></h2>
<p>Docker Compose a une structure de données très complexe : elle est profondément imbriquée, et certains objets en bas de la hiérarchie sont liés à ceux du haut.
J'ai cherché une bibliothèque Docker Compose en TypeScript, mais n'en ai trouvé aucune, donc j'ai construit la mienne à partir de zéro.</p>
<p>Elle n'est actuellement pas open-source, mais nous avons <a href="https://form.composecraft.com/s/cm472o6vf000qwl0z6xs9zclh" target="_blank" rel="noopener noreferrer">un formulaire pour demander un accès</a>.</p>
<p>Cette bibliothèque centralise tous les états dans une grande classe <code>Compose</code>. En gérant l'état d'une seule instance de <code>Compose</code>, il devient simple de rester à jour avec toutes les données liées aux tâches.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="gestion-de-létat-avec-react">Gestion de l'état avec React<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraftNext#gestion-de-l%C3%A9tat-avec-react" class="hash-link" aria-label="Lien direct vers Gestion de l'état avec React" title="Lien direct vers Gestion de l'état avec React">​</a></h3>
<p>Dès le début, nous savions que nous devions gérer l'état de <code>Compose</code> côté client, car de nombreuses mutations d'état ont lieu sur la page du playground.</p>
<p>Mais quel outil choisir entre React UseState, Zustand, Recoil, Redux... ?</p>
<p>Nous avons choisi Zustand pour la gestion d'état grâce à sa bonne intégration avec Next.js et sa capacité à modifier l'état en passant une fonction plutôt qu'un nouvel objet.</p>
<p>Pourquoi cela importe-t-il ?<br>
<!-- -->Avec une approche traditionnelle, en utilisant <code>setData(newData)</code>, un nouvel objet Compose serait recréé à chaque mise à jour de l'état, comme ceci :</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">compose</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCompose</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useComposeState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">setCompose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Compose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">whatever you change</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Cela entraînerait une nouvelle instance de <code>Compose</code> à chaque fois, ce qui pourrait causer des inefficacités ou des re-rendus inutiles.</p>
<p>Avec Zustand, nous pouvons passer une fonction de modification, permettant d'appliquer directement les changements à l'objet existant sans en créer un nouveau :</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">compose</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCompose</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useComposeState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">setCompose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">oldCompose</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> oldCompose</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">whateverChange</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">anyParamsNeeded</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Cette approche permet des mises à jour ciblées tout en gardant l'instance intacte. Pour des changements plus précis, vous pouvez modifier directement les propriétés au sein de l'état :</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">compose</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> setCompose</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">useComposeState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">setCompose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">oldCompose</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    oldCompose</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">service1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">name</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"nouveau nom"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>En utilisant cette méthode, nous maintenons la simplicité et l'efficacité des mises à jour d'état sans compromettre l'intégrité de l'instance <code>Compose</code>.</p>
<p>Mais vous vous demandez peut-être, à quoi ressemble le store Zustand ?</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">"use client"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> create </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"zustand"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Compose </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@entrecompetents/composecraftLib"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> generateRandomName </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@/lib/utils"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">ComposeState</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    compose</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Compose</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">setCompose</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function-variable function" style="color:#d73a49">updater</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentCompose</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Compose</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> useComposeStore </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token generic-function function" style="color:#d73a49">create</span><span class="token generic-function generic class-name operator" style="color:#393A34">&lt;</span><span class="token generic-function generic class-name">ComposeState</span><span class="token generic-function generic class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">set</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    compose</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Compose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">generateRandomName</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">setCompose</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function-variable function" style="color:#d73a49">updater</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">currentCompose</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Compose</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">updater</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compose</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> compose</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compose </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="la-bibliothèque-gui-nodale">La bibliothèque GUI nodale<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraftNext#la-biblioth%C3%A8que-gui-nodale" class="hash-link" aria-label="Lien direct vers La bibliothèque GUI nodale" title="Lien direct vers La bibliothèque GUI nodale">​</a></h2>
<p>Les options étaient : <a href="https://www.jointjs.com/" target="_blank" rel="noopener noreferrer">JointJS</a>, <a href="https://reactflow.dev/" target="_blank" rel="noopener noreferrer">react flow</a>, <a href="http://mermaid.js.org/" target="_blank" rel="noopener noreferrer">mermaidJS</a> (peut-être d'autres, mais nous n'avons pas exploré plus loin).</p>
<p>Voici un petit tableau récapitulatif de notre choix :</p>
<table><thead><tr><th></th><th>JointJS</th><th>React Flow</th><th>mermaidJS</th></tr></thead><tbody><tr><td>Interactivité</td><td>✅</td><td>✅</td><td>❌</td></tr><tr><td>Facilité de développement</td><td>⭐️</td><td>?</td><td>⭐️⭐️⭐️</td></tr><tr><td>Open source</td><td>✅</td><td>❌</td><td>✅</td></tr></tbody></table>
<p>Nous avons finalement choisi React Flow pour son interactivité et le fait qu'il soit open source.</p>
<p>Cependant, gardez à l'esprit que ce n'est pas la voie la plus simple, car React Flow nécessite beaucoup de développement personnalisé.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="le-schéma-du-playground-">Le schéma du playground :<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraftNext#le-sch%C3%A9ma-du-playground-" class="hash-link" aria-label="Lien direct vers Le schéma du playground :" title="Lien direct vers Le schéma du playground :">​</a></h2>
<p><img decoding="async" loading="lazy" alt="schéma" src="https://entrecompetents.fr/blog/fr/assets/images/howDoesItWorkInNextJS-18035ee61c509f5f56558acc95970552.png" width="1471" height="972" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion-">Conclusion :<a href="https://entrecompetents.fr/blog/fr/theStackBehindComposeCraftNext#conclusion-" class="hash-link" aria-label="Lien direct vers Conclusion :" title="Lien direct vers Conclusion :">​</a></h2>
<p>Créer une interface graphique nodale pour gérer les données Docker Compose dans une application Next.js est un processus à plusieurs niveaux combinant gestion d'état efficace, une bibliothèque GUI robuste, et des outils développés sur mesure. À travers cette exploration, nous avons :</p>
<ol>
<li><strong>Créé une bibliothèque TypeScript</strong> adaptée à la complexité et à la nature imbriquée de la structure Docker Compose.</li>
<li><strong>Optimisé la gestion de l'état client</strong> avec Zustand, permettant des mises à jour efficaces sans recréer d'objets ou d'instances entiers.</li>
<li><strong>Choisi React Flow</strong> comme bibliothèque GUI nodale, en exploitant son interactivité et sa personnalisation, tout en surmontant une courbe d'apprentissage importante avec des développements ciblés.</li>
</ol>
<p>Cette intégration, représentée dans le schéma final, montre un flux de travail rationalisé où les modifications d'état profondément imbriquées, les interactions GUI et les communications avec le backend fonctionnent harmonieusement ensemble. Les outils choisis répondent non seulement aux exigences fonctionnelles mais aussi aux objectifs de performance, d'ergonomie et d'évolutivité.</p>
<p>Ces efforts aboutissent à une page de playground offrant une expérience utilisateur interactive et cohérente, tout en respectant les meilleures pratiques techniques.</p>
<!-- -->
<iframe width="540" height="705" src="https://23a11111.sibforms.com/serve/MUIFAD4V-sjfjj3lAophYwHVb-SESnxq_HWg-plzTaW8rgGeG-yPYbFI8KeEg39tUVNQ0tVKOF2eoSlFHchztAiI2XzH7XA-UDWaIG5-vEuh7julQgBEdrhXjqYn4v46O7UC1yPmaf2cjB_H-wmJKE-QwzAgO3ct-Vq5KanudcV5GMHfGH9Z5mg6j0wa44i9vdecdOB32qdMHcFu" frameborder="0" scrolling="auto" style="display:block;margin-left:auto;margin-right:auto;max-width:100%"></iframe>]]></content:encoded>
            <category>compose craft</category>
            <category>dev</category>
        </item>
        <item>
            <title><![CDATA[Comment utiliser Traefik v3 avec Docker compose en mode swarm]]></title>
            <link>https://entrecompetents.fr/blog/fr/traefik-v3-docker</link>
            <guid>https://entrecompetents.fr/blog/fr/traefik-v3-docker</guid>
            <pubDate>Mon, 09 Feb 2026 19:25:36 GMT</pubDate>
            <description><![CDATA[La récente sortie de Traefik v3 apporte de nombreuses nouvelles fonctionnalités. Cet article explique comment utiliser Traefik v3 avec Docker compose en mode swarm.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="debug api avec wireshark" src="https://entrecompetents.fr/blog/fr/assets/images/traefik-v3-docker-ff3c5ea81071bf768655fc0f78d572cd.png" width="1200" height="480" class="img_ev3q"></p>
<p>La récente sortie de Traefik v3 apporte de nombreuses nouvelles fonctionnalités. Cet article n'a pas pour but d'expliquer toutes les nouveautés de Traefik v3, mais de vous montrer comment l'utiliser avec Docker compose.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="prérequis">Prérequis<a href="https://entrecompetents.fr/blog/fr/traefik-v3-docker#pr%C3%A9requis" class="hash-link" aria-label="Lien direct vers Prérequis" title="Lien direct vers Prérequis">​</a></h2>
<p>Avant de commencer, vous devez avoir Docker et Docker compose installés sur votre machine. Si vous ne les avez pas installés, vous pouvez suivre <a href="https://docs.docker.com/engine/install/" target="_blank" rel="noopener noreferrer">la documentation officielle</a> pour les installer.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="créer-un-fichier-docker-compose">Créer un fichier Docker compose<a href="https://entrecompetents.fr/blog/fr/traefik-v3-docker#cr%C3%A9er-un-fichier-docker-compose" class="hash-link" aria-label="Lien direct vers Créer un fichier Docker compose" title="Lien direct vers Créer un fichier Docker compose">​</a></h2>
<p>Créons un fichier docker-compose.yml simple pour déployer un serveur web et Traefik v3.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'3.8'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">traefik</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> traefik</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">v3.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">networks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> web</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">target</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">published</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tcp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> host</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">target</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">443</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">published</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">443</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">protocol</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tcp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> host</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> TZ=Europe/Paris</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">providers.swarm.endpoint=unix</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">///var/run/docker.sock</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">providers.docker.exposedbydefault=false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">providers.swarm.network=fdp_web</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">accesslog</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">entryPoints.websecure.address=</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">443</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--certificatesresolvers.myresolver.acme.tlschallenge=true"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--certificatesresolvers.myresolver.acme.email=organisation@lesfousdupeloton.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--entryPoints.web.forwardedHeaders.trustedIPs=10.0.0.0/24,10.0.2.0/24,192.168.100.0/24"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--entryPoints.websecure.forwardedHeaders.trustedIPs=10.0.0.2/24,10.0.2.0/24,192.168.100.0/24"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">accesslog.filepath=/log/acces/access.log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"traefik.http.middlewares.exclude-ip-log.ipwhitelist.sourceRange=217.72.195.109"</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Adresse IP à exclure pour le ping</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./certs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/letsencrypt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /var/run/docker.sock</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/var/run/docker.sock</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">ro</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./log</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">deploy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">placement</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">constraints</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> node.role == manager</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">restart_policy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">condition</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> on</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">failure</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">delay</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 5s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">max_attempts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">window</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 120s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"traefik.enable=false"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">frontend</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> registry.gitlab.com/entrecompetents/lesfousdupeltoton/frontend</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">1.0.8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">hostname</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> frontend.lfdp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">networks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> web</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">deploy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> replicated</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">replicas</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">labels</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"traefik.enable=true"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> traefik.http.services.frontend.loadbalancer.server.port=8080</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> traefik.http.routers.frontend.rule=Host(`lesfousdupeloton.com`)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"traefik.http.routers.frontend.entrypoints=websecure"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"traefik.http.routers.frontend.tls.certresolver=myresolver"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">restart_policy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">condition</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> on</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">failure</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">delay</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 5s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">max_attempts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">window</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 120s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">healthcheck</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">test</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"CMD"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"curl"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--fail"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"http://localhost:8080/"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># Commande de vérification de santé utilisant curl</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">interval</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 5s  </span><span class="token comment" style="color:#999988;font-style:italic"># Intervalle de vérification de santé</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">timeout</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 2s    </span><span class="token comment" style="color:#999988;font-style:italic"># Délai d'attente pour la vérification de santé</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">retries</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic"># Nombre de tentatives avant de marquer le conteneur comme non sain</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">start_period</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 10s  </span><span class="token comment" style="color:#999988;font-style:italic"># Délai avant de commencer les vérifications de santé après le démarrage du conteneur</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">networks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  web</span><span class="token punctuation" style="color:#393A34">:</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<!-- -->
<iframe width="540" height="705" src="https://23a11111.sibforms.com/serve/MUIFAD4V-sjfjj3lAophYwHVb-SESnxq_HWg-plzTaW8rgGeG-yPYbFI8KeEg39tUVNQ0tVKOF2eoSlFHchztAiI2XzH7XA-UDWaIG5-vEuh7julQgBEdrhXjqYn4v46O7UC1yPmaf2cjB_H-wmJKE-QwzAgO3ct-Vq5KanudcV5GMHfGH9Z5mg6j0wa44i9vdecdOB32qdMHcFu" frameborder="0" scrolling="auto" style="display:block;margin-left:auto;margin-right:auto;max-width:100%"></iframe>]]></content:encoded>
            <category>cloud</category>
            <category>devops</category>
        </item>
        <item>
            <title><![CDATA[Maîtriser le Débogage d'API REST : Un Guide avec Wireshark]]></title>
            <link>https://entrecompetents.fr/blog/fr/debug-api-wireshark</link>
            <guid>https://entrecompetents.fr/blog/fr/debug-api-wireshark</guid>
            <pubDate>Mon, 09 Feb 2026 19:25:36 GMT</pubDate>
            <description><![CDATA[Déboger n'importe quelle API REST grâce à Wireshark. Déboger une API quand aucun outil de débogage ou de log ne fonctionne.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="debug api avec wireshark" src="https://entrecompetents.fr/blog/fr/assets/images/debug-api-wireshark-74633303bab67d5e242c649510f79aec.png" width="1200" height="480" class="img_ev3q"></p>
<p>Lorsque l'on travaille sur une API, il est important de pouvoir déboguer cette dernière.&nbsp;</p>
<p>Les outils de débogage d'API peuvent se diviser en deux classes : ceux qui permettent d'éditer les requêtes HTTP envoyées, et ceux qui permettent d'inspecter le réseau pour voir ce qui a été envoyé a posteriori.</p>
<p>Dans la première catégorie, on retrouve des outils assez connus comme <a href="https://www.postman.com/" target="_blank" rel="noopener noreferrer">Postman</a>, <a href="https://insomnia.rest/" target="_blank" rel="noopener noreferrer">Insomnia</a> ou <a href="https://curl.se/" target="_blank" rel="noopener noreferrer">Curl</a>.</p>
<table><thead><tr><th></th><th>Postman</th><th>Insomnia</th><th>Curl</th></tr></thead><tbody><tr><td>GUI</td><td>✅</td><td>✅</td><td>❌</td></tr><tr><td>Open source</td><td>❌</td><td>✅</td><td>✅</td></tr><tr><td>Export des requêtes en code</td><td>✅</td><td>✅</td><td>❌</td></tr><tr><td>Import de format OpenAPI</td><td>✅</td><td>❌</td><td>❌</td></tr><tr><td>Simplicité</td><td>⭐️⭐️⭐️⭐️</td><td>⭐️⭐️⭐️</td><td>⭐️</td></tr><tr><td>Facilité d'installation</td><td>⭐️⭐️</td><td>⭐️⭐️</td><td>⭐️⭐️⭐️⭐️</td></tr></tbody></table>
<p>Dans la seconde catégorie, Wireshark supplante la plupart des solutions disponibles. Heureusement, il est open-source et gratuit.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="dans-quels-cas-utiliser-wireshark-">Dans quel(s) cas utiliser Wireshark ?<a href="https://entrecompetents.fr/blog/fr/debug-api-wireshark#dans-quels-cas-utiliser-wireshark-" class="hash-link" aria-label="Lien direct vers Dans quel(s) cas utiliser Wireshark ?" title="Lien direct vers Dans quel(s) cas utiliser Wireshark ?">​</a></h2>
<p>Wireshark nous permet d'intercepter les requêtes du réseau, il est donc utile quand :</p>
<ul>
<li>On souhaite connaître les requêtes réseau d'un outil sans avoir accès à son code.</li>
<li>On souhaite vérifier le contenu des requêtes envoyées par un service.</li>
</ul>
<p>Ci-dessous on montre que wireshark est non intrusif dans l'envoi ou la réception des requêtes HTTP, il se pose en observateur du réseau.</p>
<p><img decoding="async" loading="lazy" alt="schéma de wireshark" src="https://entrecompetents.fr/blog/fr/assets/images/wireshark_schema-0d1f2fd87777c7262ffbe1571437ced2.png" width="482" height="235" class="img_ev3q"></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="comment-utiliser-wireshark-">Comment utiliser Wireshark ?<a href="https://entrecompetents.fr/blog/fr/debug-api-wireshark#comment-utiliser-wireshark-" class="hash-link" aria-label="Lien direct vers Comment utiliser Wireshark ?" title="Lien direct vers Comment utiliser Wireshark ?">​</a></h3>
<p>Pour installer Wireshark, vous pouvez suivre <a href="https://www.wireshark.org/download.html" target="_blank" rel="noopener noreferrer">la page officielle d'installation</a>.</p>
<p>Une fois installé, à l'ouverture, Wireshark présente les réseaux disponibles sur l'ordinateur ainsi que leur activité. (c.f capture d'écran ci-dessous)</p>
<p><img decoding="async" loading="lazy" alt="wireshark screenshot interface" src="https://entrecompetents.fr/blog/fr/assets/images/wireshark_screenshot_interface-2a125a0cd79c52d84dc5751e67576742.png" width="3104" height="1820" class="img_ev3q"></p>
<p>Une fois les interfaces sélectionnées, l'utilisateur peut lancer la capture des requêtes. Il faut noter que les requêtes en HTTPS seront capturées, mais illisibles, car chiffrées.</p>
<p>Comme sur la capture d'écran ci-dessous l'utilisateur peut aussi ajouter des filtres pour n'afficher que les requêtes utiles.</p>
<p><img decoding="async" loading="lazy" alt="wireshark screenshot recording" src="https://entrecompetents.fr/blog/fr/assets/images/wireshark_screenshot_recording-554c6a06b6175db378e7ad06c772d537.png" width="3104" height="1820" class="img_ev3q"></p>
<p>L'interface d'inspection des requêtes est particulièrement utile. Elle permet de regarder en détail le contenu de la requête HTTP et comprendre ce qu'a reçu le destinataire.</p>
<p>Ainsi cet atout "non intrusif" de Wireshark en fait un allié précieux pour le débogage d'applications.</p>
<!-- -->
<iframe width="540" height="705" src="https://23a11111.sibforms.com/serve/MUIFAD4V-sjfjj3lAophYwHVb-SESnxq_HWg-plzTaW8rgGeG-yPYbFI8KeEg39tUVNQ0tVKOF2eoSlFHchztAiI2XzH7XA-UDWaIG5-vEuh7julQgBEdrhXjqYn4v46O7UC1yPmaf2cjB_H-wmJKE-QwzAgO3ct-Vq5KanudcV5GMHfGH9Z5mg6j0wa44i9vdecdOB32qdMHcFu" frameborder="0" scrolling="auto" style="display:block;margin-left:auto;margin-right:auto;max-width:100%"></iframe>]]></content:encoded>
            <category>dev</category>
            <category>cloud</category>
            <category>api</category>
            <category>network</category>
        </item>
        <item>
            <title><![CDATA[Maîtriser le Débogage réseau : Un Guide avec Tshark]]></title>
            <link>https://entrecompetents.fr/blog/fr/debug-tshark-nogui</link>
            <guid>https://entrecompetents.fr/blog/fr/debug-tshark-nogui</guid>
            <pubDate>Mon, 09 Feb 2026 19:25:36 GMT</pubDate>
            <description><![CDATA[Déboguer le réseau quand on a accès uniquement à une CLI (terminal)]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="debug api avec tshark" src="https://entrecompetents.fr/blog/fr/assets/images/debug-api-wireshark-74633303bab67d5e242c649510f79aec.png" width="1200" height="480" class="img_ev3q"></p>
<p>De nombreux outils sont disponibles pour déboguer le réseau, que cela soit pour déboguer une API REST ou pour analyser le traffic sur un routeur ou un réseau local.</p>
<p>Un comparatif est fait sur <a href="https://entrecompetents.fr/blog/fr/debug-api-wireshark">notre article sur Wireshark</a>.</p>
<p>Tshark est la version CLI de wireshark, et vous permet de faire des captures avec votre outil préféré quand vous n'avez pas accès à une GUI, ou que tout simplement le serveur en CLI ne possède pas d'environnement graphique.</p>
<p>L'avantage c'est que vous pouvez exporter la capture réseau et l'ouvrir sur votre PC avec Wireshark ! Nous verrons cela en fin d'article.</p>
<h1>Comment utiliser Tshark ?</h1>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="installation-de-tshark">Installation de tshark<a href="https://entrecompetents.fr/blog/fr/debug-tshark-nogui#installation-de-tshark" class="hash-link" aria-label="Lien direct vers Installation de tshark" title="Lien direct vers Installation de tshark">​</a></h2>
<p>Vous pouvez suivre la <a href="https://tshark.dev/setup/install/" target="_blank" rel="noopener noreferrer">documentation officielle</a>, sur du debian-based il suffit de tapper <code>apt install tshark</code>.</p>
<p>Vous avez également tshark en installant Wireshark. Et tout comme wireshark, tshark utilise Dumpcap comme moteur de capture.</p>
<p>Tshark a l'avantage d'être un outil léger et puissant, qui s'installe sur de nombreuses plateformes.</p>
<p>A l'installation vous pouvez choisir de donner l'accès à l'analyse réseau à tous les utilisateurs ou uniquement aux sudoers.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="comment-capturer-des-paquets-avec-tshark-">Comment capturer des paquets avec Tshark ?<a href="https://entrecompetents.fr/blog/fr/debug-tshark-nogui#comment-capturer-des-paquets-avec-tshark-" class="hash-link" aria-label="Lien direct vers Comment capturer des paquets avec Tshark ?" title="Lien direct vers Comment capturer des paquets avec Tshark ?">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="commandes-de-bases-utiles">Commandes de bases utiles<a href="https://entrecompetents.fr/blog/fr/debug-tshark-nogui#commandes-de-bases-utiles" class="hash-link" aria-label="Lien direct vers Commandes de bases utiles" title="Lien direct vers Commandes de bases utiles">​</a></h3>
<p>Pour voir la liste des interfaces capturables : <code>tshark -D</code>. Vous avez l'avantage de voir également les interfaces virtuelles.</p>
<p>Pour capturer : <code>tshark</code> ou <code>tshark -i &lt;interface_name&gt;</code>.</p>
<p><img decoding="async" loading="lazy" alt="tshark interface capture" src="https://entrecompetents.fr/blog/fr/assets/images/thsark-iface-692249ecc5715c9f353ce9e94bf7813b.png" width="1445" height="330" class="img_ev3q"></p>
<p>Vous pouvez faire des filtres complexes avec <code>tshark -f "${filter}</code> comme <code>tcp port 80</code>, de la même manière que les filtres sur wireshark.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="pour-aller-plus-loin">Pour aller plus loin<a href="https://entrecompetents.fr/blog/fr/debug-tshark-nogui#pour-aller-plus-loin" class="hash-link" aria-label="Lien direct vers Pour aller plus loin" title="Lien direct vers Pour aller plus loin">​</a></h3>
<p>La documenation officielle est très complète, et la commande <code>tshark --help</code> est très utile, tout comme le <em>man</em>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="exporter-et-ouvrir-la-capture-sur-wireshark">Exporter et ouvrir la capture sur Wireshark<a href="https://entrecompetents.fr/blog/fr/debug-tshark-nogui#exporter-et-ouvrir-la-capture-sur-wireshark" class="hash-link" aria-label="Lien direct vers Exporter et ouvrir la capture sur Wireshark" title="Lien direct vers Exporter et ouvrir la capture sur Wireshark">​</a></h2>
<p>Un énorme atout de tshark c'est l'intercompatibilité et le format de capture.</p>
<p>Pour enregistrer une capture dans un fichier il faut utiliser l'option <code>-w</code> comme ceci :  <code>tshark -w &lt;filename&gt;.pcap</code>.</p>
<p>Pour lire un fichier pcap, vous avez deux options :</p>
<ul>
<li>ouvrir en GUI avec Wireshark</li>
<li>ouvrir avec tshark en colorant avec : <code>tshark -r &lt;filename&gt;.pcap --color</code></li>
</ul>
<!-- -->
<iframe width="540" height="705" src="https://23a11111.sibforms.com/serve/MUIFAD4V-sjfjj3lAophYwHVb-SESnxq_HWg-plzTaW8rgGeG-yPYbFI8KeEg39tUVNQ0tVKOF2eoSlFHchztAiI2XzH7XA-UDWaIG5-vEuh7julQgBEdrhXjqYn4v46O7UC1yPmaf2cjB_H-wmJKE-QwzAgO3ct-Vq5KanudcV5GMHfGH9Z5mg6j0wa44i9vdecdOB32qdMHcFu" frameborder="0" scrolling="auto" style="display:block;margin-left:auto;margin-right:auto;max-width:100%"></iframe>]]></content:encoded>
            <category>dev</category>
            <category>cloud</category>
            <category>api</category>
        </item>
        <item>
            <title><![CDATA[Docker Commit ou Dockerfile ?]]></title>
            <link>https://entrecompetents.fr/blog/fr/dockerCommitVsDockerFile</link>
            <guid>https://entrecompetents.fr/blog/fr/dockerCommitVsDockerFile</guid>
            <pubDate>Mon, 09 Feb 2026 19:25:36 GMT</pubDate>
            <description><![CDATA[Découvrez les subtilités entre Docker Commit et Docker Build à travers une analyse approfondie, explorant leurs différences de manière détaillée. Apprenez les spécificités de la création d'images Docker à partir de conteneurs existants et de Dockerfiles, mettant l'accent sur les différences de taille et de variations d'attributs. Identifiez les scénarios idéaux pour utiliser Docker Commit afin de personnaliser des images pour une utilisation simplifiée et Docker Build pour la création scriptée et durable d'images. Rejoignez-nous dans cette comparaison perspicace pour renforcer votre expertise Docker.]]></description>
            <content:encoded><![CDATA[<p>Connaissez-vous la deuxième commande qui permet de créer des images docker ? 🐳</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Docker commit [nom du container] [nom de image]:[tag]</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Cette commande permet de créer une image en faisant une sorte de "snapshot" du container ciblé.
Mais, je me demandais quelles sont les différences avec docker build (mis à part le Dockerfile) ?</p>
<p>J'ai pris du temps pour étudier un cas très simple et regarder les différences que cela produit :</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="les-différences-entre-docker-commit-et-docker-build--">Les différences entre docker commit et docker build  :<a href="https://entrecompetents.fr/blog/fr/dockerCommitVsDockerFile#les-diff%C3%A9rences-entre-docker-commit-et-docker-build--" class="hash-link" aria-label="Lien direct vers Les différences entre docker commit et docker build  :" title="Lien direct vers Les différences entre docker commit et docker build  :">​</a></h2>
<p>Les deux commandes <code>docker commit</code> et <code>docker build</code> permettent de créer des images docker.
Mais <code>docker commit</code> permet de la créer à partir d'un container existant et <code>docker build</code> permet de la créer à partir d'un <a href="https://docs.docker.com/engine/reference/builder/" target="_blank" rel="noopener noreferrer">Dockerfile</a> .</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="protocole-de-test-de-comparaison-de-docker-commit-et-docker-build-">Protocole de test de comparaison de docker commit et docker build :<a href="https://entrecompetents.fr/blog/fr/dockerCommitVsDockerFile#protocole-de-test-de-comparaison-de-docker-commit-et-docker-build-" class="hash-link" aria-label="Lien direct vers Protocole de test de comparaison de docker commit et docker build :" title="Lien direct vers Protocole de test de comparaison de docker commit et docker build :">​</a></h3>
<ol>
<li>Construire une image <code>nginx:1.24.0</code> à laquelle on ajoute le paquet <code>iputils-ping</code> .</li>
<li>Faire cet ajout en une seule ligne de commande.</li>
</ol>
<p><em>Le Dockerfile :</em></p>
<div class="language-Dockerfile language-dockerfile codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-dockerfile codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">FROM nginx:1.24.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RUN apt update &amp;&amp; apt install iputils-ping -y</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Dans ce cas on observe une différence de <strong>1254 Octets</strong>, soit envion <strong>0.0001mb</strong>.</p>
<p>Les poids exacts des images sont de :</p>
<ul>
<li>155 18<strong>2 582</strong> octets pour le <strong>docker build</strong></li>
<li>155 18<strong>3 836</strong> octets pour le <strong>docker commit</strong></li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="différences-dattributs">Différences d'attributs<a href="https://entrecompetents.fr/blog/fr/dockerCommitVsDockerFile#diff%C3%A9rences-dattributs" class="hash-link" aria-label="Lien direct vers Différences d'attributs" title="Lien direct vers Différences d'attributs">​</a></h3>
<p>Si la taille des images n'a que très peu changé, les attributs eux sont radicalement modifiés :</p>
<p>L'image créée avec docker build à perdu les attributs suivants :</p>
<ul>
<li>Cmd</li>
<li>EntryPoint</li>
<li>Env</li>
<li>Labels</li>
<li>Exposed ports</li>
<li>Stop signal</li>
<li>Docker version</li>
<li>Parent</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion-des-différences-entre-docker-commit-et-docker-build-">Conclusion des différences entre docker commit et docker build :<a href="https://entrecompetents.fr/blog/fr/dockerCommitVsDockerFile#conclusion-des-diff%C3%A9rences-entre-docker-commit-et-docker-build-" class="hash-link" aria-label="Lien direct vers Conclusion des différences entre docker commit et docker build :" title="Lien direct vers Conclusion des différences entre docker commit et docker build :">​</a></h2>
<p>Il n'y a pas de meilleure solution, elles ont juste des buts différents :</p>
<p><strong>Docker commit</strong> devrait surtout être utilisé pour customiser une image docker pour simplifier l'utilisation de celle-ci par la suite.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="exemple-">Exemple :<a href="https://entrecompetents.fr/blog/fr/dockerCommitVsDockerFile#exemple-" class="hash-link" aria-label="Lien direct vers Exemple :" title="Lien direct vers Exemple :">​</a></h4>
<p>Ajouter un paquet, modifier rapidement une image à des fins de test...</p>
<p><strong>Docker build</strong> devrait être utilisé quand on crée un nouveau cas d'utilisation et notamment que l'on souhaite garder une version scriptée de son image sous forme de Dockerfile. Ce qui est bien plus pérenne qu'une image stockée dans un registry.</p>
<p>Les sources de mon expérience et toute ma démarche sont disponibles sur ce repos : <a href="https://github.com/LucasSovre/sourcesCommitVsBuild/tree/main" target="_blank" rel="noopener noreferrer">https://github.com/LucasSovre/sourcesCommitVsBuild/tree/main</a></p>
<p>Normalement cela ne devrait rien changer à la mécanique étudiée, mais les tests ont été faits avec un processeur ARM (m1)</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="sources">Sources<a href="https://entrecompetents.fr/blog/fr/dockerCommitVsDockerFile#sources" class="hash-link" aria-label="Lien direct vers Sources" title="Lien direct vers Sources">​</a></h3>
<ul>
<li><a href="https://docs.docker.com/engine/reference/commandline/commit/" target="_blank" rel="noopener noreferrer">Dockerfile commit officielle</a></li>
<li><a href="https://docs.docker.com/engine/reference/commandline/build/" target="_blank" rel="noopener noreferrer">Docker build documentation officielle</a></li>
<li><a href="https://docs.docker.com/engine/reference/builder/" target="_blank" rel="noopener noreferrer">Docker commit documentation officielle</a></li>
<li><a href="https://github.com/LucasSovre/sourcesCommitVsBuild/tree/main" target="_blank" rel="noopener noreferrer">Code sources</a></li>
</ul>
<!-- -->
<iframe width="540" height="705" src="https://23a11111.sibforms.com/serve/MUIFAD4V-sjfjj3lAophYwHVb-SESnxq_HWg-plzTaW8rgGeG-yPYbFI8KeEg39tUVNQ0tVKOF2eoSlFHchztAiI2XzH7XA-UDWaIG5-vEuh7julQgBEdrhXjqYn4v46O7UC1yPmaf2cjB_H-wmJKE-QwzAgO3ct-Vq5KanudcV5GMHfGH9Z5mg6j0wa44i9vdecdOB32qdMHcFu" frameborder="0" scrolling="auto" style="display:block;margin-left:auto;margin-right:auto;max-width:100%"></iframe>]]></content:encoded>
            <category>docker</category>
            <category>devops</category>
            <category>cloud</category>
        </item>
        <item>
            <title><![CDATA[Listmonk, l'alternative à Mailchimp]]></title>
            <link>https://entrecompetents.fr/blog/fr/listmonk</link>
            <guid>https://entrecompetents.fr/blog/fr/listmonk</guid>
            <pubDate>Mon, 09 Feb 2026 19:25:36 GMT</pubDate>
            <description><![CDATA[Listmonk, une alternative gratuite à Mailchimp pour vos campagnes d'email. Comparez les fonctionnalités, explorez les avantages financiers, et apprenez à intégrer Listmonk dans votre infrastructure. Optez pour une solution efficace et économique pour votre marketing par email.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="listes d&amp;#39;outils open sources" src="https://entrecompetents.fr/blog/fr/assets/images/MailChimpVsListmonk-866b2ce89793a446262108502b33a465.jpg" width="1200" height="480" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="à-quoi-servent-ces-outils-">À quoi servent ces outils ?<a href="https://entrecompetents.fr/blog/fr/listmonk#%C3%A0-quoi-servent-ces-outils-" class="hash-link" aria-label="Lien direct vers À quoi servent ces outils ?" title="Lien direct vers À quoi servent ces outils ?">​</a></h2>
<p>Ces deux outils servent à collecter des listes d'emails, puis envoyer des emails en masse.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="où-trouver-ces-outils-">Où trouver ces outils ?<a href="https://entrecompetents.fr/blog/fr/listmonk#o%C3%B9-trouver-ces-outils-" class="hash-link" aria-label="Lien direct vers Où trouver ces outils ?" title="Lien direct vers Où trouver ces outils ?">​</a></h2>
<p><a href="https://listmonk.app/" target="_blank" rel="noopener noreferrer">Site officiel de Listmonk</a></p>
<p><a href="https://mailchimp.com/" target="_blank" rel="noopener noreferrer">Site officiel de MailChimp</a></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="mailchimp-un-outil-ultra-complet">MailChimp un outil ultra-complet<a href="https://entrecompetents.fr/blog/fr/listmonk#mailchimp-un-outil-ultra-complet" class="hash-link" aria-label="Lien direct vers MailChimp un outil ultra-complet" title="Lien direct vers MailChimp un outil ultra-complet">​</a></h3>
<p>MailChimp est un outil "tout-en-un". Serveur mails, automatisation, collecte de données, formulaires d'inscription… Presque rien ne manque.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="listmonk-un-outil-focalisé-sur-les-mails">Listmonk, un outil focalisé sur les mails<a href="https://entrecompetents.fr/blog/fr/listmonk#listmonk-un-outil-focalis%C3%A9-sur-les-mails" class="hash-link" aria-label="Lien direct vers Listmonk, un outil focalisé sur les mails" title="Lien direct vers Listmonk, un outil focalisé sur les mails">​</a></h3>
<p>Listmonk lui ne s'encombre pas de toutes les fonctionnalités d'automatisation ou d'un éditeur de wysiwyg (Ce qui serait quand même bien agréable). Mais ce qu'il fait il le fait bien, et surtout, il utilise des serveurs mails externes.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="coté-finance">Coté finance<a href="https://entrecompetents.fr/blog/fr/listmonk#cot%C3%A9-finance" class="hash-link" aria-label="Lien direct vers Coté finance" title="Lien direct vers Coté finance">​</a></h3>
<p>En termes de prix Listmonk est imbattable puisqu'il est gratuit. Même si l'on compte le prix des serveurs et de fournisseurs de serveurs mail&nbsp;: les prix de Mailchimp restent beaucoup plus haut.</p>
<p>Avec une base d'utilisateurs de 10 000 utilisateurs et un envoi moyen de 5 mails par personne et par mois :</p>
<p>MailChimp a : au 3 décembre 2023 un coût de <strong>125€/mois</strong>.</p>
<p>En partant du principe que vous utilisez Amazon SES pour les mails (0,09€/1k emails). MailChimp vous coûtera 4,5€ par mois.</p>
<p>Il faudrait que votre infrastructure (uniquement celle dédiée à Listmonk) vous coûte plus de 120€ par mois pour que MailChimp soit plus intéressant.</p>
<p>On voit donc qu'en termes d'économies Listmonk peut être vraiment très intéressant. Sachant que avec Listmonk vos listes d'abonnés sont illimitées.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="comment-combler-les-manques-de-fonctionnalités-de-listmonk-">Comment combler les manques de fonctionnalités de Listmonk ?<a href="https://entrecompetents.fr/blog/fr/listmonk#comment-combler-les-manques-de-fonctionnalit%C3%A9s-de-listmonk-" class="hash-link" aria-label="Lien direct vers Comment combler les manques de fonctionnalités de Listmonk ?" title="Lien direct vers Comment combler les manques de fonctionnalités de Listmonk ?">​</a></h3>
<p>Comme dit précédemment : Listmonk n'as pas d'automatisation des parcours utilisateurs ou d'envoi automatiques d'emails.
Mais à l'aide d'un <a href="https://n8n.io/" target="_blank" rel="noopener noreferrer">n8n</a> auto-hébergé, vous pourrez très facilement créer des automatisations encore plus poussées en no-code.</p>
<p>Voici, ci-dessous : un exemple d'automatisation n8n-lismonk. Ce workflow est déclenché par l'appel d'un webhook, recupère les informations de l'utilisateur depuis la base de de données, crée l'utilisateur dans la base listmonk si necessaire puis, lui envoi un mail.</p>
<p><img decoding="async" loading="lazy" alt="exemple de workflow n8n" src="https://entrecompetents.fr/blog/fr/assets/images/n8n-listmonk-6b269daf7ee440de646fe1fdc99bcbc2.png" width="2122" height="828" class="img_ev3q"></p>
<p>Dans l'exemple suivant, ce worklow permet de programmer l'envoi d'un mail transactionel dans n jours|heures|minutes à un utilisateur.</p>
<p><img decoding="async" loading="lazy" alt="exemple de workflow n8n" src="https://entrecompetents.fr/blog/fr/assets/images/n8n-listmonk-wait-50e509d937d04eefdee141b9a1d960bf.png" width="1358" height="576" class="img_ev3q"></p>
<p>Seul l'éditeur wysiwyg de MailChimp manque vraiment, mais quelques templates de mails vous suffirons surement et vous pouvez facilement vous en procurer pour quelques euros.</p>
<p>Vous pourrez trouver le noeud n8n listmonk sur ce <a href="https://www.npmjs.com/package/n8n-nodes-listmonk" target="_blank" rel="noopener noreferrer">package npm</a></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="comment-installer-listmonk-">Comment installer Listmonk ?<a href="https://entrecompetents.fr/blog/fr/listmonk#comment-installer-listmonk-" class="hash-link" aria-label="Lien direct vers Comment installer Listmonk ?" title="Lien direct vers Comment installer Listmonk ?">​</a></h2>
<p><a href="https://listmonk.app/docs/installation/" target="_blank" rel="noopener noreferrer">La page d'installation</a> de Listmonk est très complète, mais voici un petit résumé en accéléré :</p>
<p>Vous pouvez utiliser la commande suivante si vous n'avez pas déjà de base de donnée PostgreSQL :</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mkdir listmonk &amp;&amp; cd listmonk</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sh -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copier le code" title="Copier" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Si vous avez déjà une base de donnée alors il vous suffit de lancer un conteneur avec la configuration associée.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="comment-intégrer-listmonk-dans-votre-infrastructure-">Comment intégrer Listmonk dans votre infrastructure ?<a href="https://entrecompetents.fr/blog/fr/listmonk#comment-int%C3%A9grer-listmonk-dans-votre-infrastructure-" class="hash-link" aria-label="Lien direct vers Comment intégrer Listmonk dans votre infrastructure ?" title="Lien direct vers Comment intégrer Listmonk dans votre infrastructure ?">​</a></h2>
<p>Voici un schéma qui reprend tous les éléments précédents pour donner un exemple d'intégration de Listmonk dans vos outils.</p>
<p><img decoding="async" loading="lazy" alt="Exemple d&amp;#39;architecture" src="https://entrecompetents.fr/blog/fr/assets/images/listmonk-492368af54315c5a2f84479036d0c309.jpg" width="625" height="727" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="sources">Sources<a href="https://entrecompetents.fr/blog/fr/listmonk#sources" class="hash-link" aria-label="Lien direct vers Sources" title="Lien direct vers Sources">​</a></h2>
<ul>
<li><a href="https://mailchimp.com/" target="_blank" rel="noopener noreferrer">https://mailchimp.com</a></li>
<li><a href="https://listmonk.app/" target="_blank" rel="noopener noreferrer">https://listmonk.app</a></li>
<li><a href="https://listmonk.app/docs/" target="_blank" rel="noopener noreferrer">https://listmonk.app/docs/</a></li>
<li><a href="https://aws.amazon.com/fr/ses/" target="_blank" rel="noopener noreferrer">https://aws.amazon.com/fr/ses/</a></li>
<li><a href="https://n8n.io/" target="_blank" rel="noopener noreferrer">https://n8n.io</a></li>
</ul>
<!-- -->
<iframe width="540" height="705" src="https://23a11111.sibforms.com/serve/MUIFAD4V-sjfjj3lAophYwHVb-SESnxq_HWg-plzTaW8rgGeG-yPYbFI8KeEg39tUVNQ0tVKOF2eoSlFHchztAiI2XzH7XA-UDWaIG5-vEuh7julQgBEdrhXjqYn4v46O7UC1yPmaf2cjB_H-wmJKE-QwzAgO3ct-Vq5KanudcV5GMHfGH9Z5mg6j0wa44i9vdecdOB32qdMHcFu" frameborder="0" scrolling="auto" style="display:block;margin-left:auto;margin-right:auto;max-width:100%"></iframe>]]></content:encoded>
            <category>self-hosted</category>
        </item>
    </channel>
</rss>