<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Neurau Tech</title>
    <description>The latest articles on DEV Community by Neurau Tech (@neurau_tech).</description>
    <link>https://dev.to/neurau_tech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3994593%2F49cb77af-0363-49eb-af59-7b2c101fad24.png</url>
      <title>DEV Community: Neurau Tech</title>
      <link>https://dev.to/neurau_tech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/neurau_tech"/>
    <language>en</language>
    <item>
      <title>Reconversion développeur web : marché, formations, financement et plan réaliste (2026)</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sun, 01 Nov 2026 15:35:46 +0000</pubDate>
      <link>https://dev.to/neurau_tech/reconversion-developpeur-web-marche-formations-financement-et-plan-realiste-2026-50h9</link>
      <guid>https://dev.to/neurau_tech/reconversion-developpeur-web-marche-formations-financement-et-plan-realiste-2026-50h9</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Une reconversion développeur web reste possible en 2026, mais dans un marché plus étroit qu'il y a trois ans pour les débutants. Les entreprises annoncent moins de projets de recrutement dans l'informatique, et les profils expérimentés passent devant. Le titre professionnel le plus courant n'exige pas de diplôme, l'âge est un critère de discrimination interdit, mais aucune formation ne garantit un poste.&lt;/p&gt;

&lt;p&gt;Ce guide s'adresse aux adultes qui changent de métier en France. Il repose sur des sources publiques consultées le 1er novembre 2026&amp;nbsp;: enquête de France Travail, fiches du répertoire national des certifications (RNCP), pages officielles sur le CPF et le projet de transition professionnelle, enquêtes de Stack Overflow et HackerRank. Nous n'avons suivi aucune formation et n'avons mené aucun test.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selon les résultats nationaux de l'enquête BMO de France Travail, les projets de recrutement de techniciens d'étude et de développement en informatique passent de 14&amp;nbsp;180 en 2025 à 9&amp;nbsp;500 en 2026.&lt;/li&gt;
&lt;li&gt;Le titre professionnel «&amp;nbsp;Développeur web et web mobile&amp;nbsp;» (niveau 5, ministère du Travail) n'affiche aucun prérequis de diplôme sur sa fiche RNCP.&lt;/li&gt;
&lt;li&gt;Chaque fiche RNCP publie des taux d'insertion&amp;nbsp;: lisez-les avant de signer, en distinguant l'emploi «&amp;nbsp;tout métier&amp;nbsp;» et l'emploi «&amp;nbsp;dans le métier visé&amp;nbsp;».&lt;/li&gt;
&lt;li&gt;Depuis le 2 avril 2026, utiliser son CPF coûte 150&amp;nbsp;€ de participation forfaitaire, sauf exceptions comme les demandeurs d'emploi.&lt;/li&gt;
&lt;li&gt;Un démarchage qui promet une formation «&amp;nbsp;100&amp;nbsp;% gratuite&amp;nbsp;» et demande vos identifiants CPF est le scénario d'arnaque décrit par France Travail.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Le marché des développeurs débutants en 2026
&lt;/h2&gt;

&lt;p&gt;Le marché recrute encore, mais moins, et d'abord des profils expérimentés. C'est le constat commun des trois sources que nous avons pu lire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;En France, l'enquête BMO.&lt;/strong&gt; France Travail annonce près de 2,3&amp;nbsp;millions d'intentions de recrutement pour 2026, en retrait de 6,5&amp;nbsp;% sur un an, tous métiers confondus (&lt;a href="https://www.francetravail.org/accueil/actualites/2026/enquete-bmo-2026-france-travail-deploie-sa-strategie-sectorielle-face-a-pres-de-2-3-millions-de-projets-de-recrutement.html?type=article" rel="noopener noreferrer"&gt;France Travail&lt;/a&gt;, 21 avril 2026). Dans l'informatique, la baisse est plus forte, d'après les résultats nationaux par métier publiés sur le site statistique de l'enquête (&lt;a href="https://statistiques.francetravail.org/bmo/bmo?le=0&amp;amp;pp=2026&amp;amp;ss=1" rel="noopener noreferrer"&gt;BMO 2026&lt;/a&gt; et &lt;a href="https://statistiques.francetravail.org/bmo/bmo?le=0&amp;amp;pp=2025&amp;amp;ss=1" rel="noopener noreferrer"&gt;BMO 2025&lt;/a&gt;, consultés le 1er novembre 2026).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp836hxc18neulwt9600p.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp836hxc18neulwt9600p.webp" alt="Graphique en haltères&amp;nbsp;: projets de recrutement de quatre métiers de l'informatique selon l'enquête BMO de France Travail, dont 14&amp;nbsp;180 en 2025 et 9&amp;nbsp;500 en 2026 pour les techniciens d'étude et de développement en informatique, et 22&amp;nbsp;670 puis 18&amp;nbsp;330 pour les ingénieurs, contexte d'une reconversion développeur web" width="799" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour les techniciens d'étude et de développement en informatique, selon nous la famille la plus proche d'un premier poste après un titre de niveau 5, on passe de 14&amp;nbsp;180 projets à 9&amp;nbsp;500, et la part jugée difficile par les employeurs de 50,4&amp;nbsp;% à 44,0&amp;nbsp;%. Pour les ingénieurs et cadres d'étude en informatique, de 22&amp;nbsp;670 à 18&amp;nbsp;330. L'enquête porte sur des intentions d'embauche, couvre tous les niveaux d'expérience et ne distingue pas les débutants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chez les cadres, l'Apec.&lt;/strong&gt; Selon L'Étudiant, qui cite l'Apec, les entreprises ont recruté 57&amp;nbsp;000 cadres informaticiens en 2025, contre 71&amp;nbsp;920 en 2023. Tous secteurs confondus, les recrutements de cadres de moins de six ans d'expérience ont reculé de 5&amp;nbsp;% en 2025 après 9&amp;nbsp;% l'année précédente, et l'Apec ne prévoit que 1&amp;nbsp;% de hausse pour eux en 2026 (&lt;a href="https://www.letudiant.fr/jobsstages/les-recrutements-de-jeunes-cadres-encore-en-baisse-en-2025-selon-l-apec.html" rel="noopener noreferrer"&gt;L'Étudiant&lt;/a&gt;, 2 avril 2026). Pour les cadres informaticiens, L'Étudiant annonce une reprise de 5&amp;nbsp;% en 2026, Le Monde Informatique de 4&amp;nbsp;%, avec une réserve de l'Apec sur le contexte économique (&lt;a href="https://www.lemondeinformatique.fr/actualites/lire-les-recrutements-de-cadres-it-repartent-a-la-hausse-en-2026-99899.html" rel="noopener noreferrer"&gt;Le Monde Informatique&lt;/a&gt;, 13 avril 2026).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;À l'international, HackerRank.&lt;/strong&gt; Dans son rapport 2025, fondé sur l'activité de sa plateforme de tests, l'éditeur relève une hausse de 22&amp;nbsp;% pour les postes de lead, 19&amp;nbsp;% pour les seniors, 9&amp;nbsp;% pour les juniors et 7&amp;nbsp;% pour les débutants (&lt;a href="https://www.hackerrank.com/reports/developer-skills-report-2025" rel="noopener noreferrer"&gt;HackerRank&lt;/a&gt;, rapport 2025). Un billet du même éditeur précise que la demande de juniors en front-end a encore baissé de 22&amp;nbsp;% en 2024 (&lt;a href="https://www.hackerrank.com/blog/senior-hiring-is-surging-will-early-career-talent-catch-up/" rel="noopener noreferrer"&gt;HackerRank&lt;/a&gt;). Ce sont des chiffres d'éditeur, mondiaux.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; visez un poste de technicien ou de développeur junior plutôt qu'un poste de cadre, et prévoyez une recherche longue. Pour les rémunérations, notre &lt;a href="https://neurautech.com/salaires-developpeurs-france-2026-grille-complete/" rel="noopener noreferrer"&gt;comparatif des salaires de développeurs en France&lt;/a&gt; confronte six études.&lt;/p&gt;




&lt;h2&gt;
  
  
  Faut-il un diplôme pour devenir développeur web ?
&lt;/h2&gt;

&lt;p&gt;Non pour la certification, pas toujours pour l'employeur. La fiche RNCP du titre professionnel «&amp;nbsp;Développeur web et web mobile&amp;nbsp;» laisse vides les champs de prérequis et indique six voies d'accès, dont la candidature individuelle et la validation des acquis de l'expérience (&lt;a href="https://www.francecompetences.fr/recherche/rncp/37674/" rel="noopener noreferrer"&gt;France Compétences, RNCP37674&lt;/a&gt;, consulté le 1er novembre 2026).&lt;/p&gt;

&lt;p&gt;Ce titre du ministère du Travail est classé au niveau 5, celui d'un BTS. Il comprend deux blocs&amp;nbsp;: développer la partie front-end, puis la partie back-end, d'une application web ou web mobile sécurisée. Son enregistrement court jusqu'au 1er septembre 2028.&lt;/p&gt;

&lt;p&gt;Les développeurs en poste sont pourtant majoritairement diplômés. Dans l'enquête mondiale 2025 de Stack Overflow, 45,9&amp;nbsp;% des développeurs professionnels déclarent une licence («&amp;nbsp;bachelor&amp;nbsp;»), 28&amp;nbsp;% un master, 11,5&amp;nbsp;% des études supérieures sans diplôme et 5,1&amp;nbsp;% le niveau du secondaire (&lt;a href="https://survey.stackoverflow.co/2025/developers" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2025&lt;/a&gt;). L'échantillon est volontaire et international. La même enquête indique que 5,2&amp;nbsp;% des répondants ont appris à coder en bootcamp, plusieurs réponses étant possibles.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reconversion développeur web à 40 ans : ce que dit la loi
&lt;/h2&gt;

&lt;p&gt;L'âge ne peut pas légalement vous être opposé à l'embauche, mais l'alternance a des limites d'âge. Service Public liste l'âge parmi les critères de discrimination interdits, et rappelle que la discrimination est punie de trois ans d'emprisonnement et 45&amp;nbsp;000&amp;nbsp;€ d'amende au plus (&lt;a href="https://www.service-public.gouv.fr/particuliers/vosdroits/F19448" rel="noopener noreferrer"&gt;Service Public&lt;/a&gt;, page vérifiée le 10 mars 2026).&lt;/p&gt;

&lt;p&gt;La loi n'efface pas la pratique. Selon le 18e baromètre du Défenseur des droits et de l'Organisation internationale du travail, 14&amp;nbsp;% des personnes interrogées déclarent une discrimination lors d'une recherche d'emploi, et l'âge est le premier motif cité, par 42&amp;nbsp;% d'entre elles (&lt;a href="https://www.defenseurdesdroits.fr/sites/default/files/2025-12/ddd_EAD-2024_OIT-18_DP_20251204.pdf" rel="noopener noreferrer"&gt;Défenseur des droits et OIT&lt;/a&gt;, 4 décembre 2025).&lt;/p&gt;

&lt;p&gt;Deux limites comptent pour une reconversion développeur web en alternance&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Le contrat d'apprentissage&lt;/strong&gt; est ouvert jusqu'à 29&amp;nbsp;ans révolus, 35&amp;nbsp;ans dans quelques cas, et sans limite pour une personne reconnue travailleur handicapé (&lt;a href="https://www.service-public.gouv.fr/particuliers/vosdroits/F2918" rel="noopener noreferrer"&gt;Service Public&lt;/a&gt;, page vérifiée le 8 septembre 2026).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le contrat de professionnalisation&lt;/strong&gt; reste accessible à partir de 26&amp;nbsp;ans aux demandeurs d'emploi et aux bénéficiaires du RSA, de l'ASS ou de l'AAH (&lt;a href="https://www.service-public.gouv.fr/particuliers/vosdroits/F15478" rel="noopener noreferrer"&gt;Service Public&lt;/a&gt;, page vérifiée le 22 juillet 2026).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aucune source lue ne fixe d'âge au-delà duquel une reconversion échouerait. Les témoignages publiés par des organismes sont des cas choisis, pas des taux.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les cinq voies de formation, comparées
&lt;/h2&gt;

&lt;p&gt;Aucune voie n'est à la fois gratuite, rapide et certifiée&amp;nbsp;: chacune échange du temps, de l'argent ou de l'encadrement. Le tableau reprend ce que nous avons relevé le 1er novembre 2026.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Voie&lt;/th&gt;
&lt;th&gt;Durée relevée&lt;/th&gt;
&lt;th&gt;Coût relevé&lt;/th&gt;
&lt;th&gt;Certification&lt;/th&gt;
&lt;th&gt;Pour qui&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Autodidacte&lt;/td&gt;
&lt;td&gt;Libre&lt;/td&gt;
&lt;td&gt;Ressources gratuites possibles&lt;/td&gt;
&lt;td&gt;Aucune, sauf candidature individuelle au titre&lt;/td&gt;
&lt;td&gt;Personnes très autonomes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bootcamp (exemple&amp;nbsp;: Le Wagon)&lt;/td&gt;
&lt;td&gt;2&amp;nbsp;mois à temps plein ou 6&amp;nbsp;mois à temps partiel, selon l'éditeur&lt;/td&gt;
&lt;td&gt;Prix non affiché sur la page consultée&lt;/td&gt;
&lt;td&gt;RNCP39261, niveau 6, certificateur Le Wagon&lt;/td&gt;
&lt;td&gt;Personnes disponibles, financement prévu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Titre pro DWWM (exemple&amp;nbsp;: Afpa)&lt;/td&gt;
&lt;td&gt;8&amp;nbsp;mois environ, 1&amp;nbsp;155&amp;nbsp;heures, dont 10&amp;nbsp;semaines en entreprise&lt;/td&gt;
&lt;td&gt;11&amp;nbsp;512&amp;nbsp;€ net de taxe, indicatif&lt;/td&gt;
&lt;td&gt;RNCP37674, niveau 5, ministère du Travail&lt;/td&gt;
&lt;td&gt;Reconversion encadrée et finançable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alternance&lt;/td&gt;
&lt;td&gt;Selon le contrat&lt;/td&gt;
&lt;td&gt;Formation prise en charge, salaire versé&lt;/td&gt;
&lt;td&gt;Diplôme ou titre RNCP&lt;/td&gt;
&lt;td&gt;Apprentissage&amp;nbsp;: moins de 30&amp;nbsp;ans, sauf exceptions. Contrat de professionnalisation après 26&amp;nbsp;ans&amp;nbsp;: demandeurs d'emploi, bénéficiaires du RSA, de l'ASS ou de l'AAH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Université, formation continue&lt;/td&gt;
&lt;td&gt;Variable&lt;/td&gt;
&lt;td&gt;Variable&lt;/td&gt;
&lt;td&gt;Diplôme national&lt;/td&gt;
&lt;td&gt;Viser un niveau licence ou plus&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sources&amp;nbsp;: &lt;a href="https://www.afpa.fr/formation-qualifiante/developpeur-web-et-web-mobile" rel="noopener noreferrer"&gt;page de l'Afpa&lt;/a&gt;, &lt;a href="https://www.lewagon.com/fr/paris/web-development-course" rel="noopener noreferrer"&gt;page de Le Wagon Paris&lt;/a&gt;, fiches &lt;a href="https://www.francecompetences.fr/recherche/rncp/37674/" rel="noopener noreferrer"&gt;RNCP37674&lt;/a&gt; et &lt;a href="https://www.francecompetences.fr/recherche/rncp/39261/" rel="noopener noreferrer"&gt;RNCP39261&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La voie autodidacte&lt;/strong&gt; coûte peu en argent, beaucoup en discipline. Nos guides pour &lt;a href="https://neurautech.com/apprendre-javascript/" rel="noopener noreferrer"&gt;apprendre JavaScript&lt;/a&gt; et pour &lt;a href="https://neurautech.com/apprendre-python-guide-complet-pour-debutants/" rel="noopener noreferrer"&gt;apprendre Python&lt;/a&gt; proposent chacun un plan gratuit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le bootcamp&lt;/strong&gt; promet de la vitesse&amp;nbsp;: Le Wagon annonce 40&amp;nbsp;heures de travail préparatoire, puis 40&amp;nbsp;heures par semaine à temps plein ou 16&amp;nbsp;heures à temps partiel. La version intensive ne laisse pas de place à un emploi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le titre professionnel&lt;/strong&gt; est la voie la plus lisible pour un recruteur français. L'Afpa détaille deux modules de 11&amp;nbsp;semaines, 10&amp;nbsp;semaines en entreprise et une semaine d'examen. Elle affiche, pour ses stagiaires et des données 2025, 69,80&amp;nbsp;% de réussite et 44&amp;nbsp;% d'accès à l'emploi dans les six mois&amp;nbsp;: ce sont ses chiffres, selon sa méthode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1dc2anohog4x2o8a8hre.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1dc2anohog4x2o8a8hre.webp" alt="Barres groupées&amp;nbsp;: taux d'insertion publiés par France Compétences pour des promotions anciennes et non comparables, 60&amp;nbsp;%, 55&amp;nbsp;% et 83&amp;nbsp;% pour les certifiés 2021 du titre Développeur web et web mobile, 74&amp;nbsp;%, 46&amp;nbsp;% et 47&amp;nbsp;% pour les certifiés 2022 de la certification RNCP39261" width="799" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lisez la fiche RNCP avant de signer.&lt;/strong&gt; Pour le titre DWWM, promotion 2021&amp;nbsp;: 3&amp;nbsp;899 certifiés, 60&amp;nbsp;% en emploi à six mois, 55&amp;nbsp;% dans le métier visé à six mois, 83&amp;nbsp;% dans le métier visé à deux ans. Pour la certification RNCP39261, promotion 2022&amp;nbsp;: 606 certifiés, 74&amp;nbsp;%, 46&amp;nbsp;% et 47&amp;nbsp;%. Années, niveaux et publics diffèrent, donc ces chiffres ne se comparent pas directement. La fiche RNCP37674, publiée en 2023, remplace la RNCP31114&amp;nbsp;: la promotion 2021 a donc passé, selon toute vraisemblance, la version précédente du titre. Ces taux sont aussi antérieurs au repli des recrutements de 2025 et 2026. Ils montrent surtout l'écart possible entre «&amp;nbsp;un emploi&amp;nbsp;» et «&amp;nbsp;un emploi de développeur&amp;nbsp;».&lt;/p&gt;




&lt;h2&gt;
  
  
  Financer la formation : CPF, PTP, AIF, démission
&lt;/h2&gt;

&lt;p&gt;Le bon dispositif dépend de votre statut&amp;nbsp;: salarié, demandeur d'emploi ou salarié qui veut démissionner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le CPF.&lt;/strong&gt; Un salarié au moins à mi-temps acquiert 500&amp;nbsp;€ par an, dans la limite de 5&amp;nbsp;000&amp;nbsp;€, ou 800&amp;nbsp;€ par an jusqu'à 8&amp;nbsp;000&amp;nbsp;€ sans qualification de niveau CAP ou BEP, ou pour un travailleur reconnu handicapé (&lt;a href="https://www.service-public.gouv.fr/particuliers/vosdroits/F10705" rel="noopener noreferrer"&gt;Service Public&lt;/a&gt;, page vérifiée le 27 juin 2026). Une formation à 11&amp;nbsp;512&amp;nbsp;€ dépasse donc le plafond d'un salarié qualifié&amp;nbsp;: prévoyez un cofinancement.&lt;/p&gt;

&lt;p&gt;La participation forfaitaire est de 150&amp;nbsp;€ depuis le 2 avril 2026. Elle ne s'applique pas aux demandeurs d'emploi, ni quand l'employeur abonde la formation (&lt;a href="https://www.service-public.gouv.fr/particuliers/actualites/A17364" rel="noopener noreferrer"&gt;Service Public&lt;/a&gt;, mise à jour du 2 avril 2026). Le projet de loi de finances pour 2027 pourrait la majorer, signale la &lt;a href="https://www.service-public.gouv.fr/particuliers/vosdroits/F10705" rel="noopener noreferrer"&gt;page CPF de Service Public&lt;/a&gt;, vérifiée le 27 juin 2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le projet de transition professionnelle (PTP).&lt;/strong&gt; C'est le dispositif des salariés qui changent de métier sans démissionner. Il faut deux ans d'activité salariée, dont un an dans l'entreprise, et une formation certifiante. Pour une formation de six mois ou plus à temps plein, la demande à l'employeur part au plus tard 120&amp;nbsp;jours avant le début (&lt;a href="https://www.service-public.gouv.fr/particuliers/vosdroits/F14018" rel="noopener noreferrer"&gt;Service Public&lt;/a&gt;, page vérifiée le 1er juin 2026).&lt;/p&gt;

&lt;p&gt;Le salarié perçoit 100&amp;nbsp;% de son salaire moyen de référence si celui-ci ne dépasse pas 2 Smic, soit 3&amp;nbsp;734,04&amp;nbsp;€ selon la page. Au-delà, il perçoit 90&amp;nbsp;% pour une formation d'un an ou de 1&amp;nbsp;200&amp;nbsp;heures au plus&amp;nbsp;; pour une formation plus longue, 90&amp;nbsp;% la première année puis 60&amp;nbsp;%, sans descendre sous 3&amp;nbsp;734,04&amp;nbsp;€. L'accord n'est pas automatique&amp;nbsp;: Transitions Pro juge la cohérence du projet, la pertinence du parcours et les perspectives d'emploi dans la région (&lt;a href="https://www.transitionspro.fr/nos-dispositifs/projet-de-transition-professionnelle/" rel="noopener noreferrer"&gt;Transitions Pro&lt;/a&gt;). Un conseil en évolution professionnelle, gratuit, aide à monter le dossier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L'AIF de France Travail.&lt;/strong&gt; Elle s'adresse aux demandeurs d'emploi inscrits et prend en charge les frais pédagogiques, en totalité ou en complément, pour une formation validée avec votre conseiller, sur devis de l'organisme. Si la formation est éligible au CPF, la page indique de mobiliser d'abord votre CPF, puis de demander à France Travail de financer le reste à charge depuis Mon Compte Formation (&lt;a href="https://www.francetravail.fr/candidat/en-formation/mes-aides-financieres/laide-individuelle-a-la-formatio.html" rel="noopener noreferrer"&gt;France Travail&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La démission pour reconversion.&lt;/strong&gt; Elle peut ouvrir droit au chômage, sous conditions strictes et après validation du projet avant de démissionner. Notre guide pour &lt;a href="https://neurautech.com/devenir-developpeur-freelance-en-2026-guide-complet/" rel="noopener noreferrer"&gt;devenir développeur freelance&lt;/a&gt; en expose les conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Notre plan indicatif sur 6 à 12 mois
&lt;/h2&gt;

&lt;p&gt;Un plan de reconversion développeur web tient en quatre étapes, et sa durée dépend surtout du temps hebdomadaire disponible. Les durées ci-dessous sont nos repères, pas des moyennes mesurées.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mois 1&amp;nbsp;: tester avant de payer.&lt;/strong&gt; Faites quelques dizaines d'heures de bases gratuites en HTML, CSS et JavaScript. Si corriger une erreur pendant une heure vous rebute, mieux vaut le savoir avant de signer un devis. Prenez rendez-vous avec un conseiller en évolution professionnelle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mois 2 et 3&amp;nbsp;: choisir la voie et le financement.&lt;/strong&gt; Comparez trois organismes, leurs fiches RNCP et leurs taux d'insertion. Déposez le dossier de PTP ou d'AIF dans les délais. Avec un PTP, la demande à l'employeur part au moins 120&amp;nbsp;jours avant une formation longue à temps plein, puis Transitions Pro examine le dossier&amp;nbsp;: le début de la formation recule alors de plusieurs mois, et le parcours dépasse souvent douze mois.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mois 4 à 10&amp;nbsp;: se former et produire.&lt;/strong&gt; Visez un projet par mois, de plus en plus complet&amp;nbsp;: une page statique, une application qui appelle une API, puis une application avec base de données. Les bases de &lt;a href="https://neurautech.com/sql-debutant-cours-exercices/" rel="noopener noreferrer"&gt;SQL&lt;/a&gt; font partie du titre DWWM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mois 9 à 12&amp;nbsp;: chercher en parallèle.&lt;/strong&gt; Postulez avant la fin de la formation. La période en entreprise prévue par le parcours, par exemple à l'Afpa, peut devenir une première porte d'entrée.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Six mois supposent une formation intensive à temps plein et un financement acquis. À temps partiel, en gardant son emploi, ou avec les délais d'un PTP, comptez plutôt douze mois ou plus.&lt;/p&gt;




&lt;h2&gt;
  
  
  Construire des preuves et chercher le premier poste
&lt;/h2&gt;

&lt;p&gt;Sans diplôme d'informatique ni expérience, vos projets sont votre principale preuve&amp;nbsp;: un recruteur ne peut pas vérifier une liste de technologies, il peut ouvrir un dépôt.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Peu de projets, mais finis.&lt;/strong&gt; Trois applications en ligne, avec un README qui explique le problème, les choix et les limites, valent mieux que dix exercices de tutoriel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un historique lisible.&lt;/strong&gt; Des commits réguliers sur GitHub montrent une méthode. Un pipeline de tests, comme dans notre &lt;a href="https://neurautech.com/github-actions-tutoriel/" rel="noopener noreferrer"&gt;tutoriel GitHub Actions&lt;/a&gt;, distingue un projet sérieux d'un exercice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Votre ancien métier comme atout.&lt;/strong&gt; Un outil qui résout un vrai problème de votre secteur précédent raconte une histoire qu'aucun autre candidat ne peut raconter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notre guide du &lt;a href="https://neurautech.com/portfolio-developpeur-comment-le-construire-2026/" rel="noopener noreferrer"&gt;portfolio de développeur&lt;/a&gt; détaille la structure d'une page projet. Utilisez l'IA comme aide, pas comme auteur&amp;nbsp;: un recruteur vous fera expliquer votre code.&lt;/p&gt;

&lt;p&gt;Pour la recherche, le CV tient sur une page, avec les projets avant l'ancien métier et des liens vérifiables&amp;nbsp;: notre guide du &lt;a href="https://neurautech.com/cv-developpeur-junior/" rel="noopener noreferrer"&gt;CV de développeur junior&lt;/a&gt; inclut un exemple de reconversion. Ciblez les agences web, les entreprises de services du numérique et les PME de votre ancien secteur.&lt;/p&gt;

&lt;p&gt;Ne changez pas de langage pour suivre un classement&amp;nbsp;: notre article sur les &lt;a href="https://neurautech.com/langages-de-programmation-les-mieux-payes-en-2026/" rel="noopener noreferrer"&gt;langages les mieux payés&lt;/a&gt; montre ce que ces classements ne disent pas. Le freelance dès la sortie de formation cumule deux difficultés, trouver des clients et facturer sans références.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les signaux d'alerte chez un organisme de formation
&lt;/h2&gt;

&lt;p&gt;Commencez par deux vérifications administratives&amp;nbsp;: la certification qualité, puis la fiche RNCP. La certification Qualiopi est obligatoire depuis le 1er janvier 2022 pour les prestataires qui veulent bénéficier de fonds publics et mutualisés (&lt;a href="https://paca.dreets.gouv.fr/la-certification-qualite-qualiopi" rel="noopener noreferrer"&gt;DREETS Provence-Alpes-Côte d'Azur&lt;/a&gt;, mise à jour du 26 mai 2026). La &lt;a href="https://www.data.gouv.fr/datasets/liste-publique-des-organismes-de-formation-l-6351-7-1-du-code-du-travail" rel="noopener noreferrer"&gt;liste publique des organismes de formation&lt;/a&gt; du ministère du Travail indique ceux dont la certification est en cours de validité.&lt;/p&gt;

&lt;p&gt;Méfiez-vous ensuite du démarchage. France Travail décrit le scénario type&amp;nbsp;: un contact non sollicité propose une formation «&amp;nbsp;gratuite&amp;nbsp;», financée par le CPF, avec «&amp;nbsp;emploi garanti&amp;nbsp;», pour obtenir vos identifiants (&lt;a href="https://www.francetravail.fr/candidat/soyez-vigilants/tentative-darnaque-au-compte-cpf.html" rel="noopener noreferrer"&gt;France Travail&lt;/a&gt;). Le seul site officiel est moncompteformation.gouv.fr, et personne ne peut utiliser votre compte à votre place, même avec votre accord.&lt;/p&gt;

&lt;p&gt;Les signaux d'alerte listés par France Travail&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un appel, un SMS ou un message que vous n'avez pas sollicité&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;une formation «&amp;nbsp;100&amp;nbsp;% gratuite&amp;nbsp;» ou «&amp;nbsp;entièrement prise en charge&amp;nbsp;» sans conditions&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;une demande d'identifiants CPF, de numéro de sécurité sociale ou de code FranceConnect&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;une urgence artificielle&amp;nbsp;: «&amp;nbsp;votre crédit va expirer&amp;nbsp;», «&amp;nbsp;il reste peu de places&amp;nbsp;»&amp;nbsp;;&lt;/li&gt;
&lt;li&gt;un organisme absent du catalogue officiel, ou un faux logo Qualiopi.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En cas de doute, France Travail conseille de changer vos mots de passe, de signaler la fraude sur Mon Compte Formation et sur cybermalveillance.gouv.fr, et cite Info Escroqueries au 0&amp;nbsp;805&amp;nbsp;805&amp;nbsp;817. Notre guide sur le &lt;a href="https://neurautech.com/phishing-comment-reconnaitre-et-eviter-les-arnaques/" rel="noopener noreferrer"&gt;phishing&lt;/a&gt; détaille les bons réflexes. Enfin, un taux d'embauche sérieux a une année, une population et une définition&amp;nbsp;: sinon, demandez la fiche RNCP.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les pages de l'Apec elles-mêmes&lt;/strong&gt;, qui refusaient nos requêtes&amp;nbsp;: les chiffres de l'Apec viennent de L'Étudiant et du Monde Informatique, qui ne concordent pas exactement sur la hausse attendue en 2026 dans l'informatique (4&amp;nbsp;% ou 5&amp;nbsp;%)&amp;nbsp;; Le Monde Informatique ne précise pas la base 2025.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La part des débutants dans les projets BMO.&lt;/strong&gt; L'enquête ne distingue pas le niveau d'expérience recherché.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le prix d'un bootcamp&lt;/strong&gt;, absent de la page de Le Wagon consultée, et le montant maximal de l'AIF, absent de la page de France Travail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un taux de réussite des reconversions&lt;/strong&gt; par âge ou par voie&amp;nbsp;: nous n'en avons trouvé aucun de source publique fiable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'issue du projet de loi de finances pour 2027&lt;/strong&gt; sur la participation au CPF.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Peut-on devenir développeur web sans diplôme ?
&lt;/h3&gt;

&lt;p&gt;Oui pour la certification&amp;nbsp;: la fiche &lt;a href="https://www.francecompetences.fr/recherche/rncp/37674/" rel="noopener noreferrer"&gt;RNCP37674&lt;/a&gt; du titre «&amp;nbsp;Développeur web et web mobile&amp;nbsp;» n'affiche aucun prérequis de diplôme. Selon &lt;a href="https://survey.stackoverflow.co/2025/developers" rel="noopener noreferrer"&gt;Stack Overflow&lt;/a&gt;, la majorité des développeurs professionnels sont toutefois diplômés&amp;nbsp;: sans diplôme, vos projets doivent compenser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Est-il trop tard pour une reconversion développeur web à 40 ans ?
&lt;/h3&gt;

&lt;p&gt;Aucune source ne fixe d'âge limite, et l'âge est un critère de discrimination interdit selon &lt;a href="https://www.service-public.gouv.fr/particuliers/vosdroits/F19448" rel="noopener noreferrer"&gt;Service Public&lt;/a&gt;. L'apprentissage est en principe limité à 29&amp;nbsp;ans révolus, mais le contrat de professionnalisation reste ouvert aux demandeurs d'emploi de 26&amp;nbsp;ans et plus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combien coûte une formation de développeur web ?
&lt;/h3&gt;

&lt;p&gt;De presque rien en autodidacte à parfois plus de 10&amp;nbsp;000&amp;nbsp;€ pour une formation certifiante&amp;nbsp;: par exemple, l'Afpa affiche 11&amp;nbsp;512&amp;nbsp;€ net de taxe, à titre indicatif, pour son parcours &lt;a href="https://www.afpa.fr/formation-qualifiante/developpeur-web-et-web-mobile" rel="noopener noreferrer"&gt;Développeur web et web mobile&lt;/a&gt;. Le CPF, le PTP ou l'AIF peuvent couvrir tout ou partie du coût selon votre statut.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combien de temps faut-il pour se reconvertir ?
&lt;/h3&gt;

&lt;p&gt;Environ huit mois pour un titre professionnel à l'Afpa, période en entreprise comprise, ou deux à six mois de bootcamp selon le rythme annoncé par Le Wagon. Avec un projet de transition professionnelle, comptez en plus au moins quatre mois de démarches avant le début de la formation. Il faut ajouter la recherche d'emploi, dont aucune source lue ne mesure la durée pour les personnes en reconversion.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse fondée exclusivement sur des sources publiques&amp;nbsp;: enquête Besoins en main-d'œuvre et pages de France Travail, fiches RNCP de France Compétences, pages de Service Public, de Transitions Pro et de la DREETS, baromètre du Défenseur des droits, enquêtes de Stack Overflow et de HackerRank, presse citant l'Apec, pages de l'Afpa et de Le Wagon. Les chiffres d'organismes et d'éditeurs sont signalés comme tels. Aucune formation n'a été suivie ni testée, aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux organismes mentionnés.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou une règle périmée&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 1er novembre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Julia M Cameron via Pexels (Pexels License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>reconversion</category>
      <category>developpeurweb</category>
      <category>formation</category>
      <category>cpf</category>
    </item>
    <item>
      <title>Entretien technique développeur : les étapes, 18 questions et un exercice corrigé</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sun, 01 Nov 2026 15:35:43 +0000</pubDate>
      <link>https://dev.to/neurau_tech/entretien-technique-developpeur-les-etapes-18-questions-et-un-exercice-corrige-473e</link>
      <guid>https://dev.to/neurau_tech/entretien-technique-developpeur-les-etapes-18-questions-et-un-exercice-corrige-473e</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Un entretien technique développeur n'est presque jamais un examen unique&amp;nbsp;: c'est une suite d'étapes, de la présélection téléphonique au live coding, et chacune se prépare différemment. Ce guide s'adresse aux développeurs juniors et confirmés qui postulent en France.&lt;/p&gt;

&lt;p&gt;Il repose sur des sources publiques consultées le 1er novembre 2026, sans test auprès de recruteurs&amp;nbsp;; seul le code de l'exercice corrigé a été exécuté.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selon l'Apec, 71&amp;nbsp;% des entreprises ayant recruté au moins un cadre en 2025, tous métiers confondus, ont utilisé une présélection téléphonique et 48&amp;nbsp;% des tests. Préparez-vous aux deux.&lt;/li&gt;
&lt;li&gt;Les questions techniques portent sur les fondamentaux&amp;nbsp;: égalité en JavaScript, méthodes HTTP, jointures SQL, Git, Docker, sécurité, complexité. Les 18 réponses modèles ci-dessous tiennent en quelques lignes chacune.&lt;/li&gt;
&lt;li&gt;En live coding, on évalue autant votre raisonnement que votre code&amp;nbsp;: reformulez, donnez une solution simple, puis améliorez-la à voix haute.&lt;/li&gt;
&lt;li&gt;Le Code du travail impose que les méthodes de recrutement vous soient annoncées à l'avance et qu'elles soient pertinentes pour le poste.&lt;/li&gt;
&lt;li&gt;L'IA n'est pas interdite partout&amp;nbsp;: Canva exige son usage dans certains entretiens, Anthropic l'interdit sauf indication contraire. Demandez toujours la règle.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Les étapes d'un recrutement de développeur en France
&lt;/h2&gt;

&lt;p&gt;Aucune source ne fixe un modèle unique, mais le cours d'OpenClassrooms sur les tests techniques décrit une séquence type&amp;nbsp;: tri du CV, appel de qualification non technique, tests techniques, puis entretien final avec un fondateur ou le futur manager, où le salaire est discuté (&lt;a href="https://openclassrooms.com/fr/courses/6045521-preparez-vous-aux-tests-techniques-pour-devenir-developpeur/6171071-situez-les-tests-techniques-dans-le-processus-de-recrutement" rel="noopener noreferrer"&gt;OpenClassrooms&lt;/a&gt;, consulté le 1er novembre 2026).&lt;/p&gt;

&lt;p&gt;Les chiffres français les plus récents viennent du baromètre 2026 de l'Apec, mené auprès de 1&amp;nbsp;150 entreprises ayant recruté au moins un cadre en 2025, tous métiers confondus (&lt;a href="https://www.banquedesterritoires.fr/sites/default/files/2026-05/210526%20-%20Etude%20Apec%20-%20Pratiques%20de%20recrutement%202026.pdf" rel="noopener noreferrer"&gt;Apec, Pratiques de recrutement de cadres 2026&lt;/a&gt;, mai 2026, PDF hébergé par la Banque des Territoires).&lt;/p&gt;

&lt;p&gt;Ce qu'il relève pour 2025&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;La présélection téléphonique&lt;/strong&gt;&amp;nbsp;: 71&amp;nbsp;% des entreprises y ont recouru pour au moins un recrutement de cadre.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les tests de recrutement&lt;/strong&gt;&amp;nbsp;: 48&amp;nbsp;% en ont utilisé, et 29&amp;nbsp;% de l'ensemble des entreprises ont eu recours à des mises en situation professionnelle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les compétences comportementales&lt;/strong&gt;&amp;nbsp;: 96&amp;nbsp;% des entreprises les jugent au moins aussi importantes que les compétences techniques.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La durée&lt;/strong&gt;&amp;nbsp;: le dernier recrutement de cadre a pris en moyenne 12&amp;nbsp;semaines, un niveau stable depuis deux ans.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depuis 2019, la présélection téléphonique progresse et les tests restent proches de la moitié des entreprises, selon le graphique de la page 8 du rapport.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffr38je21smlp6pjbj281.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffr38je21smlp6pjbj281.webp" alt="Graphique en courbes de l'Apec, utile pour préparer un entretien technique développeur&amp;nbsp;: présélection téléphonique de 58&amp;nbsp;% en 2019 à 71&amp;nbsp;% en 2025, tests de recrutement de 52&amp;nbsp;% en 2019 à 48&amp;nbsp;% en 2025" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Les étapes les plus courantes, avec une durée seulement quand une source la donne&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Étape&lt;/th&gt;
&lt;th&gt;Ce qui est évalué&lt;/th&gt;
&lt;th&gt;Durée indiquée par une source&lt;/th&gt;
&lt;th&gt;Qui vous reçoit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Présélection téléphonique&lt;/td&gt;
&lt;td&gt;Motivation, parcours, adéquation au poste, salaire attendu&lt;/td&gt;
&lt;td&gt;Non précisée&lt;/td&gt;
&lt;td&gt;Recruteur ou RH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discussion technique&lt;/td&gt;
&lt;td&gt;Fondamentaux, projets passés, capacité à expliquer&lt;/td&gt;
&lt;td&gt;Généralement 30 à 45&amp;nbsp;minutes, jusqu'à 1&amp;nbsp;h&amp;nbsp;30 (OpenClassrooms)&lt;/td&gt;
&lt;td&gt;Un ou deux développeurs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test technique en ligne ou à la maison&lt;/td&gt;
&lt;td&gt;Code fonctionnel, lisibilité, tests&lt;/td&gt;
&lt;td&gt;Fixée par l'entreprise&lt;/td&gt;
&lt;td&gt;Correction par l'équipe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live coding&lt;/td&gt;
&lt;td&gt;Raisonnement, communication, gestion des cas limites&lt;/td&gt;
&lt;td&gt;Fixée par l'entreprise&lt;/td&gt;
&lt;td&gt;Un ou deux développeurs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conception de système (profil confirmé)&lt;/td&gt;
&lt;td&gt;Découpage, compromis, montée en charge&lt;/td&gt;
&lt;td&gt;Fixée par l'entreprise&lt;/td&gt;
&lt;td&gt;Développeur senior ou architecte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entretien final&lt;/td&gt;
&lt;td&gt;Adéquation à l'équipe, projet, rémunération&lt;/td&gt;
&lt;td&gt;Non précisée&lt;/td&gt;
&lt;td&gt;Manager ou fondateur&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Préparer chaque étape de l'entretien technique développeur
&lt;/h2&gt;

&lt;p&gt;Chaque étape d'un entretien technique développeur teste une chose différente, donc la préparation change d'une étape à l'autre.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La présélection téléphonique.&lt;/strong&gt; Préparez une présentation de deux minutes&amp;nbsp;: ce que vous savez faire, un projet concret, ce que vous cherchez. Ayez votre fourchette de salaire prête&amp;nbsp;: notre comparatif des &lt;a href="https://neurautech.com/salaires-developpeurs-france-2026-grille-complete/" rel="noopener noreferrer"&gt;salaires des développeurs en France&lt;/a&gt; aide à la fixer. Votre CV doit raconter la même histoire&amp;nbsp;; pour sa structure, voir notre guide du &lt;a href="https://neurautech.com/cv-developpeur-junior/" rel="noopener noreferrer"&gt;CV développeur junior&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La discussion technique.&lt;/strong&gt; Relisez vos propres projets&amp;nbsp;: on vous demandera pourquoi vous avez choisi telle base de données ou telle bibliothèque. Un &lt;a href="https://neurautech.com/portfolio-developpeur-comment-le-construire-2026/" rel="noopener noreferrer"&gt;portfolio de développeur&lt;/a&gt; à jour sert de support. Le chapitre d'OpenClassrooms sur les &lt;a href="https://openclassrooms.com/fr/courses/6045521-preparez-vous-aux-tests-techniques-pour-devenir-developpeur/6171136-preparez-vous-aux-discussions-techniques" rel="noopener noreferrer"&gt;discussions techniques&lt;/a&gt; conseille de faire des schémas et d'admettre ce que l'on ne sait pas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le test technique et le live coding.&lt;/strong&gt; Avant un test technique, entraînez-vous sur des exercices courts, chronométrés, en parlant à voix haute. Révisez les bases de votre langage&amp;nbsp;: notre parcours pour &lt;a href="https://neurautech.com/apprendre-javascript/" rel="noopener noreferrer"&gt;apprendre JavaScript&lt;/a&gt; et notre &lt;a href="https://neurautech.com/sql-debutant-cours-exercices/" rel="noopener noreferrer"&gt;cours de SQL pour débutants&lt;/a&gt; couvrent la plupart des questions ci-dessous.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La conception de système, pour un poste confirmé.&lt;/strong&gt; On vous demande de concevoir un service (raccourcisseur de liens, fil d'actualité, file de tâches). Une démarche claire compte plus que la bonne réponse&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clarifier les besoins et le volume attendu.&lt;/li&gt;
&lt;li&gt;Définir l'API et le modèle de données.&lt;/li&gt;
&lt;li&gt;Dessiner les composants&amp;nbsp;: serveur, base, cache, file de messages.&lt;/li&gt;
&lt;li&gt;Repérer le point qui cassera en premier et proposer un compromis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;L'entretien final.&lt;/strong&gt; Préparez deux ou trois situations vécues&amp;nbsp;: un bug difficile, un désaccord technique, une erreur corrigée.&lt;/p&gt;




&lt;h2&gt;
  
  
  18 questions techniques classiques et leurs réponses
&lt;/h2&gt;

&lt;p&gt;Ces questions techniques couvrent les bases d'un poste web. En entretien, donnez la réponse en une phrase, puis un exemple.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Quelle différence entre &lt;code&gt;==&lt;/code&gt; et &lt;code&gt;===&lt;/code&gt;&amp;nbsp;?&lt;/strong&gt; &lt;code&gt;===&lt;/code&gt; compare sans conversion de type&amp;nbsp;; &lt;code&gt;==&lt;/code&gt; convertit d'abord les opérandes. Ainsi &lt;code&gt;'1' == 1&lt;/code&gt; vaut &lt;code&gt;true&lt;/code&gt;, mais &lt;code&gt;'1' === 1&lt;/code&gt; \1\2, égalité stricte](&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Operators/Strict_equality)" rel="noopener noreferrer"&gt;https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Operators/Strict_equality)&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;let&lt;/code&gt; et &lt;code&gt;const&lt;/code&gt;&amp;nbsp;?&lt;/strong&gt; &lt;code&gt;var&lt;/code&gt; a une portée de fonction&amp;nbsp;; &lt;code&gt;let&lt;/code&gt; et &lt;code&gt;const&lt;/code&gt; ont une portée de bloc. &lt;code&gt;const&lt;/code&gt; interdit la réaffectation de la variable, pas la modification de l'objet qu'elle contient (&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Statements/let" rel="noopener noreferrer"&gt;MDN, let&lt;/a&gt;). Exemple classique&amp;nbsp;: &lt;code&gt;for (var i = 0; i &amp;lt; 3; i++) setTimeout(() =&amp;gt; console.log(i));&lt;/code&gt; affiche trois fois &lt;code&gt;3&lt;/code&gt;, car les trois rappels partagent la même variable&amp;nbsp;; avec &lt;code&gt;let&lt;/code&gt;, chaque tour a la sienne et la boucle affiche &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Qu'est-ce qu'une closure (fermeture)&amp;nbsp;?&lt;/strong&gt; Une fonction qui garde l'accès aux variables de la portée où elle a été créée, même après la fin de cette portée. Exemple&amp;nbsp;: &lt;code&gt;function compteur() { let n = 0; return () =&amp;gt; ++n; }&lt;/code&gt; (&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide/Closures" rel="noopener noreferrer"&gt;MDN, closures&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dans quel ordre s'affichent &lt;code&gt;console.log('A')&lt;/code&gt;, un &lt;code&gt;setTimeout&lt;/code&gt; à 0&amp;nbsp;ms, une promesse résolue puis &lt;code&gt;console.log('B')&lt;/code&gt;&amp;nbsp;?&lt;/strong&gt; A, B, puis la promesse, puis le &lt;code&gt;setTimeout&lt;/code&gt;. Le code synchrone passe d'abord&amp;nbsp;; les rappels de promesses (microtâches) passent avant les tâches comme &lt;code&gt;setTimeout&lt;/code&gt; (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Execution_model" rel="noopener noreferrer"&gt;MDN, modèle d'exécution&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP et API REST
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;5. GET, POST, PUT, DELETE&amp;nbsp;: lesquelles sont idempotentes&amp;nbsp;?&lt;/strong&gt; GET, PUT et DELETE&amp;nbsp;: plusieurs requêtes identiques ont le même effet voulu qu'une seule. La réponse peut différer&amp;nbsp;: un second DELETE peut renvoyer 404 sans que la méthode cesse d'être idempotente. POST ne l'est pas. GET, HEAD et OPTIONS sont en plus des méthodes sûres, en lecture seule (&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.2" rel="noopener noreferrer"&gt;RFC 9110, section 9.2&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. 401 ou 403&amp;nbsp;?&lt;/strong&gt; 401 signifie que la requête n'a pas d'identifiants valides&amp;nbsp;; le serveur doit alors indiquer comment s'authentifier. 403 signifie que le serveur a compris la requête mais refuse de l'exécuter. Selon la RFC 9110, le client ne devrait pas la répéter automatiquement avec les mêmes identifiants, peut réessayer avec d'autres, et le refus peut n'avoir aucun lien avec les identifiants (&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5.4" rel="noopener noreferrer"&gt;RFC 9110, section 15.5.4&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Quel code renvoyer après une création et après une suppression&amp;nbsp;?&lt;/strong&gt; 201 Created après une création, avec l'adresse de la ressource dans l'en-tête &lt;code&gt;Location&lt;/code&gt;. Après un DELETE réussi, la RFC 9110 prévoit 204 sans corps, 200 avec un corps décrivant le résultat, ou 202 si la suppression n'est pas encore faite (&lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.5" rel="noopener noreferrer"&gt;RFC 9110, section 9.3.5&lt;/a&gt;). Notre tutoriel pour &lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;créer une API REST avec Node.js et Express&lt;/a&gt; applique ces codes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. À quoi sert CORS&amp;nbsp;?&lt;/strong&gt; La politique de même origine du navigateur empêche une page de lire les réponses d'une autre origine. CORS permet au serveur d'assouplir cette règle par des en-têtes HTTP, en désignant les origines autorisées. Ce n'est pas un contrôle d'accès à votre API&amp;nbsp;: curl ou un autre serveur ignorent ces en-têtes (&lt;a href="https://developer.mozilla.org/fr/docs/Web/HTTP/Guides/CORS" rel="noopener noreferrer"&gt;MDN, CORS&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  SQL
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;9. INNER JOIN ou LEFT JOIN&amp;nbsp;?&lt;/strong&gt; INNER JOIN ne garde que les lignes qui ont une correspondance des deux côtés. LEFT JOIN garde toutes les lignes de la table de gauche, avec des &lt;code&gt;NULL&lt;/code&gt; quand il n'y a pas de correspondance. Pour trouver les clients sans commande&amp;nbsp;: &lt;code&gt;LEFT JOIN commandes ... WHERE commandes.id IS NULL&lt;/code&gt; (&lt;a href="https://www.postgresql.org/docs/current/tutorial-join.html" rel="noopener noreferrer"&gt;PostgreSQL, jointures&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. WHERE ou HAVING&amp;nbsp;?&lt;/strong&gt; WHERE filtre les lignes avant le regroupement&amp;nbsp;; HAVING filtre les groupes après le calcul des agrégats. &lt;code&gt;GROUP BY client_id HAVING COUNT(*) &amp;gt;= 2&lt;/code&gt; liste les clients qui ont au moins deux commandes (&lt;a href="https://www.postgresql.org/docs/current/tutorial-agg.html" rel="noopener noreferrer"&gt;PostgreSQL, agrégats&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11. Pourquoi ne pas indexer toutes les colonnes&amp;nbsp;?&lt;/strong&gt; Un index accélère la recherche, mais il occupe de l'espace et chaque insertion ou mise à jour doit aussi le maintenir&amp;nbsp;; on indexe donc les colonnes souvent filtrées, triées ou jointes (&lt;a href="https://www.postgresql.org/docs/current/indexes-intro.html" rel="noopener noreferrer"&gt;PostgreSQL, index&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Git
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;12. Merge ou rebase&amp;nbsp;?&lt;/strong&gt; Merge conserve l'historique et ajoute un commit de fusion, sauf en cas d'avance rapide&amp;nbsp;; rebase rejoue vos commits sur la pointe d'une autre branche et donne un historique linéaire, en réécrivant ces commits. Le livre Pro Git recommande de ne jamais rebaser des commits déjà poussés sur un dépôt public (&lt;a href="https://git-scm.com/book/fr/v2/Les-branches-avec-Git-Rebaser-Rebasing" rel="noopener noreferrer"&gt;Pro Git, rebaser&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13. Comment annuler un commit déjà poussé&amp;nbsp;?&lt;/strong&gt; Avec &lt;code&gt;git revert&lt;/code&gt;, qui crée un nouveau commit inverse sans réécrire l'historique partagé (&lt;a href="https://git-scm.com/docs/git-revert" rel="noopener noreferrer"&gt;git revert&lt;/a&gt;). &lt;code&gt;git reset&lt;/code&gt; déplace la branche et convient surtout aux commits encore locaux (&lt;a href="https://git-scm.com/docs/git-reset" rel="noopener noreferrer"&gt;git reset&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker et ligne de commande
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;14. Image ou conteneur&amp;nbsp;?&lt;/strong&gt; Une image est un modèle en lecture seule, construit en couches&amp;nbsp;; un conteneur est une instance exécutable de cette image, isolée du reste de la machine, avec sa propre couche inscriptible (&lt;a href="https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/" rel="noopener noreferrer"&gt;Docker, qu'est-ce qu'une image&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;15. Comment garder les données d'une base dans Docker&amp;nbsp;?&lt;/strong&gt; Avec un volume&amp;nbsp;: les données écrites dans le système de fichiers du conteneur disparaissent avec lui, alors qu'un volume est géré par Docker et survit à la suppression du conteneur (&lt;a href="https://docs.docker.com/engine/storage/volumes/" rel="noopener noreferrer"&gt;Docker, volumes&lt;/a&gt;). Notre tutoriel &lt;a href="https://neurautech.com/docker-pour-debutants/" rel="noopener noreferrer"&gt;Docker pour débutants&lt;/a&gt; le montre pas à pas. Pour les questions de terminal, révisez nos &lt;a href="https://neurautech.com/commandes-linux-de-base/" rel="noopener noreferrer"&gt;commandes Linux de base&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sécurité
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;16. Comment éviter une injection SQL&amp;nbsp;?&lt;/strong&gt; Avec des requêtes paramétrées (requêtes préparées), qui séparent le code SQL des données, plutôt qu'en concaténant des chaînes. C'est la première défense recommandée par l'OWASP (&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP, injection SQL&lt;/a&gt;). L'injection figure toujours dans le &lt;a href="https://top10.owasp.org/2025/" rel="noopener noreferrer"&gt;Top 10 2025 de l'OWASP&lt;/a&gt;, en cinquième position.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;17. Comment stocker un mot de passe&amp;nbsp;?&lt;/strong&gt; Jamais en clair ni chiffré de façon réversible&amp;nbsp;: on le hache avec un algorithme lent et salé. L'OWASP recommande Argon2id, puis scrypt si Argon2id est indisponible, bcrypt pour les systèmes existants et PBKDF2 quand la conformité FIPS-140 est exigée (&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP, stockage des mots de passe&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Algorithmique
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;18. Pourquoi un &lt;code&gt;Set&lt;/code&gt; est-il plus rapide qu'un tableau pour tester l'appartenance&amp;nbsp;?&lt;/strong&gt; &lt;code&gt;tableau.includes(x)&lt;/code&gt; parcourt les éléments un par un&amp;nbsp;: O(n). La spécification impose à un &lt;code&gt;Set&lt;/code&gt; un temps d'accès moyen sous-linéaire&amp;nbsp;; en pratique, les moteurs utilisent une table de hachage, avec une recherche en O(1) en moyenne, au prix de mémoire en plus (&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Set" rel="noopener noreferrer"&gt;MDN, Set&lt;/a&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Penser à voix haute en live coding : un exemple corrigé
&lt;/h2&gt;

&lt;p&gt;En live coding, l'intervieweur ne peut pas noter un raisonnement qu'il n'entend pas. Une méthode en six temps structure ce que vous dites&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reformuler&lt;/strong&gt; le problème et poser les questions de cadrage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Donner des exemples&lt;/strong&gt;, dont des cas limites&amp;nbsp;: tableau vide, doublons, valeur absente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proposer une solution simple&lt;/strong&gt; et annoncer sa complexité.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'améliorer&lt;/strong&gt; en expliquant le compromis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coder&lt;/strong&gt; en commentant les choix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tester à la main&lt;/strong&gt; sur les exemples, puis conclure sur la complexité.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;L'énoncé.&lt;/strong&gt; Un tableau de nombres et une cible sont donnés&amp;nbsp;; renvoyer les indices de deux éléments situés à des positions différentes dont la somme vaut la cible, ou &lt;code&gt;null&lt;/code&gt; s'il n'y en a pas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ce que vous dites d'abord.&lt;/strong&gt; «&amp;nbsp;Je cherche deux positions différentes. Le tableau peut-il être vide&amp;nbsp;? Une seule paire suffit&amp;nbsp;?&amp;nbsp;» Puis vos exemples&amp;nbsp;: &lt;code&gt;[2, 7, 11, 15]&lt;/code&gt; avec la cible 9 donne &lt;code&gt;[0, 1]&lt;/code&gt;&amp;nbsp;; &lt;code&gt;[3, 3]&lt;/code&gt; avec 6 donne &lt;code&gt;[0, 1]&lt;/code&gt;&amp;nbsp;; &lt;code&gt;[1, 2, 3]&lt;/code&gt; avec 7 donne &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La solution simple.&lt;/strong&gt; «&amp;nbsp;Je peux tester toutes les paires avec deux boucles. C'est correct, mais en O(n²)&amp;nbsp;; sur un grand tableau, ce sera lent.&amp;nbsp;»&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L'amélioration.&lt;/strong&gt; «&amp;nbsp;En parcourant le tableau une fois, je peux mémoriser chaque valeur vue et son indice dans une &lt;code&gt;Map&lt;/code&gt;. Pour chaque nombre, je cherche si son complément, cible moins nombre, a déjà été vu.&amp;nbsp;»&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deuxSommes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nombres&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// valeur -&amp;gt; indice&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;nombres&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;complement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cible&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;nombres&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;complement&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;vus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;complement&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;vus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nombres&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;deuxSommes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// [0, 1]&lt;/span&gt;
&lt;span class="nf"&gt;deuxSommes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// [1, 2]&lt;/span&gt;
&lt;span class="nf"&gt;deuxSommes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Le détail à dire à voix haute.&lt;/strong&gt; On teste le complément avant d'ajouter le nombre courant. Sinon, &lt;code&gt;[3, 2, 4]&lt;/code&gt; avec la cible 6 renverrait &lt;code&gt;[0, 0]&lt;/code&gt;, en utilisant deux fois le 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La conclusion.&lt;/strong&gt; «&amp;nbsp;Temps O(n) en moyenne, un seul parcours&amp;nbsp;; mémoire O(n) pour la &lt;code&gt;Map&lt;/code&gt;.&amp;nbsp;» Nous avons exécuté ce code et la version à deux boucles avec Node.js 22 sur six cas, dont un tableau vide, une paire en double et des nombres négatifs&amp;nbsp;: les deux versions renvoient une paire valide, ou &lt;code&gt;null&lt;/code&gt;, sur les six cas.&lt;/p&gt;

&lt;p&gt;Si vous bloquez, dites-le et décrivez où&amp;nbsp;: «&amp;nbsp;Je vois la solution en O(n²), je cherche à éviter la seconde boucle.&amp;nbsp;» Un intervieweur peut alors vous orienter, ce qu'il ne peut pas faire face au silence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Le test à la maison : temps, consignes et droits sur votre code
&lt;/h2&gt;

&lt;p&gt;Ce test technique à la maison, un mini-projet selon OpenClassrooms, mesure ce que vous livrez seul&amp;nbsp;: un code propre, testé et documenté compte plus qu'une fonctionnalité de trop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le temps.&lt;/strong&gt; Aucune source lue ne fixe de durée maximale&amp;nbsp;: demandez le temps attendu et respectez-le. Dans le README, indiquez le temps passé, vos choix et ce que vous feriez avec plus de temps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vos droits de candidat.&lt;/strong&gt; Le Code du travail encadre ces tests. Selon l'article L1221-6, les informations demandées au candidat «&amp;nbsp;doivent présenter un lien direct et nécessaire avec l'emploi proposé ou avec l'évaluation des aptitudes professionnelles&amp;nbsp;». L'article L1221-8 prévoit que le candidat est «&amp;nbsp;expressément informé, préalablement à leur mise en oeuvre, des méthodes et techniques d'aide au recrutement&amp;nbsp;», que les résultats sont confidentiels et que ces méthodes doivent être pertinentes (&lt;a href="https://code.travail.gouv.fr/code-du-travail/l1221-8" rel="noopener noreferrer"&gt;Code du travail numérique, L1221-8&lt;/a&gt;, consulté le 1er novembre 2026&amp;nbsp;; &lt;a href="https://code.travail.gouv.fr/code-du-travail/l1221-6" rel="noopener noreferrer"&gt;L1221-6&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Qui possède le code&amp;nbsp;?&lt;/strong&gt; Le Code de la propriété intellectuelle protège les logiciels comme œuvres de l'esprit, et l'auteur a des droits sur son œuvre «&amp;nbsp;du seul fait de sa création&amp;nbsp;» (&lt;a href="https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006278875" rel="noopener noreferrer"&gt;article L112-2&lt;/a&gt; et &lt;a href="https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006278868" rel="noopener noreferrer"&gt;article L111-1&lt;/a&gt;, consultés le 1er novembre 2026). Encore faut-il que le code soit original&amp;nbsp;: la Cour de cassation exige un «&amp;nbsp;apport intellectuel propre&amp;nbsp;» et un «&amp;nbsp;effort personnalisé&amp;nbsp;» (&lt;a href="https://www.journaldunet.com/web-tech/1106311-la-cour-de-cassation-rappelle-que-l-originalite-est-une-condition-de-la-protection-du-logiciel-par-le-droit-d-auteur/" rel="noopener noreferrer"&gt;Journal du Net&lt;/a&gt;, sur l'arrêt du 17 octobre 2012), ce qui est discutable pour un exercice court.&lt;/p&gt;

&lt;p&gt;L'article L113-9 attribue à l'employeur les droits sur les logiciels créés par des employés dans l'exercice de leurs fonctions (&lt;a href="https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000006278890" rel="noopener noreferrer"&gt;article L113-9&lt;/a&gt;). Un candidat n'est pas encore un employé&amp;nbsp;: à notre lecture, le code d'un test reste à son auteur, sauf contrat contraire. Ce n'est pas un avis juridique. Lisez ce que l'on vous fait accepter, demandez par écrit si votre code sera réutilisé, et vérifiez que l'énoncé n'est pas confidentiel avant de le publier sur GitHub.&lt;/p&gt;




&lt;h2&gt;
  
  
  L'IA pendant les tests : ce que disent les règles publiées
&lt;/h2&gt;

&lt;p&gt;Certaines entreprises interdisent l'IA pendant un test technique, d'autres l'exigent&amp;nbsp;; HackerRank, éditeur de tests, note que les employeurs décident encore quand l'autoriser (&lt;a href="https://www.hackerrank.com/reports/developer-skills-report-2025" rel="noopener noreferrer"&gt;HackerRank, Developer Skills Report 2025&lt;/a&gt;, consulté le 1er novembre 2026).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entreprise&lt;/th&gt;
&lt;th&gt;Test à la maison&lt;/th&gt;
&lt;th&gt;Entretien en direct&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Anthropic&lt;/td&gt;
&lt;td&gt;Sans Claude, sauf indication contraire&lt;/td&gt;
&lt;td&gt;«&amp;nbsp;Pas d'assistance par IA, sauf indication contraire&amp;nbsp;» (notre traduction)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.anthropic.com/candidate-ai-guidance" rel="noopener noreferrer"&gt;Conseils aux candidats&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canva (postes back-end, front-end et apprentissage automatique)&lt;/td&gt;
&lt;td&gt;Non précisé dans le billet&lt;/td&gt;
&lt;td&gt;Usage des outils d'IA attendu dans l'entretien de code assisté par IA&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.canva.dev/blog/engineering/yes-you-can-use-ai-in-our-interviews/" rel="noopener noreferrer"&gt;Blog d'ingénierie de Canva&lt;/a&gt;, 11 juin 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Chez Canva, les problèmes posés «&amp;nbsp;ne peuvent pas être résolus avec un seul prompt&amp;nbsp;» (notre traduction), et l'évaluation porte sur le découpage d'un besoin ambigu et la correction du code généré.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs5lu88j79judtqu3fdbn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs5lu88j79judtqu3fdbn.webp" alt="Graphique en barres&amp;nbsp;: avis des développeurs sur les tests techniques selon HackerRank 2025, de 96&amp;nbsp;% qui veulent que résoudre compte plus que mémoriser à 56&amp;nbsp;% qui jugent l'algorithmique sans rapport avec leur poste" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans le même rapport, une enquête mondiale auprès de 13&amp;nbsp;732 répondants, 76&amp;nbsp;% des développeurs pensent que l'IA facilite la triche.&lt;/p&gt;

&lt;p&gt;La règle pratique&amp;nbsp;: si la consigne ne dit rien, demandez au recruteur avant de commencer. Si l'IA est autorisée, montrez que vous relisez et testez ce qu'elle produit. Voir aussi notre article sur l'&lt;a href="https://neurautech.com/comment-utiliser-lia-pour-coder-3x-plus-vite/" rel="noopener noreferrer"&gt;usage de l'IA pour coder&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les questions à poser au recruteur
&lt;/h2&gt;

&lt;p&gt;Préparez au moins une question précise pour chaque interlocuteur&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Au recruteur&amp;nbsp;:&lt;/strong&gt; quelles sont les prochaines étapes, et dans quels délais&amp;nbsp;? Quelle est la fourchette de salaire du poste&amp;nbsp;? Le télétravail est-il possible, et à quel rythme&amp;nbsp;?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Au manager technique&amp;nbsp;:&lt;/strong&gt; comment se passe l'intégration d'un nouveau développeur le premier mois&amp;nbsp;? Comment le code est-il relu avant d'être fusionné&amp;nbsp;? Comment déployez-vous en production&amp;nbsp;?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aux futurs collègues&amp;nbsp;:&lt;/strong&gt; quelle est la part de tests automatisés&amp;nbsp;? Existe-t-il des astreintes&amp;nbsp;? Pour un poste junior, qui sera mon référent&amp;nbsp;?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Après l'entretien
&lt;/h2&gt;

&lt;p&gt;Juste après l'entretien, notez les questions techniques posées et celles où vous avez hésité&amp;nbsp;: c'est votre programme de révision.&lt;/p&gt;

&lt;p&gt;Envoyez dans la journée un message court au recruteur&amp;nbsp;: un remerciement et un point précis de l'échange. Puis attendez&amp;nbsp;: le processus complet est souvent long, et le rapport de HackerRank note que beaucoup de candidats attendent des semaines ou ne reçoivent aucune réponse.&lt;/p&gt;

&lt;p&gt;Sans nouvelles à la date annoncée, relancez poliment le recruteur. En cas de refus, demandez un retour sur le test technique.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trois erreurs à éviter
&lt;/h2&gt;

&lt;p&gt;En plus du silence en live coding et du bluff, trois erreurs se corrigent avant l'entretien&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sur-concevoir.&lt;/strong&gt; Une architecture à microservices pour un test de deux heures inquiète plus qu'elle ne rassure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ne pas connaître son propre CV.&lt;/strong&gt; Chaque technologie citée peut devenir une question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Critiquer un ancien employeur.&lt;/strong&gt; L'entretien final évalue aussi votre manière de travailler en équipe.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Plusieurs points restent incertains&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;La fréquence du live coding et du test à la maison&lt;/strong&gt; en France&amp;nbsp;: aucune source chiffrée lue ne les distingue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les politiques sur l'IA des employeurs français.&lt;/strong&gt; Nous n'en avons trouvé aucune publiée&amp;nbsp;; les exemples cités sont américain et australien.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La pratique actuelle de Canva.&lt;/strong&gt; Le billet date de juin 2025&amp;nbsp;; le format a pu évoluer depuis.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Combien de temps dure un entretien technique développeur ?
&lt;/h3&gt;

&lt;p&gt;La discussion technique dure généralement de 30 à 45&amp;nbsp;minutes et peut aller jusqu'à 1&amp;nbsp;h&amp;nbsp;30, selon le &lt;a href="https://openclassrooms.com/fr/courses/6045521-preparez-vous-aux-tests-techniques-pour-devenir-developpeur/6171136-preparez-vous-aux-discussions-techniques" rel="noopener noreferrer"&gt;cours d'OpenClassrooms&lt;/a&gt;. Le processus complet prend plusieurs semaines&amp;nbsp;: 12 en moyenne pour un recrutement de cadre en 2025, selon l'Apec.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment se préparer à un entretien technique de développeur junior ?
&lt;/h3&gt;

&lt;p&gt;Révisez les fondamentaux (JavaScript, HTTP, SQL, Git), entraînez-vous à voix haute sur des exercices courts et relisez vos projets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Peut-on utiliser ChatGPT pendant un test technique ?
&lt;/h3&gt;

&lt;p&gt;Seulement si l'entreprise l'autorise. Les règles varient&amp;nbsp;: &lt;a href="https://www.anthropic.com/candidate-ai-guidance" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt; interdit l'IA sauf indication contraire, &lt;a href="https://www.canva.dev/blog/engineering/yes-you-can-use-ai-in-our-interviews/" rel="noopener noreferrer"&gt;Canva&lt;/a&gt; l'attend dans un de ses entretiens. Sans consigne écrite, demandez avant de commencer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Que faire si je ne connais pas la réponse à une question technique ?
&lt;/h3&gt;

&lt;p&gt;Dites-le, puis expliquez comment vous trouveriez la réponse. OpenClassrooms recommande d'admettre ce que l'on ne sait pas.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse fondée exclusivement sur des sources publiques&amp;nbsp;: baromètre 2026 de l'Apec, rapport 2025 de HackerRank, cours d'OpenClassrooms, politiques publiées par Anthropic et Canva, Code du travail, Code de la propriété intellectuelle, documentation de MDN, PostgreSQL, Git, Docker, de l'IETF et de l'OWASP. Les chiffres d'éditeurs sont signalés comme tels et les citations en anglais sont traduites par nos soins. Seul le code de l'exercice de live coding a été exécuté. Aucun test n'a été mené auprès de recruteurs, aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux entreprises mentionnées.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limites.&lt;/strong&gt; L'Apec porte sur l'ensemble des recrutements de cadres et HackerRank sur un échantillon mondial&amp;nbsp;: aucune des deux enquêtes ne mesure spécifiquement les développeurs en France. Notre lecture du Code de la propriété intellectuelle sur le code d'un test n'est pas un avis juridique&amp;nbsp;; nous n'avons trouvé aucune décision de justice sur ce cas précis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou une règle périmée&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 1er novembre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Vitaly Gariev (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>entretientechnique</category>
      <category>recrutement</category>
      <category>livecoding</category>
      <category>developpeurjunior</category>
    </item>
    <item>
      <title>Kubernetes vs Docker : Compose, Swarm ou cluster, que choisir en 2026 ?</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sun, 01 Nov 2026 15:35:05 +0000</pubDate>
      <link>https://dev.to/neurau_tech/kubernetes-vs-docker-compose-swarm-ou-cluster-que-choisir-en-2026--pn2</link>
      <guid>https://dev.to/neurau_tech/kubernetes-vs-docker-compose-swarm-ou-cluster-que-choisir-en-2026--pn2</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Kubernetes vs Docker est une comparaison trompeuse&amp;nbsp;: Docker construit des images et lance des conteneurs, Kubernetes décide sur quelles machines ils tournent, les remplace quand ils tombent et les met à jour par étapes. La vraie question est ailleurs&amp;nbsp;: Docker Compose sur un serveur suffit-il, ou faut-il un orchestrateur comme Docker Swarm, k3s ou un cluster Kubernetes géré&amp;nbsp;?&lt;/p&gt;

&lt;p&gt;Cet article tranche avec un exemple concret&amp;nbsp;: l'API de tâches de notre tutoriel &lt;a href="https://neurautech.com/docker-pour-debutants/" rel="noopener noreferrer"&gt;Docker pour débutants&lt;/a&gt;, écrite une fois en &lt;code&gt;compose.yaml&lt;/code&gt; et une fois en manifestes Kubernetes. Il s'appuie sur les documentations de Kubernetes, de Docker, de Kompose et de k3s, et sur les pages de tarifs d'AWS, de Google Cloud et d'Azure, consultées le 1er novembre 2026. Les fichiers YAML sont validés syntaxiquement, mais nous ne les avons pas exécutés sur un cluster.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker et Kubernetes ne font pas le même travail. Depuis la version 1.24, Kubernetes ne prend plus en charge Docker Engine nativement comme moteur, mais les images construites avec &lt;code&gt;docker build&lt;/code&gt; fonctionnent toujours.&lt;/li&gt;
&lt;li&gt;Au 1er novembre 2026, la version courante est Kubernetes 1.37, maintenue jusqu'au 28 octobre 2027. La 1.38 est prévue le 16 décembre 2026.&lt;/li&gt;
&lt;li&gt;Docker Compose reste l'outil officiel pour un seul serveur. Docker Swarm et Kubernetes servent à répartir les conteneurs sur plusieurs machines.&lt;/li&gt;
&lt;li&gt;Kompose 1.38.0 convertit un &lt;code&gt;compose.yaml&lt;/code&gt; en manifestes, mais ignore &lt;code&gt;depends_on&lt;/code&gt; et place les variables d'environnement en clair&amp;nbsp;: le résultat est un point de départ.&lt;/li&gt;
&lt;li&gt;Un plan de contrôle géré coûte 0,10&amp;nbsp;$ par heure chez AWS, soit environ 73&amp;nbsp;$ par mois selon notre calcul. Google Cloud applique le même tarif, mais un crédit mensuel couvre un cluster zonal ou Autopilot par compte de facturation. Azure propose un niveau gratuit, sans SLA.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Pourquoi « Kubernetes vs Docker » est une fausse opposition
&lt;/h2&gt;

&lt;p&gt;Les deux outils travaillent à des étages différents. Docker fabrique une image à partir d'un Dockerfile, la publie dans un registre et lance des conteneurs sur une machine. Kubernetes, selon sa documentation, est une plateforme qui gère des charges de travail conteneurisées sur un cluster&amp;nbsp;: il redémarre les conteneurs défaillants, tue ceux qui ne répondent pas au test de santé et ne leur envoie pas de trafic tant qu'ils ne sont pas prêts (&lt;a href="https://kubernetes.io/docs/concepts/overview/" rel="noopener noreferrer"&gt;présentation de Kubernetes&lt;/a&gt;, consultée le 1er novembre 2026).&lt;/p&gt;

&lt;p&gt;La même page précise ce que Kubernetes ne fait pas&amp;nbsp;: il ne compile pas votre code et ne construit pas vos images. Dans une chaîne réelle, Docker construit l'image, un registre la stocke et Kubernetes la déploie. C'est ce que fait notre tutoriel &lt;a href="https://neurautech.com/github-actions-tutoriel/" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;, qui publie l'image de l'API sur &lt;code&gt;ghcr.io&lt;/code&gt; à chaque modification.&lt;/p&gt;

&lt;p&gt;L'idée que «&amp;nbsp;Kubernetes a abandonné Docker&amp;nbsp;» vient d'un changement réel mais étroit. Pendant des années, Kubernetes s'est appuyé sur Docker Engine par l'intermédiaire d'un adaptateur intégré appelé dockershim. Ce composant a été retiré dans la version 1.24, car Docker Engine n'implémente pas l'interface CRI que Kubernetes utilise pour parler aux moteurs de conteneurs (&lt;a href="https://kubernetes.io/blog/2022/02/17/dockershim-faq/" rel="noopener noreferrer"&gt;FAQ sur le retrait de dockershim&lt;/a&gt;, blog de Kubernetes, 17 février 2022).&lt;/p&gt;

&lt;p&gt;La même FAQ répond aux deux inquiétudes habituelles&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vos images.&lt;/strong&gt; Celles produites par &lt;code&gt;docker build&lt;/code&gt; fonctionnent avec toutes les implémentations de CRI, comme containerd ou CRI-O.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Votre poste.&lt;/strong&gt; Si vous utilisez Docker pour développer et tester, rien ne change. Pour garder Docker Engine sur les nœuds d'un cluster, Mirantis et Docker se sont engagés à maintenir un adaptateur séparé, cri-dockerd, selon la FAQ et le &lt;a href="https://github.com/Mirantis/cri-dockerd" rel="noopener noreferrer"&gt;dépôt de cri-dockerd&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Compose, Swarm, k3s, Kubernetes géré : le tableau comparatif
&lt;/h2&gt;

&lt;p&gt;Le choix réel se fait entre quatre façons de faire tourner des conteneurs Docker en production. Le tableau résume ce que disent leurs documentations officielles, lues le 1er novembre 2026.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Critère&lt;/th&gt;
&lt;th&gt;Docker Compose&lt;/th&gt;
&lt;th&gt;Docker Swarm&lt;/th&gt;
&lt;th&gt;k3s&lt;/th&gt;
&lt;th&gt;Kubernetes géré (EKS, GKE, AKS)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nombre de machines&lt;/td&gt;
&lt;td&gt;Une seule&lt;/td&gt;
&lt;td&gt;Plusieurs&lt;/td&gt;
&lt;td&gt;Une ou plusieurs&lt;/td&gt;
&lt;td&gt;Plusieurs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fichier de description&lt;/td&gt;
&lt;td&gt;&lt;code&gt;compose.yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;compose.yaml&lt;/code&gt; via &lt;code&gt;docker stack deploy&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Manifestes Kubernetes&lt;/td&gt;
&lt;td&gt;Manifestes Kubernetes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remplacement d'un conteneur tombé&lt;/td&gt;
&lt;td&gt;Politique &lt;code&gt;restart&lt;/code&gt; sur la machine&lt;/td&gt;
&lt;td&gt;Oui, y compris sur un autre nœud&lt;/td&gt;
&lt;td&gt;Oui, comme Kubernetes&lt;/td&gt;
&lt;td&gt;Oui&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mise à jour progressive et retour arrière&lt;/td&gt;
&lt;td&gt;Non, recréation du service&lt;/td&gt;
&lt;td&gt;Oui (&lt;code&gt;update_config&lt;/code&gt;, &lt;code&gt;rollback_config&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Oui, via le Deployment&lt;/td&gt;
&lt;td&gt;Oui, via le Deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mise à l'échelle automatique&lt;/td&gt;
&lt;td&gt;Non documentée&lt;/td&gt;
&lt;td&gt;Non documentée dans les pages lues&lt;/td&gt;
&lt;td&gt;Oui, Metrics Server installé d'office&lt;/td&gt;
&lt;td&gt;Oui, Metrics Server à ajouter sur EKS, à vérifier ailleurs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan de contrôle à gérer&lt;/td&gt;
&lt;td&gt;Aucun&lt;/td&gt;
&lt;td&gt;Nœuds managers Swarm&lt;/td&gt;
&lt;td&gt;Un binaire, à mettre à jour vous-même&lt;/td&gt;
&lt;td&gt;Fourni par le cloud, facturé ou non&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versions au 1er novembre 2026&lt;/td&gt;
&lt;td&gt;Compose 5.6.1 (21 octobre)&lt;/td&gt;
&lt;td&gt;Intégré à Docker Engine 29.9.2&lt;/td&gt;
&lt;td&gt;Canal stable 1.36.5+k3s1&lt;/td&gt;
&lt;td&gt;Selon le fournisseur&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sources&amp;nbsp;: &lt;a href="https://docs.docker.com/compose/how-tos/production/" rel="noopener noreferrer"&gt;Compose en production&lt;/a&gt;, &lt;a href="https://docs.docker.com/engine/swarm/" rel="noopener noreferrer"&gt;mode Swarm&lt;/a&gt;, &lt;a href="https://docs.docker.com/reference/compose-file/deploy/" rel="noopener noreferrer"&gt;spécification deploy de Compose&lt;/a&gt;, &lt;a href="https://docs.k3s.io/" rel="noopener noreferrer"&gt;documentation de k3s&lt;/a&gt;, &lt;a href="https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/" rel="noopener noreferrer"&gt;mise à l'échelle horizontale&lt;/a&gt;, versions relevées sur les pages de publication de &lt;a href="https://github.com/docker/compose/releases" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;, de &lt;a href="https://github.com/moby/moby/releases" rel="noopener noreferrer"&gt;Docker Engine&lt;/a&gt; et sur le &lt;a href="https://update.k3s.io/v1-release/channels" rel="noopener noreferrer"&gt;canal de mise à jour de k3s&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quelques nuances que les tableaux concurrents oublient&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compose est fait pour une machine.&lt;/strong&gt; La documentation de Docker indique que Compose prend en charge la production sur un seul hôte, et que la façon la plus simple de déployer reste un seul serveur. Lancé sur un nœud Swarm, Compose prévient qu'il place tous les conteneurs sur la machine courante (&lt;a href="https://docs.docker.com/engine/swarm/stack-deploy/" rel="noopener noreferrer"&gt;déploiement d'une pile sur Swarm&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swarm demande un registre.&lt;/strong&gt; Le même tutoriel montre que &lt;code&gt;docker stack deploy&lt;/code&gt; ignore la clé &lt;code&gt;build&lt;/code&gt;&amp;nbsp;: l'image doit être poussée dans un registre accessible à tous les nœuds. La page du mode Swarm conseille d'ailleurs Compose si vous ne comptez pas déployer avec Swarm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;k3s est un vrai Kubernetes.&lt;/strong&gt; Sa documentation le décrit comme une distribution entièrement conforme, livrée en un binaire de moins de 100&amp;nbsp;Mo, avec containerd, CoreDNS, Traefik et un fournisseur de volumes locaux. Elle demande au minimum 2 cœurs et 2&amp;nbsp;Go de mémoire pour un serveur (&lt;a href="https://docs.k3s.io/installation/requirements" rel="noopener noreferrer"&gt;prérequis de k3s&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics Server n'est pas partout.&lt;/strong&gt; Le HorizontalPodAutoscaler lit le processeur et la mémoire des Pods via l'API de métriques, fournie en général par le module Metrics Server, qui doit être lancé séparément (&lt;a href="https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/" rel="noopener noreferrer"&gt;mise à l'échelle horizontale&lt;/a&gt;). k3s le déploie d'office parmi ses composants intégrés (&lt;a href="https://docs.k3s.io/installation/packaged-components" rel="noopener noreferrer"&gt;composants de k3s&lt;/a&gt;). Amazon EKS ne le déploie pas par défaut (&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/metrics-server.html" rel="noopener noreferrer"&gt;documentation d'EKS&lt;/a&gt;). La documentation d'AKS indique seulement que les offres cloud peuvent le préinstaller (&lt;a href="https://learn.microsoft.com/en-us/azure/aks/horizontal-pod-autoscaler" rel="noopener noreferrer"&gt;HPA sur AKS&lt;/a&gt;). Vérifiez avec &lt;code&gt;kubectl get deployment metrics-server -n kube-system&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Côté usage, l'écart reste net. Dans l'enquête Stack Overflow 2026, 59,1&amp;nbsp;% des 10&amp;nbsp;889 répondants à la rubrique «&amp;nbsp;Cloud development&amp;nbsp;» ont travaillé avec Docker ou Docker Compose dans l'année, contre 26,0&amp;nbsp;% pour Kubernetes et 13,0&amp;nbsp;% pour Podman, soit 6&amp;nbsp;440, 2&amp;nbsp;836 et 1&amp;nbsp;415 répondants (&lt;a href="https://survey.stackoverflow.co/2026/technology/data" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2026&lt;/a&gt;, consulté le 1er novembre 2026).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3vyxjygx716o5f8y8idg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3vyxjygx716o5f8y8idg.webp" alt="Kubernetes vs Docker en barres horizontales&amp;nbsp;: dans l'enquête Stack Overflow 2026, 59,1&amp;nbsp;% des répondants ont utilisé Docker ou Docker Compose, 26,0&amp;nbsp;% Kubernetes et 13,0&amp;nbsp;% Podman" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Kubernetes vs Docker Compose : quand passer le cap
&lt;/h2&gt;

&lt;p&gt;Docker Compose sur un serveur suffit tant qu'une panne de cette machine est acceptable et que la charge tient sur elle. Un orchestrateur devient utile dès que l'une de ces deux conditions tombe. Nous ne donnons pas de seuil en utilisateurs ou en requêtes par seconde&amp;nbsp;: aucune des sources officielles consultées n'en fixe, et les chiffres qui circulent ne sont pas sourcés.&lt;/p&gt;

&lt;p&gt;Restez sur Compose si&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Une seule machine porte l'application.&lt;/strong&gt; Une API, sa base et un &lt;a href="https://neurautech.com/nginx-reverse-proxy-docker-compose/" rel="noopener noreferrer"&gt;Nginx reverse proxy&lt;/a&gt; devant tiennent dans un fichier, avec &lt;code&gt;restart: unless-stopped&lt;/code&gt; pour relancer un conteneur arrêté.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Une courte coupure au déploiement est tolérable.&lt;/strong&gt; &lt;code&gt;docker compose up -d&lt;/code&gt; recrée le service&amp;nbsp;: c'est simple, mais ce n'est pas une mise à jour progressive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personne n'est disponible pour exploiter un cluster.&lt;/strong&gt; Compose se maintient avec les &lt;a href="https://neurautech.com/commandes-linux-de-base/" rel="noopener noreferrer"&gt;commandes Linux de base&lt;/a&gt;, Docker et un peu de supervision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les signaux généraux en faveur d'un orchestrateur (plusieurs nœuds, mises à jour sans coupure, charge variable, équipes multiples) sont détaillés dans la section &lt;a href="https://neurautech.com/kubernetes-pour-debutants-comprendre-lorchestration/#quand-kubernetes-vaut-le-coup-et-quand-cest-trop" rel="noopener noreferrer"&gt;quand Kubernetes vaut le coup&lt;/a&gt; de notre guide pour débutants. Le choix se joue ensuite entre trois options&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker Swarm si vous voulez garder &lt;code&gt;compose.yaml&lt;/code&gt; et la ligne de commande Docker.&lt;/strong&gt; Swarm répartit les services sur plusieurs machines et sait revenir en arrière après une mise à jour, mais il exige un registre et nous n'avons trouvé aucune mise à l'échelle automatique dans sa documentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;k3s si vous avez vos propres serveurs ou quelques machines virtuelles.&lt;/strong&gt; Vous obtenez l'API Kubernetes complète, avec Traefik, des volumes locaux et Metrics Server déjà installés, mais le plan de contrôle et ses mises à jour sont à votre charge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un Kubernetes géré si la disponibilité prime et que le budget suit.&lt;/strong&gt; Le fournisseur exploite le plan de contrôle. Par défaut, un Deployment garde au moins 75&amp;nbsp;% des Pods voulus disponibles et n'en crée pas plus de 125&amp;nbsp;% pendant une mise à jour (&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" rel="noopener noreferrer"&gt;documentation des Deployments&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un piège revient souvent&amp;nbsp;: Kubernetes ne rend pas une application capable de monter en charge. Notre API stocke ses données dans un fichier SQLite. Deux copies qui écrivent dans le même fichier posent problème, quel que soit l'orchestrateur&amp;nbsp;: il faut d'abord passer à PostgreSQL, comme le recommande notre tutoriel pour &lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;créer une API REST avec Node.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; dans le duel Kubernetes vs Docker Compose, le premier critère est le nombre de machines nécessaires&amp;nbsp;; le partage de l'infrastructure entre plusieurs équipes vient ensuite. Dans tous les cas, Docker reste l'outil pour construire les images et développer en local.&lt;/p&gt;




&lt;h2&gt;
  
  
  La même API en compose.yaml puis en manifestes Kubernetes
&lt;/h2&gt;

&lt;p&gt;L'API de nos tutoriels écoute le port 3000, refuse de démarrer sans un &lt;code&gt;JWT_SECRET&lt;/code&gt; d'au moins 32 octets, écrit sa base SQLite dans &lt;code&gt;/app/data&lt;/code&gt; et répond &lt;code&gt;{"statut":"ok"}&lt;/code&gt; sur &lt;code&gt;/health&lt;/code&gt;. L'image est celle que publie le workflow GitHub Actions, &lt;code&gt;ghcr.io/&amp;lt;compte&amp;gt;/api-taches&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le compose.yaml
&lt;/h3&gt;

&lt;p&gt;Ce fichier reprend les choix de nos guides Docker et Nginx&amp;nbsp;: port publié sur l'adresse locale seulement, secret lu dans le &lt;code&gt;.env&lt;/code&gt;, test de santé sur &lt;code&gt;/health&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/votre-compte/api-taches:main&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${JWT_SECRET:?definir JWT_SECRET dans .env}&lt;/span&gt;
      &lt;span class="na"&gt;DB_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app/data/data.db&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api-data:/app/data&lt;/span&gt;
    &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-e"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fetch('http://127.0.0.1:3000/health').then(r&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;process.exit(r.ok&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1)).catch(()&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;process.exit(1))"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vingt et une lignes, une commande (&lt;code&gt;docker compose up -d --build&lt;/code&gt;), et aucun composant à installer en dehors de Docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les manifestes Kubernetes
&lt;/h3&gt;

&lt;p&gt;Côté Kubernetes, le secret vit dans un objet séparé. La documentation prévient que les Secrets sont stockés sans chiffrement par défaut dans etcd, la base du cluster, et que toute personne ayant accès à l'API peut les lire (&lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/" rel="noopener noreferrer"&gt;documentation des Secrets&lt;/a&gt;). Générez d'abord une valeur de 32 octets, comme dans notre guide Nginx, puis créez le Secret en ligne de commande plutôt que de l'écrire dans un fichier versionné&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; node:24-alpine node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"console.log(require('node:crypto').randomBytes(32).toString('hex'))"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
kubectl create secret generic api-taches &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$JWT_SECRET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le fichier équivalent, à ne jamais placer dans Git avec une vraie valeur. Le texte de remplacement fait moins de 32 octets&amp;nbsp;: tant qu'il n'est pas changé, l'API refuse de démarrer, ce qui est voulu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-taches&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Opaque&lt;/span&gt;
&lt;span class="na"&gt;stringData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A-REMPLACER"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puis le fichier &lt;code&gt;api-taches.yaml&lt;/code&gt;, avec le volume, le Deployment et le Service&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-data&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1Gi&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-taches&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-taches&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Recreate&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-taches&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-taches&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;shareProcessNamespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;fsGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;
        &lt;span class="na"&gt;fsGroupChangePolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OnRootMismatch&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/votre-compte/api-taches:sha-1a2b3c4&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JWT_SECRET&lt;/span&gt;
              &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-taches&lt;/span&gt;
                  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JWT_SECRET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_PATH&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app/data/data.db&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app/data&lt;/span&gt;
          &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
              &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
            &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
            &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
          &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-data&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-taches&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-taches&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Appliquez-le avec &lt;code&gt;kubectl apply -f api-taches.yaml&lt;/code&gt;, puis suivez le déploiement avec &lt;code&gt;kubectl rollout status deployment/api-taches&lt;/code&gt;. La taille de 1 Gi est un exemple&amp;nbsp;: ajustez-la. Le tag &lt;code&gt;sha-1a2b3c4&lt;/code&gt; remplace le commit abrégé que produit notre workflow GitHub Actions&amp;nbsp;: avec un tag autre que &lt;code&gt;latest&lt;/code&gt;, Kubernetes règle par défaut &lt;code&gt;imagePullPolicy&lt;/code&gt; sur &lt;code&gt;IfNotPresent&lt;/code&gt;, et un nœud qui a déjà une image &lt;code&gt;main&lt;/code&gt; en cache ne téléchargerait pas la nouvelle (&lt;a href="https://kubernetes.io/docs/concepts/containers/images/" rel="noopener noreferrer"&gt;documentation des images&lt;/a&gt;). Un tag par commit rend chaque déploiement explicite. Si le paquet GHCR est privé, le Pod aura besoin d'un &lt;code&gt;imagePullSecrets&lt;/code&gt;. Pour les bases de ces objets, notre guide &lt;a href="https://neurautech.com/kubernetes-pour-debutants-comprendre-lorchestration/" rel="noopener noreferrer"&gt;Kubernetes pour débutants&lt;/a&gt; les présente un par un.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ce qui change entre les deux
&lt;/h3&gt;

&lt;p&gt;Quatre choix de ces manifestes découlent de SQLite et de la documentation&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;replicas: 1&lt;/code&gt; et &lt;code&gt;strategy: Recreate&lt;/code&gt;.&lt;/strong&gt; Un volume en &lt;code&gt;ReadWriteOnce&lt;/code&gt; ne peut être monté en écriture que par un seul nœud (&lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/" rel="noopener noreferrer"&gt;volumes persistants&lt;/a&gt;). La stratégie &lt;code&gt;Recreate&lt;/code&gt; arrête l'ancien Pod avant de lancer le nouveau&amp;nbsp;: il y a donc une courte coupure, comme avec Compose. Pour une vraie mise à jour progressive, passez à PostgreSQL et à la stratégie par défaut.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fsGroup: 1000&lt;/code&gt;.&lt;/strong&gt; L'image de notre guide Docker tourne sous l'utilisateur &lt;code&gt;node&lt;/code&gt;, d'identifiant 1000. Pour les types de volumes qui le permettent, &lt;code&gt;fsGroup&lt;/code&gt; attribue ce groupe aux fichiers du volume pour que SQLite puisse écrire&amp;nbsp;; &lt;code&gt;fsGroupChangePolicy: OnRootMismatch&lt;/code&gt; évite de refaire ce changement à chaque démarrage si la racine du volume est déjà correcte (&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/security-context/" rel="noopener noreferrer"&gt;contexte de sécurité&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;shareProcessNamespace: true&lt;/code&gt;, une alternative à &lt;code&gt;init: true&lt;/code&gt;.&lt;/strong&gt; Nous n'avons trouvé dans les pages consultées aucun champ de Pod qui lance un processus d'init comme le fait Compose&amp;nbsp;; avec ce réglage, les conteneurs d'un même Pod voient aussi les processus et les fichiers des autres, ce qui compte si vous ajoutez un second conteneur. Or Node.js n'est pas conçu pour tourner avec le PID 1 et ne répond alors pas à des signaux comme &lt;code&gt;SIGINT&lt;/code&gt; (&lt;a href="https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md" rel="noopener noreferrer"&gt;bonnes pratiques de l'image Node.js&lt;/a&gt;). Sans correctif, chaque arrêt du Pod attendrait la fin du délai de grâce, 30&amp;nbsp;secondes par défaut (&lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/" rel="noopener noreferrer"&gt;cycle de vie des Pods&lt;/a&gt;). Avec un espace de processus partagé, le processus du conteneur n'a plus le PID 1 (&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/" rel="noopener noreferrer"&gt;partage de l'espace de processus&lt;/a&gt;). L'autre solution, que recommandent aussi les bonnes pratiques de l'image Node.js, consiste à ajouter Tini au Dockerfile&amp;nbsp;: &lt;code&gt;RUN apk add --no-cache tini&lt;/code&gt; et &lt;code&gt;ENTRYPOINT ["/sbin/tini", "--"]&lt;/code&gt;, selon le &lt;a href="https://github.com/krallin/tini" rel="noopener noreferrer"&gt;README de Tini&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deux sondes au lieu d'un test de santé.&lt;/strong&gt; La sonde de disponibilité (readiness) décide si le Pod reçoit du trafic, la sonde de vie (liveness) le redémarre s'il échoue. Une sonde de vie trop sévère peut provoquer des redémarrages en cascade sous forte charge, prévient la &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/probes/" rel="noopener noreferrer"&gt;documentation des sondes&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dans compose.yaml&lt;/th&gt;
&lt;th&gt;Dans Kubernetes&lt;/th&gt;
&lt;th&gt;Remarque&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;services.api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deployment + Service&lt;/td&gt;
&lt;td&gt;Le Service donne une adresse stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ports: 127.0.0.1:3000:3000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Service &lt;code&gt;ClusterIP&lt;/code&gt;, port 80 vers 3000&lt;/td&gt;
&lt;td&gt;Exposition publique&amp;nbsp;: Ingress ou Gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;environment&lt;/code&gt; + &lt;code&gt;.env&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Secret + &lt;code&gt;secretKeyRef&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Secret non chiffré par défaut dans etcd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;volumes: api-data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PersistentVolumeClaim&lt;/td&gt;
&lt;td&gt;Suppose une classe de stockage par défaut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;healthcheck&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;readinessProbe&lt;/code&gt; et &lt;code&gt;livenessProbe&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Deux rôles distincts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;restart: unless-stopped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;restartPolicy: Always&lt;/code&gt;, seule valeur permise dans un Deployment&lt;/td&gt;
&lt;td&gt;Le kubelet relance le conteneur&amp;nbsp;; le Deployment recrée le Pod, au besoin ailleurs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;build: .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Aucun équivalent&lt;/td&gt;
&lt;td&gt;L'image doit venir d'un registre&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Comptées de la même façon, sans les lignes vides, le fichier Compose fait 21 lignes et les manifestes Kubernetes 82 avec le Secret, soit plus du triple. Dans ce match Kubernetes vs Docker Compose, ce surcoût en YAML n'est rentable que si vous avez besoin de ce qu'il apporte.&lt;/p&gt;




&lt;h2&gt;
  
  
  Migrer avec Kompose : ce qu'il convertit, ce qu'il laisse
&lt;/h2&gt;

&lt;p&gt;Kompose, un projet officiel du groupe Kubernetes, sert à passer d'un fichier Compose à des manifestes. Son README le dit lui-même&amp;nbsp;: la transformation «&amp;nbsp;peut ne pas être exacte&amp;nbsp;», mais elle aide beaucoup pour un premier déploiement (notre traduction, &lt;a href="https://github.com/kubernetes/kompose" rel="noopener noreferrer"&gt;dépôt de Kompose&lt;/a&gt;, consulté le 1er novembre 2026).&lt;/p&gt;

&lt;p&gt;La dernière version publiée est la 1.38.0, du 15 janvier 2026, selon la &lt;a href="https://github.com/kubernetes/kompose/releases" rel="noopener noreferrer"&gt;page des versions&lt;/a&gt;. Le README annonce un rythme de sortie toutes les trois semaines&amp;nbsp;: aucune version n'est pourtant parue depuis plus de neuf mois. La commande de base est la suivante&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kompose convert &lt;span class="nt"&gt;-f&lt;/span&gt; compose.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le &lt;a href="https://kompose.io/conversion/" rel="noopener noreferrer"&gt;tableau de conversion de Kompose&lt;/a&gt; et son &lt;a href="https://kompose.io/user-guide/" rel="noopener noreferrer"&gt;guide d'utilisation&lt;/a&gt; détaillent ce qui passe et ce qui ne passe pas&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Clé Compose&lt;/th&gt;
&lt;th&gt;Ce que produit Kompose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;image&lt;/code&gt;, &lt;code&gt;ports&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Deployment et Service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;environment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Variables en clair dans le Deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;volumes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PersistentVolumeClaim de 100 Mi par défaut, avec stratégie &lt;code&gt;Recreate&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;healthcheck&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sonde de vie (liveness) qui exécute la commande &lt;code&gt;test&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;depends_on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non applicable, ignoré&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Construction seulement avec &lt;code&gt;--build&lt;/code&gt;, envoi avec &lt;code&gt;--push-image&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;build: args&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pas encore implémenté&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Trois limites comptent pour notre API. D'abord, le secret&amp;nbsp;: &lt;code&gt;environment&lt;/code&gt; devient une variable du Deployment, donc &lt;code&gt;JWT_SECRET&lt;/code&gt; se retrouve en clair dans un fichier YAML. Remplacez-le par un Secret avant tout commit. Ensuite, l'ordre de démarrage&amp;nbsp;: &lt;code&gt;depends_on&lt;/code&gt; n'a pas d'équivalent, et chaque service doit savoir attendre ses dépendances.&lt;/p&gt;

&lt;p&gt;Enfin, les sondes&amp;nbsp;: le &lt;code&gt;healthcheck&lt;/code&gt; de Compose devient une sonde de vie qui lance la même commande, mais aucune sonde de disponibilité n'est créée sans étiquettes propres à Kompose. Ajoutées au service &lt;code&gt;api&lt;/code&gt;, celles-ci produisent une sonde de disponibilité HTTP sur &lt;code&gt;/health&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;kompose.service.healthcheck.readiness.http_get_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
      &lt;span class="na"&gt;kompose.service.healthcheck.readiness.http_get_port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La taille par défaut de 100 Mi et le passage du &lt;code&gt;healthcheck&lt;/code&gt; en sonde de vie se lisent dans le &lt;a href="https://github.com/kubernetes/kompose/tree/v1.38.0/pkg" rel="noopener noreferrer"&gt;code source de la version 1.38.0&lt;/a&gt;, l'étiquette &lt;code&gt;kompose.volume.size&lt;/code&gt; permettant de changer la taille.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; utilisez Kompose pour obtenir un brouillon, puis relisez chaque fichier. Les points ci-dessus sont ceux que nos manifestes écrits à la main traitent différemment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Coûts et charge d'exploitation
&lt;/h2&gt;

&lt;p&gt;Le logiciel est gratuit, son exploitation ne l'est pas&amp;nbsp;: il faut payer le plan de contrôle géré et suivre les versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le rythme des versions
&lt;/h3&gt;

&lt;p&gt;Le projet maintient les trois dernières versions mineures, soit 1.37, 1.36 et 1.35, avec environ un an de correctifs pour chacune, suivi de deux mois en mode maintenance (&lt;a href="https://kubernetes.io/releases/" rel="noopener noreferrer"&gt;page des versions&lt;/a&gt; et &lt;a href="https://kubernetes.io/releases/patch-releases/" rel="noopener noreferrer"&gt;politique des correctifs&lt;/a&gt;, consultées le 1er novembre 2026). La 1.37, nom de code Garhwal, est sortie le 26 août 2026 avec 67 améliorations (&lt;a href="https://kubernetes.io/blog/2026/08/26/kubernetes-v1-37-release/" rel="noopener noreferrer"&gt;annonce de la 1.37&lt;/a&gt;).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Sortie&lt;/th&gt;
&lt;th&gt;Mode maintenance&lt;/th&gt;
&lt;th&gt;Fin de vie&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1.37&lt;/td&gt;
&lt;td&gt;26 août 2026&lt;/td&gt;
&lt;td&gt;28 août 2027&lt;/td&gt;
&lt;td&gt;28 octobre 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.36&lt;/td&gt;
&lt;td&gt;22 avril 2026&lt;/td&gt;
&lt;td&gt;28 avril 2027&lt;/td&gt;
&lt;td&gt;28 juin 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.35&lt;/td&gt;
&lt;td&gt;17 décembre 2025&lt;/td&gt;
&lt;td&gt;28 décembre 2026&lt;/td&gt;
&lt;td&gt;28 février 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La 1.38 est prévue le 16 décembre 2026, selon le &lt;a href="https://www.kubernetes.dev/resources/release/" rel="noopener noreferrer"&gt;calendrier de la version 1.38&lt;/a&gt;. Un cluster installé aujourd'hui devra donc être mis à jour au moins une fois par an, en passant d'une version mineure à la suivante. Dernier correctif relevé&amp;nbsp;: 1.37.2, publié le 28 octobre 2026 sur &lt;a href="https://github.com/kubernetes/kubernetes/releases" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv2m25imd20iwqaaf8rgh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv2m25imd20iwqaaf8rgh.webp" alt="Diagramme de Gantt&amp;nbsp;: versions de Kubernetes 1.35, 1.36 et 1.37 avec leurs périodes de correctifs et de maintenance jusqu'à leur fin de vie, et sortie de la 1.38 prévue le 16 décembre 2026" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Le prix d'un plan de contrôle géré
&lt;/h3&gt;

&lt;p&gt;Les trois grands clouds facturent différemment le plan de contrôle, hors machines, stockage et réseau. Tarifs relevés le 1er novembre 2026&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offre&lt;/th&gt;
&lt;th&gt;Plan de contrôle&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amazon EKS, support standard&lt;/td&gt;
&lt;td&gt;0,10&amp;nbsp;$ par cluster et par heure&lt;/td&gt;
&lt;td&gt;14 premiers mois d'une version dans EKS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon EKS, support étendu&lt;/td&gt;
&lt;td&gt;0,60&amp;nbsp;$ par cluster et par heure&lt;/td&gt;
&lt;td&gt;12&amp;nbsp;mois supplémentaires&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Kubernetes Engine&lt;/td&gt;
&lt;td&gt;0,10&amp;nbsp;$ par cluster et par heure&lt;/td&gt;
&lt;td&gt;Crédit de 74,40&amp;nbsp;$ par mois et par compte de facturation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Kubernetes Service, niveau Free&lt;/td&gt;
&lt;td&gt;Gratuit&lt;/td&gt;
&lt;td&gt;Sans SLA, déconseillé au-delà de 10 nœuds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Kubernetes Service, niveau Standard&lt;/td&gt;
&lt;td&gt;Prix non relevé, illisible sur la page&lt;/td&gt;
&lt;td&gt;Avec SLA sur le serveur d'API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Kubernetes Service, niveau Premium&lt;/td&gt;
&lt;td&gt;Prix non relevé, illisible sur la page&lt;/td&gt;
&lt;td&gt;SLA et support à long terme&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sources&amp;nbsp;: &lt;a href="https://aws.amazon.com/eks/pricing/" rel="noopener noreferrer"&gt;tarifs d'Amazon EKS&lt;/a&gt;, &lt;a href="https://cloud.google.com/kubernetes-engine/pricing" rel="noopener noreferrer"&gt;tarifs de GKE&lt;/a&gt; et &lt;a href="https://azure.microsoft.com/en-us/pricing/details/kubernetes-service/" rel="noopener noreferrer"&gt;tarifs d'AKS&lt;/a&gt;. Google précise que son crédit gratuit équivaut à un cluster zonal ou Autopilot par mois.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notre calcul.&lt;/strong&gt; 0,10&amp;nbsp;$ sur 730&amp;nbsp;heures donne environ 73&amp;nbsp;$ par mois pour le seul plan de contrôle chez AWS. Une version laissée en support étendu chez EKS passe à 0,60&amp;nbsp;$, soit environ 438&amp;nbsp;$ par mois. Ne pas mettre à jour son cluster a donc un prix direct.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ce surcoût place Kubernetes géré au-dessus des offres plus simples présentées dans notre guide de l'&lt;a href="https://neurautech.com/hebergement-cloud-pour-developpeurs/" rel="noopener noreferrer"&gt;hébergement cloud pour développeurs&lt;/a&gt;. Pour comparer les trois fournisseurs au-delà de Kubernetes, notre duel &lt;a href="https://neurautech.com/aws-vs-azure-vs-google-cloud-prix-services-usage/" rel="noopener noreferrer"&gt;AWS vs Azure vs Google Cloud&lt;/a&gt; détaille les autres services.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Cet article est une synthèse documentaire. Les points suivants restent ouverts&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L'exécution des fichiers.&lt;/strong&gt; Le &lt;code&gt;compose.yaml&lt;/code&gt; et les manifestes ont été validés comme YAML, pas exécutés sur Docker ni sur un cluster. La conversion par Kompose n'a pas été lancée.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le prix des niveaux Standard et Premium d'AKS.&lt;/strong&gt; La page de tarifs d'Azure n'affichait pas de montant lisible au moment de notre lecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'avenir de Docker Swarm.&lt;/strong&gt; La documentation de Docker le présente comme intégré à Docker Engine, mais nous n'avons trouvé aucune feuille de route officielle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics Server sur GKE et AKS.&lt;/strong&gt; Nous n'avons trouvé aucune page officielle qui dise clairement s'il est installé d'office&amp;nbsp;; la documentation d'AKS dit seulement que les offres cloud «&amp;nbsp;peuvent&amp;nbsp;» le préinstaller (notre traduction).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'arrêt de l'API avec &lt;code&gt;shareProcessNamespace&lt;/code&gt;.&lt;/strong&gt; La documentation dit que le processus perd le PID 1&amp;nbsp;; que Node.js s'arrête alors proprement sur &lt;code&gt;SIGTERM&lt;/code&gt; est notre déduction, non testée. Tini dans l'image reste l'option la plus documentée.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Kubernetes remplace-t-il Docker ?
&lt;/h3&gt;

&lt;p&gt;Non, et c'est le cœur du débat Kubernetes vs Docker. Kubernetes a retiré dockershim dans la version 1.24 et ne prend plus en charge Docker Engine nativement sur ses nœuds, mais les images construites avec &lt;code&gt;docker build&lt;/code&gt; fonctionnent avec tous les moteurs compatibles CRI, selon la &lt;a href="https://kubernetes.io/blog/2022/02/17/dockershim-faq/" rel="noopener noreferrer"&gt;FAQ officielle&lt;/a&gt;. Docker reste l'outil de construction et de développement local.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Compose peut-il servir en production ?
&lt;/h3&gt;

&lt;p&gt;Oui, sur une seule machine. La &lt;a href="https://docs.docker.com/compose/intro/features-uses/" rel="noopener noreferrer"&gt;documentation de Docker&lt;/a&gt; cite les déploiements sur un seul hôte parmi les usages de Compose. Au-delà d'une machine, il faut Docker Swarm ou Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment passer de Docker Compose à Kubernetes ?
&lt;/h3&gt;

&lt;p&gt;Kompose convertit un &lt;code&gt;compose.yaml&lt;/code&gt; avec &lt;code&gt;kompose convert -f compose.yaml&lt;/code&gt;. Relisez ensuite chaque fichier&amp;nbsp;: les variables d'environnement y sont en clair, &lt;code&gt;depends_on&lt;/code&gt; est ignoré et chaque volume devient une demande de stockage, selon le &lt;a href="https://kompose.io/conversion/" rel="noopener noreferrer"&gt;tableau de conversion&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quelle est la version actuelle de Kubernetes ?
&lt;/h3&gt;

&lt;p&gt;Au 1er novembre 2026, c'est la 1.37, sortie le 26 août 2026 et maintenue jusqu'au 28 octobre 2027, selon la &lt;a href="https://kubernetes.io/releases/" rel="noopener noreferrer"&gt;page des versions&lt;/a&gt;. Les versions 1.36 et 1.35 sont aussi maintenues, et la 1.38 est prévue le 16 décembre 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse documentaire fondée exclusivement sur des sources publiques consultées le 1er novembre 2026&amp;nbsp;: documentation, blog et pages de versions de Kubernetes, documentation de Docker, de Kompose et de k3s, pages de publication sur GitHub, pages de tarifs d'AWS, de Google Cloud et d'Azure, enquête Stack Overflow 2026. Les fichiers YAML ont été validés syntaxiquement mais n'ont été exécutés ni sur Docker ni sur un cluster. Les citations issues de sources en anglais sont traduites par nos soins. Aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux éditeurs mentionnés.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou un tarif périmé&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 1er novembre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image d'en-tête&amp;nbsp;: Tom Fisk (Pexels License). Graphiques&amp;nbsp;: NeurauTech, d'après Stack Overflow et kubernetes.io.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>devops</category>
      <category>conteneurs</category>
    </item>
    <item>
      <title>Apprendre TypeScript en 2026 : les bases, la différence avec JavaScript et la migration pas à pas</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sun, 01 Nov 2026 15:35:01 +0000</pubDate>
      <link>https://dev.to/neurau_tech/apprendre-typescript-en-2026-les-bases-la-difference-avec-javascript-et-la-migration-pas-a-pas-1ejo</link>
      <guid>https://dev.to/neurau_tech/apprendre-typescript-en-2026-les-bases-la-difference-avec-javascript-et-la-migration-pas-a-pas-1ejo</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Apprendre TypeScript prend peu de temps quand on connaît déjà JavaScript&amp;nbsp;: c'est le même langage, avec des types vérifiés avant l'exécution. Ce guide part de là. Si les variables, les fonctions, &lt;code&gt;map&lt;/code&gt; ou &lt;code&gt;async/await&lt;/code&gt; ne vous sont pas encore familiers, commencez par notre guide pour &lt;a href="https://neurautech.com/apprendre-javascript/" rel="noopener noreferrer"&gt;apprendre JavaScript&lt;/a&gt;&amp;nbsp;: nous ne reprenons pas ces bases ici.&lt;/p&gt;

&lt;p&gt;Chaque exemple a été vérifié avec le compilateur TypeScript 7.0.2 et exécuté avec Node.js 22.15.0 et l'option &lt;code&gt;--experimental-strip-types&lt;/code&gt;&amp;nbsp;; les erreurs affichées sont celles que le compilateur a produites. Versions et chiffres viennent de sources officielles lues le 31 octobre 2026.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript vérifie les types avant l'exécution, puis les efface&amp;nbsp;: il ne valide pas les données reçues.&lt;/li&gt;
&lt;li&gt;La version courante est TypeScript 7.0.2, un compilateur natif que Microsoft annonce de 8 à 12 fois plus rapide sur une compilation complète.&lt;/li&gt;
&lt;li&gt;Node.js exécute un fichier &lt;code&gt;.ts&lt;/code&gt; sans option depuis la version 22.18.0, si le code n'utilise que de la syntaxe effaçable, et sans vérifier les types.&lt;/li&gt;
&lt;li&gt;À apprendre en premier&amp;nbsp;: inférence, interfaces, unions, rétrécissement, génériques, et &lt;code&gt;unknown&lt;/code&gt; plutôt que &lt;code&gt;any&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Un projet JavaScript se migre fichier par fichier, avec &lt;code&gt;checkJs&lt;/code&gt; et JSDoc d'abord.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Ce que TypeScript ajoute à JavaScript, et ce qu'il ne fait pas
&lt;/h2&gt;

&lt;p&gt;TypeScript est une surcouche de JavaScript&amp;nbsp;: il ajoute une syntaxe de types, un vérificateur qui la contrôle avant l'exécution, et un outillage d'éditeur qui s'en sert. Le reste est du JavaScript.&lt;/p&gt;

&lt;p&gt;Ce qu'il apporte, concrètement&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Des types statiques.&lt;/strong&gt; Un appel avec une chaîne là où un &lt;code&gt;number&lt;/code&gt; est attendu est signalé avant l'exécution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Des erreurs à la compilation.&lt;/strong&gt; Propriété mal orthographiée, valeur peut-être &lt;code&gt;undefined&lt;/code&gt;, argument oublié&amp;nbsp;: &lt;code&gt;tsc&lt;/code&gt; les trouve sans exécuter le code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un éditeur plus utile.&lt;/strong&gt; Autocomplétion, renommage sûr et navigation s'appuient sur les types, dans &lt;a href="https://neurautech.com/meilleurs-ide-editeurs-code-2026-comparatif/" rel="noopener noreferrer"&gt;VS Code ou un autre IDE&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ses limites sont aussi importantes à comprendre pour apprendre TypeScript sans malentendu&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les types sont effacés à l'exécution.&lt;/strong&gt; Le code n'est pas plus rapide, et rien n'est vérifié pendant qu'il tourne.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aucune validation des données externes.&lt;/strong&gt; Une requête HTTP ou un fichier JSON arrivent sans garantie&amp;nbsp;; écrire &lt;code&gt;as Utilisateur&lt;/code&gt; ne vérifie rien.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;any&lt;/code&gt; désactive tout.&lt;/strong&gt; Une valeur &lt;code&gt;any&lt;/code&gt; n'est plus contrôlée, et l'erreur ressort à l'exécution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour les données externes, il faut une validation à l'exécution. Notre tutoriel pour &lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;créer une API REST avec Node.js et Express&lt;/a&gt; utilise Zod, une bibliothèque qui valide les données et dont on peut dériver les types TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qui utilise TypeScript ?
&lt;/h3&gt;

&lt;p&gt;TypeScript est sixième de l'enquête Stack Overflow 2026, utilisé par 43,8&amp;nbsp;% des 14&amp;nbsp;099 répondants à la question sur les langages (&lt;a href="https://survey.stackoverflow.co/2026/technology/data/languages" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2026&lt;/a&gt;, consulté le 31 octobre 2026). C'est un usage déclaré, pas une mesure du marché.&lt;/p&gt;

&lt;p&gt;Selon le rapport Octoverse 2026 de GitHub, TypeScript reste le premier langage par nombre de contributeurs. De septembre 2025 à septembre 2026, il a gagné 3,62&amp;nbsp;millions de contributeurs mensuels, contre 3,23&amp;nbsp;millions pour Python et 1,82&amp;nbsp;million pour JavaScript (&lt;a href="https://github.blog/news-insights/octoverse/octoverse-commits-pushes-reach-new-highs-in-2026-as-developers-build-with-agents/" rel="noopener noreferrer"&gt;GitHub Octoverse 2026&lt;/a&gt;, 28 octobre 2026).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmkds6tye93eygerle3go.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmkds6tye93eygerle3go.webp" alt="Pourquoi apprendre TypeScript&amp;nbsp;: graphique en barres des contributeurs mensuels gagnés sur GitHub de septembre 2025 à septembre 2026, 3,62&amp;nbsp;millions pour TypeScript, 3,23&amp;nbsp;millions pour Python et 1,82&amp;nbsp;million pour JavaScript" width="799" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le rapport compte les comptes actifs chaque mois, dépôts privés inclus, et un même compte peut compter pour plusieurs langages.&lt;/p&gt;




&lt;h2&gt;
  
  
  TypeScript vs JavaScript : le tableau des différences
&lt;/h2&gt;

&lt;p&gt;La différence entre TypeScript et JavaScript tient au moment où les erreurs de type sont détectées&amp;nbsp;: avant l'exécution pour TypeScript, pendant pour JavaScript.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Critère&lt;/th&gt;
&lt;th&gt;JavaScript&lt;/th&gt;
&lt;th&gt;TypeScript&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Typage&lt;/td&gt;
&lt;td&gt;Dynamique, vérifié à l'exécution&lt;/td&gt;
&lt;td&gt;Statique, vérifié avant l'exécution, puis effacé&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exécution&lt;/td&gt;
&lt;td&gt;Directe dans le navigateur et Node.js&lt;/td&gt;
&lt;td&gt;Compilé en JavaScript, ou types retirés par Node.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Erreur &lt;code&gt;"5" + 3&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Silencieuse, donne &lt;code&gt;"53"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Signalée si un &lt;code&gt;number&lt;/code&gt; est attendu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation des données externes&lt;/td&gt;
&lt;td&gt;À écrire soi-même&lt;/td&gt;
&lt;td&gt;À écrire soi-même aussi (Zod, Valibot, contrôles manuels)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Éditeur&lt;/td&gt;
&lt;td&gt;Autocomplétion approximative&lt;/td&gt;
&lt;td&gt;Autocomplétion, renommage et navigation fondés sur les types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performances à l'exécution&lt;/td&gt;
&lt;td&gt;Référence&lt;/td&gt;
&lt;td&gt;Identiques&amp;nbsp;: c'est le même JavaScript qui tourne&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Projets adaptés&lt;/td&gt;
&lt;td&gt;Scripts, prototypes, petites pages&lt;/td&gt;
&lt;td&gt;Code partagé en équipe, bibliothèques, applications qui durent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;TypeScript n'accélère donc pas l'exécution&amp;nbsp;: il réduit les erreurs qui l'atteignent. Le chiffre de 15&amp;nbsp;% de bugs évités, souvent cité, vient d'une étude de Zheng Gao, Christian Bird et Earl T. Barr présentée à la conférence ICSE 2017. Après avoir annoté à la main le code de 400 bugs publics de projets JavaScript sur GitHub, ils estiment que TypeScript 2.0 et Flow 0.30 en détectaient chacun 60, soit 15&amp;nbsp;% (intervalle de confiance à 95&amp;nbsp;%&amp;nbsp;: 11,5&amp;nbsp;% à 18,5&amp;nbsp;%) (&lt;a href="https://janvitek.org/events/NEU/7575/pubs/no-type.pdf" rel="noopener noreferrer"&gt;«&amp;nbsp;To Type or Not to Type&amp;nbsp;», ICSE 2017&lt;/a&gt;, DOI 10.1109/ICSE.2017.75).&lt;/p&gt;

&lt;p&gt;Réserves&amp;nbsp;: l'étude porte sur TypeScript 2.0, ne compte que des bugs déjà publiés (ce qui, selon les auteurs, sous-estime l'effet) et mesure des bugs détectables, pas évités.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installer TypeScript 7 et configurer tsconfig
&lt;/h2&gt;

&lt;p&gt;TypeScript s'installe dans chaque projet, comme dépendance de développement. Le &lt;a href="https://registry.npmjs.org/typescript/latest" rel="noopener noreferrer"&gt;registre npm&lt;/a&gt; sert la version 7.0.2 sous l'étiquette &lt;code&gt;latest&lt;/code&gt; (consulté le 31 octobre 2026), et le &lt;a href="https://www.typescriptlang.org/download/" rel="noopener noreferrer"&gt;site officiel&lt;/a&gt; indique «&amp;nbsp;currently 7.0&amp;nbsp;».&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; typescript
npx tsc &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; Version 7.0.2&lt;/span&gt;
npm pkg &lt;span class="nb"&gt;set type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La dernière commande ajoute &lt;code&gt;"type": "module"&lt;/code&gt; au &lt;code&gt;package.json&lt;/code&gt;. Sans elle, avec la configuration ci-dessous, &lt;code&gt;tsc&lt;/code&gt; traite vos fichiers comme du CommonJS et refuse &lt;code&gt;import&lt;/code&gt; et &lt;code&gt;export&lt;/code&gt; (erreurs TS1295 et TS1287 dans notre essai).&lt;/p&gt;

&lt;h3&gt;
  
  
  Ce qui change avec TypeScript 7
&lt;/h3&gt;

&lt;p&gt;TypeScript 7 est une réécriture du compilateur en code natif. Selon son annonce, il donne «&amp;nbsp;typiquement des accélérations entre 8x et 12x sur les compilations complètes&amp;nbsp;» (&lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/" rel="noopener noreferrer"&gt;blog de TypeScript&lt;/a&gt;, 8 juillet 2026, notre traduction). Ce sont des mesures de Microsoft (équipe TypeScript)&amp;nbsp;; le graphique reprend les cinq projets publiés, de 7,7x à 11,9x.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffm4bky4lodum31ex4lri.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffm4bky4lodum31ex4lri.webp" alt="Graphique en haltères&amp;nbsp;: temps de compilation complète de TypeScript 6.0 à TypeScript 7.0 selon l'équipe TypeScript, de 125,7 à 10,6&amp;nbsp;secondes pour vscode et de 139,8 à 15,7&amp;nbsp;secondes pour sentry" width="799" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Trois conséquences pour qui débute en 2026&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;strict&lt;/code&gt; est activé par défaut&lt;/strong&gt; depuis TypeScript 6.0, et &lt;code&gt;module&lt;/code&gt; vaut &lt;code&gt;esnext&lt;/code&gt;. Les options dépréciées en 6.0, comme &lt;code&gt;target: es5&lt;/code&gt; ou &lt;code&gt;baseUrl&lt;/code&gt;, sont devenues des erreurs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le compilateur n'expose pas encore d'API programmable.&lt;/strong&gt; Le contrôle des gabarits Vue, Svelte, Angular, Astro ou MDX n'est donc pas encore pris en charge et ces outils restent sur TypeScript 6.0. TypeScript 7.1, en bêta depuis le 20 octobre 2026, doit apporter une nouvelle API (&lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-1-beta/" rel="noopener noreferrer"&gt;annonce de TypeScript 7.1 Beta&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'éditeur.&lt;/strong&gt; L'annonce indique que VS Code dispose d'une extension dédiée à TypeScript 7. Pour bien équiper l'éditeur par ailleurs, voyez notre sélection d'&lt;a href="https://neurautech.com/meilleures-extensions-vs-code-en-2026-pour-devs/" rel="noopener noreferrer"&gt;extensions VS Code&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Le tsconfig.json essentiel
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npx tsc --init&lt;/code&gt; génère un fichier commenté qui active notamment &lt;code&gt;strict&lt;/code&gt; et &lt;code&gt;noUncheckedIndexedAccess&lt;/code&gt;, et fixe &lt;code&gt;"types": []&lt;/code&gt;&amp;nbsp;: pour Node.js, ajoutez &lt;code&gt;"types": ["node"]&lt;/code&gt; et installez &lt;code&gt;@types/node&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Voici la configuration utilisée pour nos exemples, celle que recommande la &lt;a href="https://nodejs.org/api/typescript.html" rel="noopener noreferrer"&gt;documentation de Node.js&lt;/a&gt;, plus &lt;code&gt;strict&lt;/code&gt; et &lt;code&gt;skipLibCheck&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodenext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noEmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"erasableSyntaxOnly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"verbatimModuleSyntax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rewriteRelativeImportExtensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Rôle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strict&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toutes les vérifications strictes, dont &lt;code&gt;noImplicitAny&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;noEmit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Vérifie sans produire de fichiers &lt;code&gt;.js&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;erasableSyntaxOnly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refuse la syntaxe que Node.js ne sait pas effacer, comme &lt;code&gt;enum&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verbatimModuleSyntax&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Impose &lt;code&gt;import type&lt;/code&gt; pour les imports de types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rewriteRelativeImportExtensions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Autorise les imports en &lt;code&gt;.ts&lt;/code&gt; et les réécrit en &lt;code&gt;.js&lt;/code&gt; lors d'une compilation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Exécuter un fichier .ts directement avec Node.js
&lt;/h2&gt;

&lt;p&gt;Node.js sait retirer les annotations d'un fichier &lt;code&gt;.ts&lt;/code&gt; et exécuter le JavaScript restant. Selon sa documentation, cette suppression des types est activée par défaut depuis les versions 23.6.0 et 22.18.0, et elle est déclarée stable depuis les versions 25.2.0 et 24.12.0 (&lt;a href="https://nodejs.org/api/typescript.html" rel="noopener noreferrer"&gt;TypeScript dans Node.js&lt;/a&gt;, consulté le 31 octobre 2026).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node bases.ts          &lt;span class="c"&gt;# Node.js 22.18.0 ou plus récent&lt;/span&gt;
npx tsc &lt;span class="nt"&gt;--noEmit&lt;/span&gt;       &lt;span class="c"&gt;# la vérification des types, à lancer à part&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quatre règles évitent les surprises&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Node.js ne vérifie pas les types.&lt;/strong&gt; Sa &lt;a href="https://nodejs.org/learn/typescript/run-natively" rel="noopener noreferrer"&gt;page d'apprentissage&lt;/a&gt; recommande &lt;code&gt;npx tsc --noEmit&lt;/code&gt; à côté.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seule la syntaxe effaçable passe.&lt;/strong&gt; &lt;code&gt;enum&lt;/code&gt;, les propriétés de paramètres, les &lt;code&gt;namespace&lt;/code&gt; qui contiennent du code et les alias &lt;code&gt;import x = require(...)&lt;/code&gt; demandent une transformation. L'option &lt;code&gt;--experimental-transform-types&lt;/code&gt;, qui contournait cette limite, a été retirée dans Node.js 26.0.0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'extension est obligatoire dans les imports&lt;/strong&gt;&amp;nbsp;: &lt;code&gt;import "./fichier.ts"&lt;/code&gt;, pas &lt;code&gt;import "./fichier"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les imports de types portent le mot-clé &lt;code&gt;type&lt;/code&gt;&lt;/strong&gt;, sinon le programme échoue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Les fichiers &lt;code&gt;.tsx&lt;/code&gt; et les &lt;code&gt;paths&lt;/code&gt; du tsconfig ne sont pas pris en charge non plus&amp;nbsp;: dans ce cas, compilez avec &lt;code&gt;tsc&lt;/code&gt; ou un outil de build.&lt;/p&gt;




&lt;h2&gt;
  
  
  Apprendre TypeScript par les notions clés
&lt;/h2&gt;

&lt;p&gt;Ces exemples passent tous &lt;code&gt;tsc&lt;/code&gt; avec la configuration ci-dessus&amp;nbsp;; &lt;code&gt;// -&amp;gt;&lt;/code&gt; indique la sortie obtenue. C'est l'ordre que nous conseillons pour apprendre TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Annotations et inférence
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prenom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Inès&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                 &lt;span class="c1"&gt;// type inféré : number&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;langage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TypeScript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// type inféré : "TypeScript"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;prixTTC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prixHT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;taux&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prixHT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;taux&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prenom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; a &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; points`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; Inès a 15 points&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prixTTC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;langage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;         &lt;span class="c1"&gt;// -&amp;gt; 120 TypeScript&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;N'annotez pas tout. TypeScript déduit le type de &lt;code&gt;score&lt;/code&gt; de sa valeur initiale. Annotez surtout les paramètres de fonctions et les valeurs de retour publiques&amp;nbsp;: c'est là que les types documentent votre code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interfaces et alias de type
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Utilisateur&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// propriété facultative&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Admin&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Utilisateur&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;droits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ada&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;droits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pas d'e-mail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; Ada pas d'e-mail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le manuel officiel note qu'une interface peut être rouverte pour recevoir de nouvelles propriétés, pas un alias &lt;code&gt;type&lt;/code&gt;, et propose une règle&amp;nbsp;: utiliser &lt;code&gt;interface&lt;/code&gt; jusqu'à avoir besoin d'une fonction propre à &lt;code&gt;type&lt;/code&gt; (par exemple une union) (&lt;a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html" rel="noopener noreferrer"&gt;Everyday Types&lt;/a&gt;, notre traduction).&lt;/p&gt;

&lt;h3&gt;
  
  
  Unions, types littéraux et rétrécissement
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editeur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lecteur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// union de types littéraux&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valeur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;valeur&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;valeur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// ici, valeur est un number&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;valeur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;// ici, valeur est une string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Resultat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;valeur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;erreur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;afficher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Resultat&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Valeur : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valeur&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Erreur : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;erreur&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;formater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3.14159&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;formater&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 3.14 TS&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;afficher&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;erreur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fichier introuvable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="c1"&gt;// -&amp;gt; Erreur : fichier introuvable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le rétrécissement (narrowing) est le cœur de TypeScript au quotidien&amp;nbsp;: après un test &lt;code&gt;typeof&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt; ou sur une propriété commune comme &lt;code&gt;ok&lt;/code&gt;, le compilateur sait quel membre de l'union vous manipulez.&lt;/p&gt;

&lt;p&gt;Pour qu'un &lt;code&gt;switch&lt;/code&gt; traite tous les cas d'une union, ajoutez une vérification d'exhaustivité avec le type &lt;code&gt;never&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Forme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cercle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;rayon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;carre&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;aire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Forme&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cercle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rayon&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;carre&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cote&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;jamais&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// erreur si un cas est oublié&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jamais&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;aire&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;carre&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 9.00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si l'on ajoute &lt;code&gt;{ type: "triangle"; base: number; hauteur: number }&lt;/code&gt; à l'union sans nouveau &lt;code&gt;case&lt;/code&gt;, &lt;code&gt;tsc&lt;/code&gt; répond&amp;nbsp;: «&amp;nbsp;Impossible d'assigner le type '{ type: "triangle"; base: number; hauteur: number; }' au type 'never'&amp;nbsp;» (TS2322).&lt;/p&gt;

&lt;h3&gt;
  
  
  Les génériques
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;premier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;liste&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;liste&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Reponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;statut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;donnees&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;premier&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// number | undefined&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Reponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;donnees&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;donnees&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 10 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un générique est un type en paramètre. &lt;code&gt;premier&lt;/code&gt; fonctionne pour toute liste et conserve le type de ses éléments, là où &lt;code&gt;any&lt;/code&gt; le perdrait.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les types utilitaires
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Article&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;titre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;contenu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;publie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MiseAJour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Article&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// tout devient facultatif&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Apercu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;titre&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// garde deux propriétés&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NouvelArticle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// retire id&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Compteurs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vues&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;partages&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apercu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Readonly&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Apercu&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;titre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Apprendre TypeScript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ils évitent de recopier une interface pour chaque variante. &lt;code&gt;Partial&lt;/code&gt; sert aux mises à jour partielles d'une API, &lt;code&gt;Omit&lt;/code&gt; aux données de création sans identifiant.&lt;/p&gt;

&lt;p&gt;L'opérateur &lt;code&gt;satisfies&lt;/code&gt; vérifie qu'une valeur respecte un type sans perdre le type précis de chaque propriété&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Couleur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rouge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bleu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;palette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;rouge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#e11d48&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;vert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;197&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;94&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;satisfies &lt;/span&gt;&lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Couleur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;palette&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rouge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; #E11D48&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;palette&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;      &lt;span class="c1"&gt;// -&amp;gt; 34, 197, 94&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Une clé &lt;code&gt;jaune&lt;/code&gt; serait refusée (TS2353). Avec une simple annotation de type, &lt;code&gt;palette.rouge.toUpperCase()&lt;/code&gt; échouerait (TS18048 et TS2339).&lt;/p&gt;

&lt;h3&gt;
  
  
  enum ou union de littéraux ?
&lt;/h3&gt;

&lt;p&gt;Préférez une union de littéraux, ou un objet &lt;code&gt;as const&lt;/code&gt;, à &lt;code&gt;enum&lt;/code&gt;. Avec &lt;code&gt;erasableSyntaxOnly&lt;/code&gt;, &lt;code&gt;tsc&lt;/code&gt; refuse &lt;code&gt;enum&lt;/code&gt; (erreur TS1294), et Node.js 22.15.0 échoue avec &lt;code&gt;ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX&lt;/code&gt;&amp;nbsp;: «&amp;nbsp;TypeScript enum is not supported in strip-only mode&amp;nbsp;». L'alternative fonctionne partout&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Statut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Brouillon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;brouillon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Publie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;as const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Statut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Statut&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Statut&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// "brouillon" | "publie"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Statut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Statut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Publie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; publie&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  any ou unknown ?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;any&lt;/code&gt; accepte tout et ne vérifie plus rien. Le code suivant passe &lt;code&gt;tsc&lt;/code&gt; en mode strict sans la moindre erreur, puis plante&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;donnee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;texte&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;donnee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; TypeError: donnee.toFixed is not a function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;unknown&lt;/code&gt; accepte tout aussi, mais interdit de s'en servir avant de l'avoir vérifié. Appeler &lt;code&gt;.toUpperCase()&lt;/code&gt; sur une valeur &lt;code&gt;unknown&lt;/code&gt; produit l'erreur TS18046. Voici la bonne façon d'en sortir, avec une fonction de garde&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;brut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"nom": "Ada", "age": 36}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;estPersonne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nom&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;estPersonne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;brut&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;brut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;brut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ans`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; Ada, 36 ans&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Valider à l'exécution avec Zod
&lt;/h3&gt;

&lt;p&gt;Écrire ces gardes à la main devient vite pénible. Zod décrit le schéma une fois, valide à l'exécution et fournit le type&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kd"&gt;as &lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Inscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Inscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Inscription&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// { email: string; age: number }&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"email": "ada@example.com", "age": "36"}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resultat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Inscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;resultat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flattenError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resultat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;fieldErrors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// -&amp;gt; { age: [ 'Invalid input: expected number, received string' ] }&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le compilateur ne pouvait pas voir que &lt;code&gt;age&lt;/code&gt; arrivait sous forme de chaîne&amp;nbsp;: seule la validation à l'exécution l'a détecté. Exemple exécuté avec Zod 4.6.7, la version servie par le &lt;a href="https://registry.npmjs.org/zod/latest" rel="noopener noreferrer"&gt;registre npm&lt;/a&gt; le 31 octobre 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  Migrer un petit projet JavaScript en 4 étapes
&lt;/h2&gt;

&lt;p&gt;Nous avons déroulé une migration sur un projet de deux fichiers qui calcule un panier. Le bug est classique&amp;nbsp;: des frais de port lus dans un formulaire arrivent sous forme de chaîne. Fichier &lt;code&gt;src/panier.js&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fraisPort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sousTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;somme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;somme&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prix&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sousTotal&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fraisPort&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fichier &lt;code&gt;src/main.js&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./panier.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;panier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clavier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;49.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fraisSaisis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4.90&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// valeur lue dans un champ de formulaire&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;panier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fraisSaisis&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 49.94.90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Étape 1&amp;nbsp;: installer TypeScript et vérifier le JavaScript tel quel.&lt;/strong&gt; Le &lt;code&gt;package.json&lt;/code&gt; contient déjà &lt;code&gt;"type": "module"&lt;/code&gt;. Un &lt;code&gt;tsconfig.json&lt;/code&gt; avec &lt;code&gt;"allowJs": true&lt;/code&gt;, &lt;code&gt;"checkJs": true&lt;/code&gt;, &lt;code&gt;"noEmit": true&lt;/code&gt; et &lt;code&gt;"strict": false&lt;/code&gt; fait analyser les fichiers &lt;code&gt;.js&lt;/code&gt; sans les modifier. Sur ce projet, &lt;code&gt;npx tsc&lt;/code&gt; ne signale rien&amp;nbsp;: sans annotations, le compilateur ne sait pas ce que &lt;code&gt;fraisPort&lt;/code&gt; doit contenir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Étape 2&amp;nbsp;: ajouter des types en JSDoc.&lt;/strong&gt; Les commentaires JSDoc donnent des types sans changer d'extension&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * @typedef {{ nom: string, prix: number, quantite: number }} Article
 * @param {Article[]} articles
 * @param {number} fraisPort
 * @returns {number}
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cette fois, &lt;code&gt;npx tsc --locale fr&lt;/code&gt; trouve le bug&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/main.js(4,27): error TS2345: L'argument de type 'string' n'est pas attribuable au paramètre de type 'number'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Étape 3&amp;nbsp;: renommer en &lt;code&gt;.ts&lt;/code&gt; et passer en &lt;code&gt;strict&lt;/code&gt;.&lt;/strong&gt; Les types JSDoc deviennent des annotations, l'interface &lt;code&gt;Article&lt;/code&gt; est exportée et importée avec &lt;code&gt;import { total, type Article } from "./panier.ts"&lt;/code&gt;. La configuration devient celle de la section tsconfig (gardez &lt;code&gt;"type": "module"&lt;/code&gt;). On corrige l'appel dans &lt;code&gt;src/main.ts&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;montant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;total&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;panier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fraisSaisis&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;montant&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// -&amp;gt; 54.8&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;montant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 54.80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;tsc&lt;/code&gt; ne signale plus rien et le programme affiche les deux lignes ci-dessus.&lt;/p&gt;

&lt;p&gt;Si l'on oublie le mot-clé &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;tsc&lt;/code&gt; signale l'erreur TS1484, et Node.js échoue avec «&amp;nbsp;The requested module './panier.ts' does not provide an export named 'Article'&amp;nbsp;».&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Étape 4&amp;nbsp;: compiler pour la production, si besoin.&lt;/strong&gt; &lt;code&gt;npx tsc --noEmit false --rootDir src --outDir dist&lt;/code&gt; produit &lt;code&gt;dist/main.js&lt;/code&gt; et &lt;code&gt;dist/panier.js&lt;/code&gt;, import réécrit en &lt;code&gt;./panier.js&lt;/code&gt;. &lt;code&gt;rootDir&lt;/code&gt; désigne le dossier racine des sources, dont l'arborescence est reproduite dans &lt;code&gt;outDir&lt;/code&gt;. Depuis TypeScript 6.0, il vaut par défaut le dossier du &lt;code&gt;tsconfig.json&lt;/code&gt; (&lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/" rel="noopener noreferrer"&gt;annonce de TypeScript 6.0&lt;/a&gt;). Sans &lt;code&gt;--rootDir&lt;/code&gt;, TypeScript 7.0.2 refuse ici de compiler (erreur TS5011, dont le message renvoie au guide de migration de la version 6).&lt;/p&gt;

&lt;p&gt;Dans un vrai projet, migrez fichier par fichier, en commençant par les modules sans dépendances, et ajoutez &lt;code&gt;tsc --noEmit&lt;/code&gt; à votre intégration continue. Notre &lt;a href="https://neurautech.com/github-actions-tutoriel/" rel="noopener noreferrer"&gt;tutoriel GitHub Actions&lt;/a&gt; montre comment lancer ce genre de vérification à chaque push.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lire les erreurs du compilateur
&lt;/h2&gt;

&lt;p&gt;Une erreur TypeScript donne le fichier, la position (ligne, colonne), un code &lt;code&gt;TSxxxx&lt;/code&gt;, utile pour chercher de l'aide, puis le message, en français avec l'option &lt;code&gt;--locale fr&lt;/code&gt;. Voici des erreurs provoquées volontairement, avec le texte exact de TypeScript 7.0.2&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Message de tsc (avec --locale fr)&lt;/th&gt;
&lt;th&gt;Cause et correction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TS2322&lt;/td&gt;
&lt;td&gt;Impossible d'assigner le type 'string' au type 'number'.&lt;/td&gt;
&lt;td&gt;Une variable &lt;code&gt;number&lt;/code&gt; reçoit une chaîne.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS2345&lt;/td&gt;
&lt;td&gt;L'argument de type 'string' n'est pas attribuable au paramètre de type 'number'.&lt;/td&gt;
&lt;td&gt;Mauvais argument&amp;nbsp;: convertissez avec &lt;code&gt;Number()&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS2551&lt;/td&gt;
&lt;td&gt;La propriété 'mail' n'existe pas sur le type 'Utilisateur'. Est-ce qu'il ne s'agit pas plutôt de 'email'&amp;nbsp;?&lt;/td&gt;
&lt;td&gt;Faute de frappe&amp;nbsp;: suivez la suggestion.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS18048&lt;/td&gt;
&lt;td&gt;'u.email' est peut-être 'non défini'.&lt;/td&gt;
&lt;td&gt;Propriété facultative. Testez-la ou utilisez &lt;code&gt;?.&lt;/code&gt; et &lt;code&gt;??&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS7006&lt;/td&gt;
&lt;td&gt;Le paramètre 'x' possède implicitement un type 'any'.&lt;/td&gt;
&lt;td&gt;Paramètre non annoté en mode strict&amp;nbsp;: annotez-le.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS18046&lt;/td&gt;
&lt;td&gt;'inconnue' est de type 'unknown'.&lt;/td&gt;
&lt;td&gt;Rétrécissez la valeur avant de l'utiliser.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Les messages longs s'emboîtent. Pour &lt;code&gt;const premiere: number = notes.find((n) =&amp;gt; n &amp;gt; 20)&lt;/code&gt;, &lt;code&gt;tsc&lt;/code&gt; ajoute en retrait «&amp;nbsp;Impossible d'assigner le type 'undefined' au type 'number'&amp;nbsp;»&amp;nbsp;: la dernière ligne donne la cause précise. Ici, &lt;code&gt;find&lt;/code&gt; peut ne rien trouver.&lt;/p&gt;

&lt;p&gt;Deux réflexes à éviter&amp;nbsp;: multiplier les &lt;code&gt;as&lt;/code&gt; pour faire taire le compilateur, et répondre à chaque erreur par &lt;code&gt;any&lt;/code&gt;. Les deux déplacent le bug vers l'exécution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les ressources pour progresser
&lt;/h2&gt;

&lt;p&gt;Ressources ouvertes le 31 octobre 2026&amp;nbsp;; l'offre en français est plus mince que pour JavaScript.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ressource&lt;/th&gt;
&lt;th&gt;Langue&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Accès&lt;/th&gt;
&lt;th&gt;Pour quoi faire&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.typescriptlang.org/fr/docs/" rel="noopener noreferrer"&gt;Documentation officielle en français&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Français, en partie&lt;/td&gt;
&lt;td&gt;Guides et manuel&lt;/td&gt;
&lt;td&gt;Gratuit&lt;/td&gt;
&lt;td&gt;«&amp;nbsp;TypeScript pour les programmeurs JS&amp;nbsp;», puis le manuel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.typescriptlang.org/docs/handbook/intro.html" rel="noopener noreferrer"&gt;Manuel TypeScript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Anglais&lt;/td&gt;
&lt;td&gt;Manuel officiel&lt;/td&gt;
&lt;td&gt;Gratuit&lt;/td&gt;
&lt;td&gt;La référence, notamment Everyday Types et Narrowing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://openclassrooms.com/fr/courses/8039116-decouvrez-typescript" rel="noopener noreferrer"&gt;OpenClassrooms, Découvrez TypeScript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Français&lt;/td&gt;
&lt;td&gt;Cours de 4&amp;nbsp;heures&lt;/td&gt;
&lt;td&gt;Libre accès&lt;/td&gt;
&lt;td&gt;Les bases, les génériques et un projet React&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grafikart.fr/formations/typescript" rel="noopener noreferrer"&gt;Grafikart, Apprendre TypeScript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Français&lt;/td&gt;
&lt;td&gt;19 chapitres, 4&amp;nbsp;h&amp;nbsp;37 de vidéos&lt;/td&gt;
&lt;td&gt;Non précisé par la page&lt;/td&gt;
&lt;td&gt;Des bases jusqu'à Zod et React&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.totaltypescript.com/tutorials/beginners-typescript" rel="noopener noreferrer"&gt;Total TypeScript, Beginners&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Anglais&lt;/td&gt;
&lt;td&gt;18 leçons&lt;/td&gt;
&lt;td&gt;Gratuit&lt;/td&gt;
&lt;td&gt;Des exercices interactifs, par Matt Pocock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.typescriptlang.org/play/" rel="noopener noreferrer"&gt;Bac à sable officiel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Interface en anglais&lt;/td&gt;
&lt;td&gt;Éditeur en ligne&lt;/td&gt;
&lt;td&gt;Gratuit&lt;/td&gt;
&lt;td&gt;Tester un type sans rien installer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La documentation française est partielle&amp;nbsp;: Everyday Types et Utility Types, entre autres, restent en anglais. Le cours d'OpenClassrooms date du 19 août 2025, avant TypeScript 7.&lt;/p&gt;

&lt;p&gt;Pour pratiquer, réécrivez en TypeScript les exercices d'un cours que vous connaissez déjà&amp;nbsp;: notre série d'&lt;a href="https://neurautech.com/exercices-python-debutant-corriges/" rel="noopener noreferrer"&gt;exercices Python corrigés&lt;/a&gt; se transpose bien. Un petit projet typé de bout en bout, publié sur GitHub, a aussi sa place dans un &lt;a href="https://neurautech.com/portfolio-developpeur-comment-le-construire-2026/" rel="noopener noreferrer"&gt;portfolio de développeur&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Points incertains ou non testés&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les exemples sous une version récente de Node.js.&lt;/strong&gt; Ils ont été exécutés avec Node.js 22.15.0, la version de notre poste, et l'option &lt;code&gt;--experimental-strip-types&lt;/code&gt;. Nous n'avons pas constaté nous-mêmes que la 22.18.0 s'en passe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les gains de vitesse de TypeScript 7.&lt;/strong&gt; Ce sont les mesures de Microsoft (équipe TypeScript)&amp;nbsp;; nous n'en avons fait aucune.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'accès gratuit aux vidéos de Grafikart&lt;/strong&gt;, que la page de la formation ne précise pas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La date de sortie de TypeScript 7.1&lt;/strong&gt;, que l'annonce de la bêta ne donne pas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La représentativité des chiffres d'usage.&lt;/strong&gt; Aucune des deux sources ne décrit le marché français.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La portée actuelle de l'étude de 2017.&lt;/strong&gt; Ses 15&amp;nbsp;% portent sur TypeScript 2.0&amp;nbsp;; nous n'avons trouvé aucune mesure comparable pour une version récente.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Faut-il connaître JavaScript avant d'apprendre TypeScript ?
&lt;/h3&gt;

&lt;p&gt;Oui. TypeScript ajoute des types à JavaScript sans en changer le fonctionnement&amp;nbsp;: les erreurs d'exécution, l'asynchrone et le DOM restent ceux de JavaScript. Notre guide pour &lt;a href="https://neurautech.com/apprendre-javascript/" rel="noopener noreferrer"&gt;apprendre JavaScript&lt;/a&gt; couvre ce prérequis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quelle est la différence entre TypeScript et JavaScript ?
&lt;/h3&gt;

&lt;p&gt;TypeScript vérifie les types avant l'exécution, puis les retire&amp;nbsp;: le code qui tourne est du JavaScript. Il ne valide pas les données reçues, mais détecte de nombreuses erreurs plus tôt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quelle version de TypeScript installer en 2026 ?
&lt;/h3&gt;

&lt;p&gt;La version servie par npm sous l'étiquette &lt;code&gt;latest&lt;/code&gt;, soit TypeScript 7.0.2 au 31 octobre 2026. Si vous utilisez Vue, Svelte, Angular ou Astro, vérifiez leur compatibilité&amp;nbsp;: selon &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/" rel="noopener noreferrer"&gt;l'annonce de TypeScript 7.0&lt;/a&gt;, leurs outils reposent encore sur TypeScript 6.0.&lt;/p&gt;

&lt;h3&gt;
  
  
  Peut-on exécuter TypeScript sans compilation ?
&lt;/h3&gt;

&lt;p&gt;Oui, avec Node.js 22.18.0 ou plus récent&amp;nbsp;: &lt;code&gt;node fichier.ts&lt;/code&gt; retire les types et exécute le reste, si le code n'utilise que de la syntaxe effaçable (&lt;a href="https://nodejs.org/api/typescript.html" rel="noopener noreferrer"&gt;documentation de Node.js&lt;/a&gt;). Les types ne sont pas vérifiés pour autant&amp;nbsp;: lancez &lt;code&gt;npx tsc --noEmit&lt;/code&gt; à part.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse fondée sur des sources publiques consultées les 31 octobre et 1er novembre 2026&amp;nbsp;: registre npm, blog et documentation de TypeScript, documentation de Node.js, enquête Stack Overflow 2026, Octoverse 2026, étude ICSE 2017, pages des cours cités. Chaque exemple de code a été vérifié avec TypeScript 7.0.2, y compris la sortie de &lt;code&gt;tsc --init&lt;/code&gt; et le projet de migration, et exécuté avec Node.js 22.15.0 et l'option &lt;code&gt;--experimental-strip-types&lt;/code&gt;. L'exemple Zod utilise Zod 4.6.7. Les erreurs du tableau sont copiées de &lt;code&gt;tsc --locale fr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Aucun cours n'a été suivi en entier, aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux plateformes mentionnées.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou une version périmée&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 1er novembre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Timothy Cuenat (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>node</category>
      <category>developpementweb</category>
    </item>
    <item>
      <title>Double authentification : l'activer sur Gmail, Outlook, Facebook, Orange et choisir la bonne méthode</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:44:40 +0000</pubDate>
      <link>https://dev.to/neurau_tech/double-authentification-lactiver-sur-gmail-outlook-facebook-orange-et-choisir-la-bonne-methode-24m2</link>
      <guid>https://dev.to/neurau_tech/double-authentification-lactiver-sur-gmail-outlook-facebook-orange-et-choisir-la-bonne-methode-24m2</guid>
      <description>&lt;h2&gt;
  
  
  Pourquoi activer la double authentification maintenant
&lt;/h2&gt;

&lt;p&gt;Le piratage de compte reste l'une des premières causes de demande d'aide en France. Selon le bilan 2025 de Cybermalveillance.gouv.fr, il arrive en deuxième position chez les particuliers, avec plus de 11&amp;nbsp;% des assistances malgré une «&amp;nbsp;légère baisse en volume (-2&amp;nbsp;%)&amp;nbsp;», juste derrière l'hameçonnage qui en représente près de 33&amp;nbsp;% (&lt;a href="https://www.cybermalveillance.gouv.fr/tous-nos-contenus/actualites/top-10-cybermalveillances-particuliers-2025" rel="noopener noreferrer"&gt;Cybermalveillance.gouv.fr&lt;/a&gt;, publié le 22 avril 2026, mis à jour le 29 octobre 2026).&lt;/p&gt;

&lt;p&gt;Chez les professionnels, le piratage de compte passe en tête. Il représente 21&amp;nbsp;% des parcours d'assistance des entreprises et associations, «&amp;nbsp;en croissance de 52&amp;nbsp;%&amp;nbsp;» selon l'organisme, et 20,1&amp;nbsp;% de ceux des collectivités, avec un volume en hausse de 14&amp;nbsp;% (&lt;a href="https://www.cybermalveillance.gouv.fr/tous-nos-contenus/actualites/top10-cybermalveillances-professionnels-2025" rel="noopener noreferrer"&gt;Cybermalveillance.gouv.fr&lt;/a&gt;, publié le 6 mai 2026).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fge8s5o6qwd2ydifcx6vr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fge8s5o6qwd2ydifcx6vr.webp" alt="Graphique en barres&amp;nbsp;: part du piratage de compte dans les demandes d'assistance de Cybermalveillance.gouv.fr en 2025, plus de 11&amp;nbsp;% chez les particuliers, 20,1&amp;nbsp;% chez les collectivités et 21&amp;nbsp;% chez les entreprises, un risque que la double authentification réduit" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La double authentification ne règle pas tout. La CNIL le dit elle-même&amp;nbsp;: le dispositif «&amp;nbsp;n'est pas infaillible&amp;nbsp;» et reste exposé au phishing en temps réel, à l'interception des SMS et au détournement de carte SIM (&lt;a href="https://www.cnil.fr/fr/securite-utilisez-lauthentification-multifacteur-pour-vos-comptes-en-ligne" rel="noopener noreferrer"&gt;CNIL&lt;/a&gt;, fiche de 2021 en cours de mise à jour). Pour situer ce risque, voyez notre panorama des &lt;a href="https://neurautech.com/cyberattaques-les-plus-frequentes-comment-proteger/" rel="noopener noreferrer"&gt;cyberattaques les plus fréquentes&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2FA, MFA, validation en deux étapes : les mots exacts
&lt;/h2&gt;

&lt;p&gt;Il existe trois catégories de facteurs&amp;nbsp;: ce que vous savez (un mot de passe, un code PIN), ce que vous possédez (un téléphone, une clé physique) et ce que vous êtes (une empreinte, un visage).&lt;/p&gt;

&lt;p&gt;La recommandation de la CNIL du 20 mars 2025 fixe le vocabulaire (&lt;a href="https://cnil.fr/sites/cnil/files/2025-05/recommandation_relative_a_l_authentification_multifacteur.pdf" rel="noopener noreferrer"&gt;CNIL, recommandation relative à l'authentification multifacteur&lt;/a&gt;, section 2.3)&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentification multifacteur (MFA)&lt;/strong&gt;&amp;nbsp;: au moins deux facteurs de catégories distinctes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentification à deux facteurs (2FA)&lt;/strong&gt;&amp;nbsp;: une MFA avec exactement deux facteurs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation en deux étapes (2SV)&lt;/strong&gt;&amp;nbsp;: deux vérifications successives qui peuvent être de la même catégorie. La CNIL donne l'exemple d'un mot de passe suivi d'un code reçu par mail, qui «&amp;nbsp;est une vérification en 2 étapes et non une authentification multifacteur&amp;nbsp;».&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L'ANSSI ajoute une distinction utile&amp;nbsp;: multifacteur ne veut pas dire fort. Un mot de passe et un code reçu par SMS forment une authentification multifacteur, mais aucun des deux n'est «&amp;nbsp;fort&amp;nbsp;» au sens cryptographique (&lt;a href="https://messervices.cyber.gouv.fr/documents-guides/anssi-guide-authentification_multifacteur_et_mots_de_passe.pdf" rel="noopener noreferrer"&gt;ANSSI, guide sur l'authentification multifacteur et les mots de passe&lt;/a&gt;, version 2.0 du 8 octobre 2021, section 2.5). Dans les menus des services, ces termes désignent pourtant le même réglage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les méthodes, de la plus fragile à la plus solide
&lt;/h2&gt;

&lt;p&gt;Trois textes servent de référence&amp;nbsp;: le guide de l'ANSSI, la recommandation de la CNIL et le guide américain NIST SP 800-63B, révision 4, finalisé le 31 juillet 2025 (&lt;a href="https://csrc.nist.gov/pubs/sp/800/63/b/4/final" rel="noopener noreferrer"&gt;NIST&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5lwxjoazbqaxrt8jx8ua.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5lwxjoazbqaxrt8jx8ua.webp" alt="Grille établie selon notre lecture de l'ANSSI, de la CNIL et du NIST&amp;nbsp;: sept variantes de double authentification jugées sur cinq critères, ligne téléphonique, phishing en temps réel, fatigue MFA, facteur reconnu par la CNIL et niveau AAL2 du NIST" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cette grille est notre lecture des trois textes, pas un classement publié par eux. Une marque «&amp;nbsp;partiel&amp;nbsp;» signale une condition, expliquée dans la section de la méthode concernée, et «&amp;nbsp;s. o.&amp;nbsp;» (sans objet) une méthode qui n'envoie aucune demande à approuver.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code par e-mail : le plus faible
&lt;/h3&gt;

&lt;p&gt;Un code envoyé par e-mail ne prouve que l'accès à votre messagerie. La CNIL ne le considère pas comme un facteur de possession, et le NIST, dont le cadre s'impose aux services fédéraux américains, l'exclut de l'authentification hors bande&amp;nbsp;: «&amp;nbsp;l'e-mail ne DOIT PAS être utilisé&amp;nbsp;» (notre traduction), car il peut n'être protégé que par un mot de passe (&lt;a href="https://pages.nist.gov/800-63-4/sp800-63b.html" rel="noopener noreferrer"&gt;NIST SP 800-63B-4&lt;/a&gt;, section 3.1.3.1). À remplacer dès que possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  SMS et appel vocal : à éviter quand c'est possible
&lt;/h3&gt;

&lt;p&gt;L'ANSSI recommande de ne pas utiliser le SMS pour recevoir un facteur d'authentification (recommandation R8, section 2.6). Elle cite l'interception via SS7 et le «&amp;nbsp;SIM swapping&amp;nbsp;», le détournement de votre numéro chez l'opérateur. Dans son cadre, le NIST classe le réseau téléphonique comme un authentifiant «&amp;nbsp;restreint&amp;nbsp;», toléré sous conditions, dont une analyse de risque, une information claire de l'utilisateur, une alternative non restreinte et un plan de migration (sections 3.1.3.3 et 3.2.9), d'où notre marque «&amp;nbsp;partiel&amp;nbsp;».&lt;/p&gt;

&lt;p&gt;La CNIL qualifie l'OTP par SMS de «&amp;nbsp;facteur de possession dont le niveau de confiance reste à évaluer&amp;nbsp;» au cas par cas, d'où le «&amp;nbsp;partiel&amp;nbsp;» de la grille, tout en jugeant qu'un SMS associé à un autre facteur assure «&amp;nbsp;un niveau de sécurité supérieur à l'authentification simple&amp;nbsp;» (recommandation de 2025, section 2.2).&lt;/p&gt;

&lt;h3&gt;
  
  
  Application d'authentification (TOTP) : le bon réglage par défaut
&lt;/h3&gt;

&lt;p&gt;Une application génère sur votre téléphone un code temporaire à partir d'un secret partagé une fois par QR code, sans passer par le réseau téléphonique. L'ANSSI range «&amp;nbsp;les protocoles d'OTP (One-Time Password) comme HOTP, TOTP ou OCRA&amp;nbsp;» parmi les exemples d'authentification forte reposant sur un facteur de possession (section 2.5). Sa limite&amp;nbsp;: le code se recopie à la main, donc un faux site peut vous le demander et le rejouer aussitôt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notification à approuver : pratique, mais à surveiller
&lt;/h3&gt;

&lt;p&gt;Le service envoie une demande sur votre téléphone, vous touchez «&amp;nbsp;Oui&amp;nbsp;». Le NIST juge pourtant que l'approbation simple «&amp;nbsp;n'est plus considérée comme acceptable&amp;nbsp;» (notre traduction) à cause des attaques par fatigue, et exige le transfert d'un secret entre l'écran de connexion et le téléphone, par exemple un nombre à recopier (section 3.1.3). Cette saisie réduit le risque sans le supprimer&amp;nbsp;: la CNIL la range parmi les mesures pour «&amp;nbsp;réduire ce risque d'attaque par MFA fatigue&amp;nbsp;», avec l'affichage du lieu de connexion et la limitation du nombre de demandes (recommandation de 2025, section 3.10, exemple 6), d'où notre marque «&amp;nbsp;partiel&amp;nbsp;».&lt;/p&gt;

&lt;p&gt;Microsoft l'applique à toutes les notifications d'Authenticator sur sa plateforme professionnelle Entra ID (&lt;a href="https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-mfa-number-match" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Clé de sécurité FIDO2 : la plus robuste
&lt;/h3&gt;

&lt;p&gt;Une petite clé physique signe un défi cryptographique lié à l'adresse exacte du site. Un faux site à une autre adresse ne peut rien en tirer. L'ANSSI cite FIDO2 et U2F parmi les authentifications fortes, et le NIST demande aux services de son cadre, ceux des agences fédérales américaines, d'offrir au moins une option résistante au phishing au niveau AAL2 (section 2.2.2). Inconvénient&amp;nbsp;: il faut l'acheter, et prévoir une deuxième clé de secours.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clés d'accès (passkeys) : le futur, déjà là
&lt;/h3&gt;

&lt;p&gt;Les clés d'accès reposent sur le même standard FIDO, stockées dans votre téléphone ou votre gestionnaire de mots de passe et débloquées par empreinte ou code. Le NIST les admet au niveau AAL2 quand elles sont synchronisées (annexe B). La CNIL ne les tient pas pour multifactorielles par défaut&amp;nbsp;: elles «&amp;nbsp;pourraient être multifacteur&amp;nbsp;» si la clé est associée à un terminal spécifique verrouillé par code PIN (section 2.3). Une clé synchronisée sur plusieurs appareils ne remplit généralement pas cette condition, d'où notre «&amp;nbsp;partiel&amp;nbsp;». Notre guide dédié aux &lt;a href="https://neurautech.com/passkeys/" rel="noopener noreferrer"&gt;passkeys&lt;/a&gt; explique comment les créer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Activer la double authentification service par service
&lt;/h2&gt;

&lt;p&gt;Les chemins ci-dessous viennent des pages d'aide officielles lues le 31 octobre 2026. Si un libellé a changé, cherchez «&amp;nbsp;deux étapes&amp;nbsp;» ou «&amp;nbsp;deux facteurs&amp;nbsp;» dans les paramètres du service.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Nom du réglage&lt;/th&gt;
&lt;th&gt;Méthodes listées sur la page d'aide&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google et Gmail&lt;/td&gt;
&lt;td&gt;Validation en deux étapes&lt;/td&gt;
&lt;td&gt;Invites Google, clés d'accès, clés de sécurité, Authenticator, message ou appel, codes de secours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft et Outlook&lt;/td&gt;
&lt;td&gt;Vérification en deux étapes&lt;/td&gt;
&lt;td&gt;Application d'authentification, téléphone, e-mail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple&lt;/td&gt;
&lt;td&gt;Identification à deux facteurs&lt;/td&gt;
&lt;td&gt;Code de validation, numéro de téléphone de confiance, clés de sécurité en option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facebook&lt;/td&gt;
&lt;td&gt;Authentification à deux facteurs&lt;/td&gt;
&lt;td&gt;Clé de sécurité, application tierce, SMS, dix codes de récupération&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instagram&lt;/td&gt;
&lt;td&gt;Authentification à deux facteurs&lt;/td&gt;
&lt;td&gt;Application (recommandée), SMS, WhatsApp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;td&gt;Vérification en deux étapes&lt;/td&gt;
&lt;td&gt;Mot de passe, adresse e-mail, clé d'accès&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orange&lt;/td&gt;
&lt;td&gt;Authentification renforcée&lt;/td&gt;
&lt;td&gt;Notification dans l'application Orange et moi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Google et Gmail
&lt;/h3&gt;

&lt;p&gt;Sur la &lt;a href="https://support.google.com/accounts/answer/185839?hl=fr" rel="noopener noreferrer"&gt;page d'aide de Google&lt;/a&gt;&amp;nbsp;: accédez à votre compte Google, appuyez sur «&amp;nbsp;Sécurité et connexion&amp;nbsp;», puis sous «&amp;nbsp;Comment vous connecter à Google&amp;nbsp;», sélectionnez «&amp;nbsp;Activer la validation en deux étapes&amp;nbsp;» et suivez les instructions. Google recommande ses invites comme deuxième étape si vous ne vous connectez pas avec une clé d'accès.&lt;/p&gt;

&lt;h3&gt;
  
  
  Microsoft et Outlook
&lt;/h3&gt;

&lt;p&gt;Sur la &lt;a href="https://support.microsoft.com/fr-fr/accounts-billing/security/how-to-use-two-step-verification-with-your-microsoft-account" rel="noopener noreferrer"&gt;page d'aide de Microsoft&lt;/a&gt;&amp;nbsp;: connectez-vous à l'onglet Sécurité sur account.microsoft.com/security, choisissez «&amp;nbsp;Gérer la façon dont je me connecte&amp;nbsp;», puis sous «&amp;nbsp;Sécurité supplémentaire&amp;nbsp;» et «&amp;nbsp;Vérification en deux étapes&amp;nbsp;», choisissez «&amp;nbsp;Activer&amp;nbsp;».&lt;/p&gt;

&lt;p&gt;Microsoft prévient que si vous perdez votre méthode de contact, «&amp;nbsp;il peut s'écouler 30&amp;nbsp;jours&amp;nbsp;» avant de récupérer l'accès, et recommande d'enregistrer trois informations de sécurité. La même page annonce que Microsoft va «&amp;nbsp;commencer à supprimer progressivement les SMS&amp;nbsp;» comme méthode d'authentification des comptes personnels, sans donner de date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apple (iPhone, iPad, Mac)
&lt;/h3&gt;

&lt;p&gt;Sur la &lt;a href="https://support.apple.com/fr-fr/102660" rel="noopener noreferrer"&gt;page d'aide d'Apple&lt;/a&gt;&amp;nbsp;: ouvrez Réglages sur iPhone ou iPad, ou Réglages Système sur Mac, sélectionnez votre nom, puis «&amp;nbsp;Connexion et sécurité&amp;nbsp;», et activez l'identification à deux facteurs. Si votre compte a été créé avec cette protection, elle ne peut pas être supprimée. Apple propose aussi des clés de sécurité en option, dans «&amp;nbsp;Identification à deux facteurs&amp;nbsp;», puis «&amp;nbsp;Clés de sécurité&amp;nbsp;», avec «&amp;nbsp;au moins deux clés de sécurité FIDO® Certified&amp;nbsp;» (&lt;a href="https://support.apple.com/fr-fr/102637" rel="noopener noreferrer"&gt;Apple, clés de sécurité&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Facebook et Instagram
&lt;/h3&gt;

&lt;p&gt;Sur la &lt;a href="https://www.facebook.com/help/148233965247823?locale=fr_FR" rel="noopener noreferrer"&gt;page d'aide de Facebook&lt;/a&gt;&amp;nbsp;: cliquez sur votre photo de profil, puis «&amp;nbsp;Paramètres et confidentialité&amp;nbsp;», «&amp;nbsp;Paramètres&amp;nbsp;», «&amp;nbsp;Espace Comptes&amp;nbsp;», «&amp;nbsp;Mot de passe et sécurité&amp;nbsp;», enfin «&amp;nbsp;Authentification à deux facteurs&amp;nbsp;». Facebook propose une clé de sécurité, une application d'authentification tierce ou les SMS, et dix codes de récupération à utiliser sans votre téléphone.&lt;/p&gt;

&lt;p&gt;Sur Instagram, la &lt;a href="https://help.instagram.com/566810106808145?locale=fr_FR" rel="noopener noreferrer"&gt;page d'aide&lt;/a&gt; passe par «&amp;nbsp;Plus&amp;nbsp;», «&amp;nbsp;Paramètres&amp;nbsp;», «&amp;nbsp;En voir plus dans l'Espace Comptes&amp;nbsp;», puis «&amp;nbsp;Mot de passe et sécurité&amp;nbsp;». Instagram recommande l'application d'authentification et précise qu'elle ne s'active que depuis l'application Android ou iPhone.&lt;/p&gt;

&lt;h3&gt;
  
  
  WhatsApp
&lt;/h3&gt;

&lt;p&gt;Sur la &lt;a href="https://faq.whatsapp.com/1920866721452534/?cms_platform=web&amp;amp;locale=fr_FR" rel="noopener noreferrer"&gt;page d'aide de WhatsApp&lt;/a&gt;&amp;nbsp;: sur Android, appuyez sur le menu, puis «&amp;nbsp;Paramètres&amp;nbsp;», «&amp;nbsp;Compte&amp;nbsp;», «&amp;nbsp;Vérification en deux étapes&amp;nbsp;»&amp;nbsp;; sur iPhone, «&amp;nbsp;Vous&amp;nbsp;», «&amp;nbsp;Compte&amp;nbsp;», «&amp;nbsp;Vérification en deux étapes&amp;nbsp;». Activez l'option, créez un mot de passe fort, ajoutez une adresse e-mail. Changement à noter&amp;nbsp;: WhatsApp indique remplacer le code PIN à 6 chiffres par un mot de passe. Sa page &lt;a href="https://faq.whatsapp.com/1278661612895630/?cms_platform=web&amp;amp;locale=fr_FR" rel="noopener noreferrer"&gt;À propos de la vérification en deux étapes&lt;/a&gt; présente aussi la clé d'accès, qui accélère la connexion quand la vérification est activée.&lt;/p&gt;

&lt;p&gt;Ne communiquez jamais à personne, même à un proche qui vous le demande, le code reçu par SMS lors d'une inscription WhatsApp&amp;nbsp;: c'est le ressort des escroqueries décrites dans notre guide sur l'&lt;a href="https://neurautech.com/arnaque-en-ligne/" rel="noopener noreferrer"&gt;arnaque en ligne&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Orange Mail
&lt;/h3&gt;

&lt;p&gt;Orange remplace Mobile Connect, dont l'arrêt progressif est annoncé à partir d'août 2026 (&lt;a href="https://assistance.orange.fr/article/orange-fr-vous-identifier-avec-mobile-connect_799990" rel="noopener noreferrer"&gt;Assistance Orange&lt;/a&gt;), par une «&amp;nbsp;authentification renforcée&amp;nbsp;» via l'application Orange et moi.&lt;/p&gt;

&lt;p&gt;Sur la &lt;a href="https://assistance.orange.fr/article/compte-orange-comment-vous-identifier-avec-orange-et-moi-et-votre-mobile_985652" rel="noopener noreferrer"&gt;page d'aide d'Orange&lt;/a&gt;, mise à jour le 12 octobre 2026&amp;nbsp;: ouvrez Orange et moi, puis «&amp;nbsp;Compte&amp;nbsp;», «&amp;nbsp;Connexion et sécurité&amp;nbsp;», et dans «&amp;nbsp;Vos moyens d'authentification&amp;nbsp;», cliquez sur «&amp;nbsp;Activer&amp;nbsp;». Pour un compte Internet, le mobile doit être sur le wifi de votre Livebox, et Mobile Connect doit être désactivé. Ne désinstallez pas l'application ensuite. Dans Outlook ou Thunderbird, le mot de passe reste demandé&amp;nbsp;: Orange propose un mot de passe dédié.&lt;/p&gt;

&lt;h3&gt;
  
  
  Votre banque : l'authentification forte
&lt;/h3&gt;

&lt;p&gt;Pour la banque, l'authentification forte est imposée par la directive européenne DSP2&amp;nbsp;: pas d'option à cocher, mais vous devez enrôler le moyen proposé par votre banque, le plus souvent son application sur votre téléphone. La Banque de France la définit comme «&amp;nbsp;la confirmation d'au moins deux facteurs d'authentification de catégories différentes&amp;nbsp;» et note qu'un SMS seul ne compte que pour un facteur&amp;nbsp;: il faut l'associer à un mot de passe ou à un autre facteur (&lt;a href="https://www.banque-france.fr/fr/strategie-monetaire/moyens-de-paiement/cnmp/paiements-en-questions" rel="noopener noreferrer"&gt;Banque de France&lt;/a&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Codes de secours : la sauvegarde à préparer avant la panne
&lt;/h2&gt;

&lt;p&gt;Les codes de secours, à usage unique, remplacent votre téléphone si vous le perdez. Générez-les dès l'activation.&lt;/p&gt;

&lt;p&gt;Chez Google, la &lt;a href="https://support.google.com/accounts/answer/1187538?hl=fr" rel="noopener noreferrer"&gt;page sur les codes de secours&lt;/a&gt; indique que chaque série compte 10 codes, que chaque code «&amp;nbsp;devient inactif&amp;nbsp;» une fois utilisé, et qu'en générer une nouvelle série désactive l'ancienne. Google précise qu'il ne vous demandera «&amp;nbsp;jamais de code de secours, sauf lors de la connexion&amp;nbsp;».&lt;/p&gt;

&lt;p&gt;Apple fonctionne autrement&amp;nbsp;: sa «&amp;nbsp;clé de secours&amp;nbsp;» est un code de 28&amp;nbsp;caractères. Apple prévient&amp;nbsp;: «&amp;nbsp;Si vous la perdez, Apple n'est pas en mesure de vous communiquer votre clé de secours.&amp;nbsp;» Et si vous ne pouvez pas la fournir après avoir perdu l'accès à votre compte, «&amp;nbsp;vous ne pourrez plus jamais accéder à votre compte&amp;nbsp;» (&lt;a href="https://support.apple.com/fr-fr/109345" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;). Ne l'activez que si vous êtes certain de la conserver.&lt;/p&gt;

&lt;p&gt;Rangez-les sur papier ou dans un coffre-fort de mots de passe, que l'ANSSI recommande. Notre comparatif de &lt;a href="https://neurautech.com/gestionnaire-de-mot-de-passe-gratuit/" rel="noopener noreferrer"&gt;gestionnaire de mot de passe gratuit&lt;/a&gt; aide à choisir.&lt;/p&gt;




&lt;h2&gt;
  
  
  Téléphone perdu ou volé : que faire
&lt;/h2&gt;

&lt;p&gt;Depuis un autre appareil, commencez par ce qui coupe l'accès au voleur&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Faites suspendre votre ligne&lt;/strong&gt; auprès de votre opérateur, ce qui coupe l'accès aux codes SMS, puis verrouillez ou effacez le téléphone à distance avec le service de localisation de son système.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connectez-vous avec un code de secours&lt;/strong&gt; ou une clé de sécurité de secours, puis retirez le téléphone perdu de la liste des appareils de confiance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Réenregistrez votre application d'authentification&lt;/strong&gt; sur le nouveau téléphone, service par service, ou restaurez sa sauvegarde.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Changez les mots de passe&lt;/strong&gt; des comptes critiques si le téléphone n'était pas verrouillé.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sans code de secours, chaque service a sa procédure de récupération, souvent lente. Microsoft parle de 30&amp;nbsp;jours possibles. Pour éviter ce scénario, enregistrez au moins deux méthodes par compte&amp;nbsp;: une application et une clé, ou une application et des codes imprimés.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fatigue MFA et phishing en temps réel : les attaques qui contournent la 2FA
&lt;/h2&gt;

&lt;p&gt;Deux techniques visent précisément le deuxième facteur.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La fatigue MFA.&lt;/strong&gt; L'attaquant, qui connaît déjà le mot de passe, déclenche des dizaines de demandes jusqu'à ce que la victime en approuve une par lassitude. C'est ce qui est arrivé chez Uber en septembre 2022&amp;nbsp;: selon l'entreprise, l'attaquant a probablement acheté le mot de passe d'un prestataire, a multiplié les tentatives de connexion, et «&amp;nbsp;finalement, le prestataire en a accepté une&amp;nbsp;» (notre traduction) (&lt;a href="https://www.uber.com/newsroom/security-update/" rel="noopener noreferrer"&gt;Uber&lt;/a&gt;, mise à jour du 19 septembre 2022). La parade&amp;nbsp;: refuser toute demande que vous n'avez pas initiée, et préférer les notifications avec nombre à saisir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le phishing en temps réel (adversary-in-the-middle).&lt;/strong&gt; Un faux site sert de relais entre vous et le vrai&amp;nbsp;: vous tapez mot de passe et code, le relais les transmet aussitôt et récupère le cookie de session. Microsoft a décrit en juillet 2022 une campagne de ce type qui a tenté de cibler plus de 10&amp;nbsp;000 organisations depuis septembre 2021, en précisant que ce n'est «&amp;nbsp;pas une vulnérabilité de la MFA&amp;nbsp;» (notre traduction) (&lt;a href="https://www.microsoft.com/en-us/security/blog/2022/07/12/from-cookie-theft-to-bec-attackers-use-aitm-phishing-sites-as-entry-point-to-further-financial-fraud/" rel="noopener noreferrer"&gt;Microsoft Security&lt;/a&gt;, 12 juillet 2022).&lt;/p&gt;

&lt;p&gt;Contre ce relais, SMS, codes TOTP et notifications échouent, car rien ne les lie à l'adresse du site. Le NIST exclut de la résistance au phishing toute méthode où l'on recopie un code, et ne retient que la liaison au canal ou au nom du vérificateur, ce que font FIDO2 et les clés d'accès (section 3.2.5 et annexe B). L'agence américaine CISA juge FIDO/WebAuthn seule méthode largement disponible résistante au phishing, tout en rappelant que «&amp;nbsp;n'importe quelle MFA vaut mieux que pas de MFA&amp;nbsp;» (notre traduction) (&lt;a href="https://www.cisa.gov/MFA" rel="noopener noreferrer"&gt;CISA&lt;/a&gt;, page archivée). Pour repérer les faux sites en amont, relisez nos &lt;a href="https://neurautech.com/phishing-comment-reconnaitre-et-eviter-les-arnaques/" rel="noopener noreferrer"&gt;signes d'un phishing&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quelle application d'authentification choisir
&lt;/h2&gt;

&lt;p&gt;Toutes génèrent les mêmes codes TOTP&amp;nbsp;: la différence se joue sur la sauvegarde et le transfert vers un nouveau téléphone. Le tableau reprend ce qu'affichent les pages officielles le 31 octobre 2026.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Application&lt;/th&gt;
&lt;th&gt;Plateformes&lt;/th&gt;
&lt;th&gt;Code source ouvert&lt;/th&gt;
&lt;th&gt;Sauvegarde ou synchronisation&lt;/th&gt;
&lt;th&gt;Compte requis&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://support.google.com/accounts/answer/1066447?hl=fr" rel="noopener noreferrer"&gt;Google Authenticator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Android, iOS&lt;/td&gt;
&lt;td&gt;Non indiqué&lt;/td&gt;
&lt;td&gt;Synchronisation via le compte Google, chiffrée en transit et au repos&amp;nbsp;; transfert par QR code&lt;/td&gt;
&lt;td&gt;Non, optionnel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://support.microsoft.com/fr-fr/authenticator/back-up-your-accounts-in-microsoft-authenticator" rel="noopener noreferrer"&gt;Microsoft Authenticator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Android, iOS&lt;/td&gt;
&lt;td&gt;Non indiqué&lt;/td&gt;
&lt;td&gt;Compte Microsoft personnel (Android, via Google One à partir de janvier 2027) ou iCloud (iOS)&amp;nbsp;; pas de restauration d'iOS vers Android&lt;/td&gt;
&lt;td&gt;Pour la sauvegarde&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/beemdevelopment/Aegis" rel="noopener noreferrer"&gt;Aegis&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Android uniquement&lt;/td&gt;
&lt;td&gt;Oui, GPL-3.0&lt;/td&gt;
&lt;td&gt;Coffre chiffré AES-256-GCM, sauvegardes automatiques, export chiffré&lt;/td&gt;
&lt;td&gt;Non&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://2fas.com/auth/" rel="noopener noreferrer"&gt;2FAS Auth&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mobile et extension navigateur&lt;/td&gt;
&lt;td&gt;Oui&lt;/td&gt;
&lt;td&gt;iCloud, Google Drive ou fichier d'export&lt;/td&gt;
&lt;td&gt;Non&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://bitwarden.com/products/authenticator/" rel="noopener noreferrer"&gt;Bitwarden Authenticator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Android, iOS&lt;/td&gt;
&lt;td&gt;Oui&lt;/td&gt;
&lt;td&gt;Sauvegarde du système du téléphone&amp;nbsp;; codes aussi accessibles depuis Bitwarden Password Manager&lt;/td&gt;
&lt;td&gt;Non&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://proton.me/authenticator" rel="noopener noreferrer"&gt;Proton Authenticator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Android, iOS, Mac, Windows, Linux&lt;/td&gt;
&lt;td&gt;Oui&lt;/td&gt;
&lt;td&gt;Synchronisation chiffrée de bout en bout&lt;/td&gt;
&lt;td&gt;Optionnel, requis pour synchroniser&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Aegis, 2FAS, Bitwarden Authenticator et Proton Authenticator se disent gratuits. En résumé&amp;nbsp;: Aegis pour tout garder en local sur Android, Proton Authenticator pour une synchronisation chiffrée de bout en bout hors de Google et Microsoft, 2FAS pour une sauvegarde sur iCloud ou Google Drive sans créer de compte, et l'application de votre écosystème sinon. Vérifiez la sauvegarde avant de changer de téléphone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les procédures de récupération sans code de secours&lt;/strong&gt; de Google, Apple, Meta et Orange&amp;nbsp;: nous n'avons pas lu leurs pages dédiées, et leurs délais varient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La date de fin des SMS chez Microsoft&lt;/strong&gt;&amp;nbsp;: la page annonce une suppression progressive, sans calendrier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le prix des clés de sécurité&lt;/strong&gt;&amp;nbsp;: nous n'avons relevé aucun tarif, d'où l'absence de chiffres.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La licence et le prix exacts de Google Authenticator et Microsoft Authenticator&lt;/strong&gt;&amp;nbsp;: les pages lues ne les mentionnent pas.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  La double authentification par SMS est-elle encore utile ?
&lt;/h3&gt;

&lt;p&gt;Oui, si le service ne propose rien d'autre. La CNIL juge qu'un SMS associé à un mot de passe reste plus sûr que le mot de passe seul. Mais l'&lt;a href="https://messervices.cyber.gouv.fr/documents-guides/anssi-guide-authentification_multifacteur_et_mots_de_passe.pdf" rel="noopener noreferrer"&gt;ANSSI&lt;/a&gt; recommande de ne pas utiliser le SMS à cause du SIM swap et de l'interception. Passez à une application d'authentification dès que possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment activer la double authentification sur Gmail ?
&lt;/h3&gt;

&lt;p&gt;Gmail utilise la validation en deux étapes du compte Google. Selon la &lt;a href="https://support.google.com/accounts/answer/185839?hl=fr" rel="noopener noreferrer"&gt;page d'aide de Google&lt;/a&gt;&amp;nbsp;: compte Google, «&amp;nbsp;Sécurité et connexion&amp;nbsp;», puis «&amp;nbsp;Activer la validation en deux étapes&amp;nbsp;» sous «&amp;nbsp;Comment vous connecter à Google&amp;nbsp;». Générez ensuite vos 10 codes de secours.&lt;/p&gt;

&lt;h3&gt;
  
  
  Que se passe-t-il si je change de téléphone ?
&lt;/h3&gt;

&lt;p&gt;Rien de grave si votre application est sauvegardée&amp;nbsp;: restaurez-la sur le nouveau téléphone, en vérifiant les limites indiquées dans le tableau des applications plus haut. Sinon, utilisez vos codes de secours pour réenregistrer chaque compte.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les clés d'accès remplacent-elles la double authentification ?
&lt;/h3&gt;

&lt;p&gt;Sur les services qui les proposent, elles remplacent mot de passe et code par un déverrouillage de l'appareil. Le &lt;a href="https://pages.nist.gov/800-63-4/sp800-63b.html" rel="noopener noreferrer"&gt;NIST&lt;/a&gt; les admet au niveau AAL2 et les juge résistantes au phishing, mais la CNIL ne les tient pas d'office pour multifacteur. Gardez la 2FA active sur les comptes qui ne les proposent pas encore.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse fondée exclusivement sur des sources publiques lues le 31 octobre 2026&amp;nbsp;: guide de l'ANSSI, recommandation et fiche de la CNIL, publications de Cybermalveillance.gouv.fr, NIST SP 800-63B-4, Banque de France, pages d'aide officielles de Google, Microsoft, Apple, Meta, WhatsApp et Orange, pages des éditeurs d'applications. Les citations issues de sources en anglais sont traduites par nos soins. Aucun test n'a été mené, aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux éditeurs mentionnés.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou un menu qui a changé&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 31 octobre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Sasun Bughdaryan (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>doubleauthentification</category>
      <category>2fa</category>
      <category>cybersecurite</category>
      <category>motsdepasse</category>
    </item>
    <item>
      <title>CV développeur junior : structure, exemple complet et checklist (2026)</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:44:37 +0000</pubDate>
      <link>https://dev.to/neurau_tech/cv-developpeur-junior-structure-exemple-complet-et-checklist-2026-1568</link>
      <guid>https://dev.to/neurau_tech/cv-developpeur-junior-structure-exemple-complet-et-checklist-2026-1568</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Un CV développeur junior tient sur une page et doit prouver ce que l'expérience ne prouve pas encore&amp;nbsp;: que vous savez livrer du code qui fonctionne. Quand l'expérience est mince, les projets passent avant elle, chacun avec un lien vers le code et, si possible, une démo.&lt;/p&gt;

&lt;p&gt;Ce guide vise les jeunes diplômés et les personnes en reconversion qui cherchent un premier poste en France. Il repose sur des sources publiques consultées le 31 octobre 2026&amp;nbsp;; nous n'avons mené aucun test auprès de recruteurs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Une page, un titre de poste précis, un résumé court, puis les projets avant une expérience sans rapport avec le développement.&lt;/li&gt;
&lt;li&gt;Chaque projet se décrit par ce qu'il fait, la pile utilisée, votre rôle et une preuve vérifiable&amp;nbsp;: dépôt public, démo, tests, README.&lt;/li&gt;
&lt;li&gt;La documentation de Greenhouse, seul éditeur d'ATS que nous avons lu, décrit un rejet automatique fondé sur les réponses au formulaire, pas sur le contenu du CV.&lt;/li&gt;
&lt;li&gt;En France, la photo, l'âge et la situation de famille sont facultatifs&amp;nbsp;: le Code du travail limite les questions à ce qui a un lien direct et nécessaire avec le poste.&lt;/li&gt;
&lt;li&gt;Répondez avec soin aux questions du formulaire (expérience, lieu, visa)&amp;nbsp;: chez Greenhouse, ce sont elles qui peuvent déclencher un rejet automatique.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Ce que disent les études sur le tri des candidatures
&lt;/h2&gt;

&lt;p&gt;Les données fiables sur la lecture des CV sont rares. Voici celles que nous avons pu lire, avec leur périmètre.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le marché junior reste difficile.&lt;/strong&gt; Le rapport 2025 de HackerRank, éditeur de tests techniques, écrit que les développeurs en début de carrière «&amp;nbsp;ont été les plus touchés&amp;nbsp;» (notre traduction) et que 74&amp;nbsp;% des développeurs interrogés peinent encore à décrocher un poste. L'enquête couvre 13&amp;nbsp;732 répondants dans 102 pays (&lt;a href="https://www.hackerrank.com/reports/developer-skills-report-2025" rel="noopener noreferrer"&gt;HackerRank, Developer Skills Report 2025&lt;/a&gt;, consulté le 31 octobre 2026). C'est une étude mondiale d'un éditeur, pas une mesure du marché français.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Les entreprises françaises testent davantage.&lt;/strong&gt; Le baromètre 2026 de l'Apec porte sur 1&amp;nbsp;150 entreprises de 10 salariés et plus ayant recruté au moins un cadre en 2025. Il relève que 48&amp;nbsp;% ont utilisé des tests de recrutement et 29&amp;nbsp;% des mises en situation, alors que la lettre de motivation n'est plus demandée que par la moitié d'entre elles, contre près de 70&amp;nbsp;% jusqu'en 2021 (&lt;a href="https://www.banquedesterritoires.fr/sites/default/files/2026-05/210526%20-%20Etude%20Apec%20-%20Pratiques%20de%20recrutement%202026.pdf" rel="noopener noreferrer"&gt;Apec, Pratiques de recrutement de cadres 2026&lt;/a&gt;, mai 2026, PDF hébergé par la Banque des Territoires). L'étude porte sur les cadres, pas spécifiquement sur les développeurs&amp;nbsp;: vos projets doivent donc résister à un test technique.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fidc9vae90zc8tqhzabpe.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fidc9vae90zc8tqhzabpe.webp" alt="Graphique en sucettes&amp;nbsp;: pratiques des entreprises qui recrutent des cadres selon l'Apec 2026, utiles pour préparer un CV développeur junior&amp;nbsp;: offre d'emploi 84&amp;nbsp;%, réseaux sociaux 73&amp;nbsp;%, présélection téléphonique 71&amp;nbsp;%, lettre de motivation 50&amp;nbsp;%, tests 48&amp;nbsp;%, mises en situation 29&amp;nbsp;%, outils d'IA 8&amp;nbsp;%" width="799" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Les fameuses «&amp;nbsp;6&amp;nbsp;secondes&amp;nbsp;».&lt;/strong&gt; Ce chiffre vient d'une étude d'oculométrie de Ladders, un site d'emploi américain. Selon HR Dive, son édition 2018 mesure un premier survol de 7,4&amp;nbsp;secondes en moyenne, contre 6 en 2012, et les CV aérés, aux rubriques clairement titrées, y retiennent mieux l'attention que les pages chargées ou en colonnes (&lt;a href="https://www.hrdive.com/news/eye-tracking-study-shows-recruiters-look-at-resumes-for-7-seconds/541582/" rel="noopener noreferrer"&gt;HR Dive&lt;/a&gt;, 8 novembre 2018). Étude d'éditeur, ancienne et américaine&amp;nbsp;: retenez la leçon de mise en page, pas le chronomètre.&lt;/p&gt;




&lt;h2&gt;
  
  
  La structure d'un CV développeur junior en une page
&lt;/h2&gt;

&lt;p&gt;La bonne structure met en haut ce qui prouve le mieux votre niveau. Pour un profil sans expérience de développement, ce sont les projets. OpenClassrooms, dans son cours sur le premier emploi de développeur, recommande une page pour un profil junior en France et une mise en page sobre (&lt;a href="https://openclassrooms.com/fr/courses/8033446-trouvez-votre-premier-emploi-en-tant-que-developpeur/8055653-preparez-votre-cv" rel="noopener noreferrer"&gt;OpenClassrooms, Préparez votre CV&lt;/a&gt;, consulté le 31 octobre 2026).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rubrique&lt;/th&gt;
&lt;th&gt;Contenu&lt;/th&gt;
&lt;th&gt;Profil étudiant&lt;/th&gt;
&lt;th&gt;Profil en reconversion&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;En-tête&lt;/td&gt;
&lt;td&gt;Nom, ville, téléphone, e-mail, liens GitHub et LinkedIn&lt;/td&gt;
&lt;td&gt;En haut&lt;/td&gt;
&lt;td&gt;En haut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Titre&lt;/td&gt;
&lt;td&gt;Le poste visé, avec la pile principale&lt;/td&gt;
&lt;td&gt;Juste sous le nom&lt;/td&gt;
&lt;td&gt;Juste sous le nom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Résumé&lt;/td&gt;
&lt;td&gt;Deux ou trois lignes&amp;nbsp;: pile, type de projets, poste recherché&lt;/td&gt;
&lt;td&gt;Oui&lt;/td&gt;
&lt;td&gt;Oui, avec l'ancien métier en une phrase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compétences&lt;/td&gt;
&lt;td&gt;Groupées par catégorie, sans barres de niveau&lt;/td&gt;
&lt;td&gt;Avant les projets&lt;/td&gt;
&lt;td&gt;Avant les projets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Projets&lt;/td&gt;
&lt;td&gt;Deux à quatre, chacun avec lien et preuve&lt;/td&gt;
&lt;td&gt;Avant la formation&lt;/td&gt;
&lt;td&gt;Avant l'expérience&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expérience&lt;/td&gt;
&lt;td&gt;Stages, alternance, puis emplois sans rapport en une ligne&lt;/td&gt;
&lt;td&gt;Après les projets&lt;/td&gt;
&lt;td&gt;Après les projets, condensée&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formation&lt;/td&gt;
&lt;td&gt;Diplôme ou formation, établissement, année&lt;/td&gt;
&lt;td&gt;Peut remonter si le diplôme est récent et ciblé&lt;/td&gt;
&lt;td&gt;En bas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Langues&lt;/td&gt;
&lt;td&gt;Niveau sur l'échelle européenne (A1 à C2)&lt;/td&gt;
&lt;td&gt;En bas&lt;/td&gt;
&lt;td&gt;En bas&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Quelques règles pour chaque bloc&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L'en-tête.&lt;/strong&gt; La ville suffit, sans adresse complète, avec des liens courts et cliquables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le titre.&lt;/strong&gt; «&amp;nbsp;Développeur web junior, React et Node.js&amp;nbsp;» dit plus que «&amp;nbsp;Passionné d'informatique&amp;nbsp;». Il reprend, quand c'est vrai, l'intitulé de l'offre.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le résumé.&lt;/strong&gt; Ce que vous savez faire, ce que vous avez construit, ce que vous cherchez.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La formation.&lt;/strong&gt; Une formation courte se présente comme un diplôme&amp;nbsp;: intitulé, organisme, année.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Décrire un projet : des preuves plutôt que des adjectifs
&lt;/h2&gt;

&lt;p&gt;Un projet convainc quand un recruteur peut le vérifier en deux clics. Le rapport de HackerRank recommande d'ailleurs aux employeurs d'évaluer les candidats sur des projets réels, qui montrent comment ils résolvent des problèmes en pratique (&lt;a href="https://www.hackerrank.com/reports/developer-skills-report-2025" rel="noopener noreferrer"&gt;HackerRank&lt;/a&gt;, 2025).&lt;/p&gt;

&lt;p&gt;Chaque projet tient en quatre lignes&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ce qu'il fait,&lt;/strong&gt; en une phrase compréhensible par un non-développeur.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La pile,&lt;/strong&gt; sans tout lister&amp;nbsp;: langage, framework, base de données, hébergement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Votre rôle,&lt;/strong&gt; surtout si le projet était collectif&amp;nbsp;: ce que vous avez écrit vous-même.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La preuve&amp;nbsp;:&lt;/strong&gt; lien vers le dépôt et la démo, plus un détail vérifiable (tests automatisés, intégration continue, documentation d'API).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Un chiffre n'a de valeur que s'il est vrai. «&amp;nbsp;40 tests unitaires, exécutés à chaque push par GitHub Actions&amp;nbsp;» se vérifie dans le dépôt&amp;nbsp;; un pourcentage d'amélioration sans mesure se démonte en entretien. Si vous n'avez rien mesuré, décrivez ce que vous avez fait. Sur un même projet fictif, la différence donne ceci&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version faible&lt;/th&gt;
&lt;th&gt;Version vérifiable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Application de gestion de tâches en React&lt;/td&gt;
&lt;td&gt;Application de gestion de tâches pour une association de quartier, utilisée par ses bénévoles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilisation de nombreuses technologies modernes&lt;/td&gt;
&lt;td&gt;React, API REST Node.js et Express, PostgreSQL, déployée sur un VPS avec Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Travail en équipe&lt;/td&gt;
&lt;td&gt;Projet à trois&amp;nbsp;: j'ai écrit l'API et l'authentification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code de qualité&lt;/td&gt;
&lt;td&gt;40 tests unitaires lancés à chaque push, README, lien vers la démo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Si vous manquez de projets, trois sujets classiques&amp;nbsp;: une &lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;API REST avec Node.js et Express&lt;/a&gt;, une application &lt;a href="https://neurautech.com/docker-pour-debutants/" rel="noopener noreferrer"&gt;conteneurisée avec Docker&lt;/a&gt;, et une base de données bien conçue, que notre &lt;a href="https://neurautech.com/sql-debutant-cours-exercices/" rel="noopener noreferrer"&gt;cours de SQL pour débutants&lt;/a&gt; aide à construire. Pour présenter ces projets en ligne, notre guide pour &lt;a href="https://neurautech.com/portfolio-developpeur-comment-le-construire-2026/" rel="noopener noreferrer"&gt;construire un portfolio de développeur&lt;/a&gt; détaille la démarche.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les compétences sans bourrage de mots-clés
&lt;/h2&gt;

&lt;p&gt;La rubrique compétences dit ce que vous savez utiliser, pas tous les mots-clés possibles. Règle simple&amp;nbsp;: n'écrivez que ce dont vous pouvez parler vingt minutes en entretien technique.&lt;/p&gt;

&lt;p&gt;Groupez par catégorie (langages, frameworks, données, outils, méthodes) plutôt qu'en liste unique, comme dans l'exemple plus bas.&lt;/p&gt;

&lt;p&gt;Trois pièges reviennent dans les CV de débutants&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les barres de niveau.&lt;/strong&gt; Une barre remplie à 80&amp;nbsp;% ne dit pas ce que vous savez faire. Le niveau se lit dans les projets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La liste exhaustive.&lt;/strong&gt; Vingt technologies sur une ligne font penser à un survol. Mieux vaut six outils prouvés par des projets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le vocabulaire décalé.&lt;/strong&gt; Si l'offre écrit «&amp;nbsp;Node.js&amp;nbsp;» et votre CV «&amp;nbsp;NodeJS&amp;nbsp;», harmonisez sur le mot de l'offre, à condition d'avoir la compétence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour choisir les langages à mettre en avant, notre analyse des &lt;a href="https://neurautech.com/langages-de-programmation-les-mieux-payes-en-2026/" rel="noopener noreferrer"&gt;langages de programmation les mieux payés&lt;/a&gt; montre ce que disent vraiment les enquêtes. Si votre base est encore fragile, notre plan pour &lt;a href="https://neurautech.com/apprendre-javascript/" rel="noopener noreferrer"&gt;apprendre JavaScript&lt;/a&gt; et nos &lt;a href="https://neurautech.com/exercices-python-debutant-corriges/" rel="noopener noreferrer"&gt;exercices Python pour débutants&lt;/a&gt; donnent de quoi alimenter la rubrique projets.&lt;/p&gt;




&lt;h2&gt;
  
  
  ATS : ce qui est documenté et ce qui relève du mythe
&lt;/h2&gt;

&lt;p&gt;Un ATS (applicant tracking system, logiciel de suivi des candidatures) range les candidatures et peut appliquer des règles automatiques. Ce qu'il fait dépend de l'éditeur et du réglage choisi par l'entreprise. Voici ce que nous avons pu vérifier.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Affirmation courante&lt;/th&gt;
&lt;th&gt;Ce que disent les sources&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;«&amp;nbsp;Les ATS ne lisent pas les PDF&amp;nbsp;»&lt;/td&gt;
&lt;td&gt;Greenhouse accepte les CV en .doc, .docx, .pdf, .rtf et .txt, jusqu'à 100&amp;nbsp;Mo (&lt;a href="https://support.greenhouse.io/hc/en-us/articles/360052218132-Supported-formats-for-resumes-cover-letters-and-other-candidate-uploads" rel="noopener noreferrer"&gt;Greenhouse&lt;/a&gt;, page mise à jour le 19 août 2026)&lt;/td&gt;
&lt;td&gt;Faux pour cet éditeur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;«&amp;nbsp;Le robot rejette les CV sans les bons mots-clés&amp;nbsp;»&lt;/td&gt;
&lt;td&gt;Chez Greenhouse, le rejet automatique «&amp;nbsp;utilise des questions de candidature personnalisées&amp;nbsp;» (notre traduction) et n'existe que dans les offres Plus et Pro (&lt;a href="https://support.greenhouse.io/hc/en-us/articles/203105595-Application-rules-overview" rel="noopener noreferrer"&gt;Greenhouse&lt;/a&gt;, mars 2026)&lt;/td&gt;
&lt;td&gt;Non documenté pour le contenu du CV chez cet éditeur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;«&amp;nbsp;Les entreprises trient par logiciel&amp;nbsp;»&lt;/td&gt;
&lt;td&gt;Plus de 90&amp;nbsp;% des employeurs interrogés qui utilisent un tel système s'en servent pour un premier tri ou un classement, selon une enquête menée aux États-Unis, au Royaume-Uni et en Allemagne (&lt;a href="https://www.library.hbs.edu/working-knowledge/how-to-tap-the-talent-automated-hr-platforms-miss" rel="noopener noreferrer"&gt;Harvard Business School&lt;/a&gt;, 2021)&lt;/td&gt;
&lt;td&gt;Vrai dans ce périmètre, pas mesuré en France&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;«&amp;nbsp;L'IA trie déjà tous les CV&amp;nbsp;»&lt;/td&gt;
&lt;td&gt;En France, 8&amp;nbsp;% des entreprises interrogées utilisent des outils d'IA pour recruter des cadres, surtout pour rédiger les offres (&lt;a href="https://www.banquedesterritoires.fr/sites/default/files/2026-05/210526%20-%20Etude%20Apec%20-%20Pratiques%20de%20recrutement%202026.pdf" rel="noopener noreferrer"&gt;Apec&lt;/a&gt;, 2026)&lt;/td&gt;
&lt;td&gt;Minoritaire en 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;«&amp;nbsp;75&amp;nbsp;% des CV sont rejetés par les ATS&amp;nbsp;»&lt;/td&gt;
&lt;td&gt;Aucune source primaire trouvée&lt;/td&gt;
&lt;td&gt;À ne pas citer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Le point important pour un junior&amp;nbsp;: les exemples de Greenhouse portent sur des questions du formulaire (années d'expérience, lieu de résidence, besoin de visa), qui peuvent déclencher un rejet automatique si l'entreprise l'a configuré. Répondez-y avec soin, et honnêtement.&lt;/p&gt;

&lt;p&gt;L'étude de Harvard Business School décrit aussi des filtres «&amp;nbsp;négatifs&amp;nbsp;», comme exclure les candidats sans diplôme ou avec un trou dans le parcours. Pour une reconversion, une ligne qui explique une période de formation vaut donc mieux qu'un vide.&lt;/p&gt;

&lt;p&gt;Notre conseil, qui n'est pas une règle d'éditeur&amp;nbsp;: un PDF dont le texte se sélectionne, des titres de rubriques standards («&amp;nbsp;Expérience&amp;nbsp;», «&amp;nbsp;Projets&amp;nbsp;», «&amp;nbsp;Formation&amp;nbsp;»), une seule colonne et des coordonnées dans le corps de la page. Ces choix facilitent aussi la lecture humaine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Photo, âge, CV anonyme, RGPD : les règles françaises
&lt;/h2&gt;

&lt;p&gt;En France, le droit limite ce qu'un employeur peut demander. France Travail rappelle que, selon l'article L.1221-6 du Code du travail, les informations demandées au candidat doivent avoir un lien direct et nécessaire avec le poste ou l'évaluation des aptitudes professionnelles (&lt;a href="https://www.francetravail.fr/employeur/vos-recrutements/les-offres-demploi-et-le-droi-1/recruter-sans-discriminer.html" rel="noopener noreferrer"&gt;France Travail&lt;/a&gt;, consulté le 31 octobre 2026).&lt;/p&gt;

&lt;p&gt;La même page liste les critères interdits par l'article L.1132-1, dont l'âge, la situation de famille, l'apparence physique, le nom, le lieu de résidence et l'état de santé. Selon le Code pénal, qu'elle cite, la discrimination est punie de trois ans d'emprisonnement et de 45&amp;nbsp;000&amp;nbsp;€ d'amende.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La photo.&lt;/strong&gt; Elle n'est pas obligatoire. Le guide du Défenseur des droits écrit qu'exiger une photo sur un CV «&amp;nbsp;ne peut être qu'exceptionnel&amp;nbsp;», et conseille aux recruteurs de masquer nom, adresse, âge, situation de famille et photo lors de la sélection (&lt;a href="https://www.defenseurdesdroits.fr/sites/default/files/2023-03/DDD_guide_recrutement-sans-discrimination_20190703.pdf" rel="noopener noreferrer"&gt;Défenseur des droits&lt;/a&gt;, guide de 2019). Vous pouvez l'omettre.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L'âge et la situation de famille.&lt;/strong&gt; Selon le 18e baromètre du Défenseur des droits et de l'Organisation internationale du travail, 14&amp;nbsp;% des actifs de 18 à 64&amp;nbsp;ans déclarent avoir subi une discrimination lors d'une recherche d'emploi au cours des cinq dernières années. L'âge est le premier motif cité, par 42&amp;nbsp;% d'entre eux (&lt;a href="https://www.defenseurdesdroits.fr/sites/default/files/2025-12/ddd_EAD-2024_OIT-18_DP_20251204.pdf" rel="noopener noreferrer"&gt;Défenseur des droits et OIT&lt;/a&gt;, dossier de presse du 4 décembre 2025).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp5jje4465bv44bn31c97.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp5jje4465bv44bn31c97.webp" alt="Graphique en barres&amp;nbsp;: motifs déclarés par les personnes discriminées lors d'une recherche d'emploi selon le Défenseur des droits et l'OIT, âge 42&amp;nbsp;%, origine ou couleur de peau 21&amp;nbsp;%, situation de famille 16&amp;nbsp;%, grossesse ou maternité 15&amp;nbsp;%, état de santé ou handicap 11&amp;nbsp;%" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Parmi les personnes discriminées lors d'une recherche d'emploi, 21&amp;nbsp;% citent l'origine ou la couleur de peau, 16&amp;nbsp;% la situation de famille, 15&amp;nbsp;% la grossesse ou la maternité et 11&amp;nbsp;% l'état de santé ou le handicap. Le baromètre ajoute que 80&amp;nbsp;% d'entre elles déclarent avoir reçu des questions sur des critères prohibés par la loi, et que 4&amp;nbsp;% seulement ont ensuite contacté une association, le Défenseur des droits ou un avocat, porté plainte ou saisi la justice. Le Défenseur des droits peut être saisi depuis &lt;a href="https://www.defenseurdesdroits.fr/" rel="noopener noreferrer"&gt;son site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le CV anonyme.&lt;/strong&gt; Il est facultatif. La loi du 31 mars 2006 l'avait rendu obligatoire dans les entreprises de cinquante salariés et plus, mais aucun décret ne l'a appliqué, malgré une injonction du &lt;a href="https://www.conseil-etat.fr/actualites/cv-anonyme2" rel="noopener noreferrer"&gt;Conseil d'État&lt;/a&gt;. La loi du 17 août 2015 l'a rendu facultatif, comme le rappelle une question écrite de 2026. Le 29 septembre 2026, le gouvernement a répondu qu'il «&amp;nbsp;n'envisage pas de rétablir une obligation de recours au CV anonyme&amp;nbsp;» (&lt;a href="https://questions.assemblee-nationale.fr/q17/17-15001QE.htm" rel="noopener noreferrer"&gt;Assemblée nationale, question écrite n° 15001&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Le RGPD.&lt;/strong&gt; Selon la CNIL, «&amp;nbsp;à la condition de vous en informer et d'avoir obtenu votre accord&amp;nbsp;», un recruteur peut conserver votre dossier après un refus, «&amp;nbsp;2&amp;nbsp;ans après votre dernier contact avec le recruteur ou plus longtemps si vous avez donné votre accord formel&amp;nbsp;» (&lt;a href="https://www.cnil.fr/fr/cnil-direct/question/recrutement-un-employeur-peut-il-conserver-mon-dossier" rel="noopener noreferrer"&gt;CNIL&lt;/a&gt;, consulté le 31 octobre 2026). Vous pouvez obtenir une copie de vos données sans justification, et le recruteur doit vous informer d'un éventuel tri automatisé (&lt;a href="https://www.cnil.fr/fr/recrutement-et-donnees-personnelles-dans-les-tpepme-cinq-questions-incontournables-se-poser" rel="noopener noreferrer"&gt;CNIL&lt;/a&gt;, 30 janvier 2023). La même page interdit de demander le numéro de sécurité sociale&amp;nbsp;: ne l'écrivez pas.&lt;/p&gt;




&lt;h2&gt;
  
  
  Le CV en anglais pour un poste international
&lt;/h2&gt;

&lt;p&gt;Pour une équipe qui travaille en anglais, traduisez le CV entier plutôt que d'ajouter «&amp;nbsp;English&amp;nbsp;: fluent&amp;nbsp;». Les usages varient selon les pays&amp;nbsp;: OpenClassrooms conseille de se renseigner sur ceux du pays visé.&lt;/p&gt;

&lt;p&gt;Les sources ne s'accordent pas sur la photo. Europass, le format de CV de l'Union européenne, conseille d'ajouter une photo professionnelle, propose des CV en 31 langues et recommande l'ordre antichronologique (&lt;a href="https://europass.europa.eu/en/create-europass-cv" rel="noopener noreferrer"&gt;Europass&lt;/a&gt;, consulté le 31 octobre 2026). Pour un poste en France, la règle reste celle de la section précédente&amp;nbsp;: la photo est facultative.&lt;/p&gt;

&lt;p&gt;Quelques ajustements à faire&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Le titre&amp;nbsp;:&lt;/strong&gt; «&amp;nbsp;Junior Software Developer&amp;nbsp;» ou «&amp;nbsp;Junior Full-Stack Developer&amp;nbsp;», selon l'intitulé de l'offre.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les diplômes&amp;nbsp;:&lt;/strong&gt; «&amp;nbsp;Master's degree in Computer Science&amp;nbsp;» plutôt que «&amp;nbsp;Bac+5&amp;nbsp;», inconnu hors de France.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les langues&amp;nbsp;:&lt;/strong&gt; donnez votre niveau sur l'échelle européenne (B2, C1) ou un score de test, pas un adjectif.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour fixer vos prétentions, que l'offre soit en France ou à l'étranger, notre &lt;a href="https://neurautech.com/salaires-developpeurs-france-2026-grille-complete/" rel="noopener noreferrer"&gt;comparatif des salaires des développeurs en France&lt;/a&gt; rassemble six études. Si vous visez des missions plutôt qu'un emploi salarié, notre guide pour &lt;a href="https://neurautech.com/devenir-developpeur-freelance-en-2026-guide-complet/" rel="noopener noreferrer"&gt;devenir développeur freelance&lt;/a&gt; détaille le statut et la recherche des premiers clients.&lt;/p&gt;




&lt;h2&gt;
  
  
  Aligner LinkedIn et GitHub sur le CV
&lt;/h2&gt;

&lt;p&gt;Le CV, LinkedIn et GitHub doivent raconter la même histoire. Selon l'Apec, 73&amp;nbsp;% des entreprises interrogées ont utilisé les réseaux sociaux pour au moins un recrutement de cadre en 2025 (&lt;a href="https://www.banquedesterritoires.fr/sites/default/files/2026-05/210526%20-%20Etude%20Apec%20-%20Pratiques%20de%20recrutement%202026.pdf" rel="noopener noreferrer"&gt;Apec&lt;/a&gt;, 2026).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sur LinkedIn,&lt;/strong&gt; reprenez le titre et les dates du CV. La fonction «&amp;nbsp;Open to Work&amp;nbsp;» se montre soit à tous les membres, avec le cadre #OpenToWork sur la photo, soit aux seuls utilisateurs de LinkedIn Recruiter&amp;nbsp;; LinkedIn précise alors ne pas pouvoir garantir une confidentialité totale vis-à-vis de votre employeur actuel (&lt;a href="https://www.linkedin.com/help/linkedin/answer/a507508/let-recruiters-know-you-re-open-to-work?lang=en" rel="noopener noreferrer"&gt;aide LinkedIn&lt;/a&gt;, consultée le 31 octobre 2026).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sur GitHub,&lt;/strong&gt; vous pouvez épingler jusqu'à six dépôts et gists sur votre profil&amp;nbsp;: choisissez ceux cités dans le CV (&lt;a href="https://docs.github.com/en/account-and-profile/how-tos/profile-customization/pinning-items-to-your-profile" rel="noopener noreferrer"&gt;documentation GitHub&lt;/a&gt;). Et un dépôt public portant votre nom d'utilisateur, avec un README.md à la racine, s'affiche en tête de profil (&lt;a href="https://docs.github.com/en/account-and-profile/how-tos/profile-customization/managing-your-profile-readme" rel="noopener noreferrer"&gt;documentation GitHub&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Vérifiez aussi que chaque dépôt cité a un README et ne contient aucune clé d'API en clair.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exemple complet de CV développeur junior (fictif)
&lt;/h2&gt;

&lt;p&gt;Ce CV a été écrit pour cet article&amp;nbsp;: la personne, les liens et les détails sont inventés. Copiez la méthode, pas les éléments. Le profil est celui d'une reconversion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CAMILLE MARTIN (profil fictif)
Lyon · 06 00 00 00 00 · camille@example.com
github.com/votre-pseudo · linkedin.com/in/votre-pseudo

DÉVELOPPEUSE WEB JUNIOR, JAVASCRIPT ET NODE.JS

Ancienne responsable logistique reconvertie au développement web.
Je conçois des applications complètes en React et Node.js, testées et
déployées. Je cherche un premier poste de développeuse full stack en CDI.

COMPÉTENCES
Langages : JavaScript, SQL
Front-end : React
Back-end : Node.js, Express, API REST, PostgreSQL
Outils : Git, GitHub Actions, Docker
Méthodes : tests unitaires, revue de code par pull requests

PROJETS
Suivi de stock pour une épicerie solidaire (projet personnel, 2026)
Application web qui remplace le tableur de l'association.
React, API Express, PostgreSQL, déployée avec Docker sur un VPS.
Seule développeuse ; 40 tests unitaires lancés à chaque push par GitHub Actions.
Code : github.com/votre-pseudo/stock · Démo : stock.example.org

API de réservation de salles (projet de formation, 2026)
API REST documentée, authentification par jeton, rôles utilisateur.
Node.js, Express, PostgreSQL. Projet à trois, revues de code par
pull requests : j'ai écrit les routes de réservation et leurs tests.
Code : github.com/votre-pseudo/salles

EXPÉRIENCE
Responsable logistique, entrepôt de distribution, Lyon (2019-2025)
Gestion des stocks et d'une équipe de six personnes ; à l'origine du
besoin traité dans le projet de suivi de stock.

FORMATION
Titre professionnel Développeur web et web mobile (niveau 5), 2026
Organisme de formation fictif, Lyon

LANGUES
Français : langue maternelle · Anglais : B2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pourquoi cet ordre&amp;nbsp;: les projets prouvent le niveau technique, donc ils passent avant l'expérience, et l'ancien métier tient en quelques lignes qui expliquent l'origine d'un projet. Pas de photo, pas d'âge, pas d'adresse complète.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les erreurs fréquentes
&lt;/h2&gt;

&lt;p&gt;Ces erreurs se corrigent avant l'envoi&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Un titre vague,&lt;/strong&gt; comme «&amp;nbsp;Développeur&amp;nbsp;» sans spécialité.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Des projets sans lien,&lt;/strong&gt; ou des liens vers des dépôts vides, privés ou sans README.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Une liste de vingt technologies&lt;/strong&gt; dont la moitié a été vue une seule fois en tutoriel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Une mise en page en colonnes&lt;/strong&gt; ou chargée, que l'étude de Ladders, citée par HR Dive, associe à une lecture moins attentive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un CV unique pour toutes les offres,&lt;/strong&gt; alors qu'ajuster le titre, le résumé et l'ordre des compétences prend peu de temps.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  La checklist avant d'envoyer
&lt;/h2&gt;

&lt;p&gt;Relisez votre CV développeur junior avec cette liste&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Le document tient sur une page et s'enregistre en PDF dont le texte se sélectionne.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Le nom du fichier est explicite&amp;nbsp;: «&amp;nbsp;CV-Prenom-Nom-developpeur-web.pdf&amp;nbsp;».&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Le titre reprend l'intitulé de l'offre, avec la pile principale.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Le résumé tient en trois lignes et dit quel poste vous cherchez.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chaque projet a un lien de code qui fonctionne, et une démo si possible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chaque chiffre est vrai, et vous savez expliquer comment il a été obtenu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Les compétences listées apparaissent dans au moins un projet ou une expérience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Les profils GitHub et LinkedIn sont à jour.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Aucune information sans lien avec le poste (âge, situation de famille, numéro de sécurité sociale).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Une autre personne a relu l'orthographe.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Les points suivants restent incertains&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Le taux de CV rejetés par les ATS.&lt;/strong&gt; Le chiffre de 75&amp;nbsp;% circule sans source primaire que nous ayons pu lire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les autres ATS.&lt;/strong&gt; Nous n'avons lu que la documentation de Greenhouse&amp;nbsp;; d'autres outils peuvent analyser le contenu des CV.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La méthode de l'étude Ladders,&lt;/strong&gt; lue seulement à travers HR Dive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le texte exact de l'article L.1221-7 du Code du travail&lt;/strong&gt; sur le CV anonyme&amp;nbsp;: Légifrance n'était pas accessible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'effet d'un CV sur le taux d'entretien.&lt;/strong&gt; Aucune étude que nous avons lue ne mesure l'effet d'une structure de CV sur les réponses des recruteurs en France.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Un CV développeur junior doit-il tenir sur une page ?
&lt;/h3&gt;

&lt;p&gt;Oui, dans la plupart des cas. OpenClassrooms recommande une page pour un profil junior en France. Une deuxième page n'a de sens que si elle contient des projets solides, pas des emplois sans rapport.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faut-il mettre une photo sur un CV de développeur en France ?
&lt;/h3&gt;

&lt;p&gt;Non. Le &lt;a href="https://www.defenseurdesdroits.fr/sites/default/files/2023-03/DDD_guide_recrutement-sans-discrimination_20190703.pdf" rel="noopener noreferrer"&gt;guide du Défenseur des droits&lt;/a&gt; estime qu'exiger une photo ne peut être qu'exceptionnel, et l'apparence physique fait partie des critères de discrimination interdits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment faire un CV de développeur sans expérience ?
&lt;/h3&gt;

&lt;p&gt;Placez les projets avant l'expérience, et décrivez chacun avec ce qu'il fait, sa pile, votre rôle et un lien vers le code. Les emplois sans rapport tiennent en une ligne, sauf s'ils expliquent l'origine d'un projet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les ATS rejettent-ils les CV en PDF ?
&lt;/h3&gt;

&lt;p&gt;Pas chez Greenhouse, dont la &lt;a href="https://support.greenhouse.io/hc/en-us/articles/360052218132-Supported-formats-for-resumes-cover-letters-and-other-candidate-uploads" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; accepte le PDF, le Word, le RTF et le texte brut. Selon sa &lt;a href="https://support.greenhouse.io/hc/en-us/articles/203105595-Application-rules-overview" rel="noopener noreferrer"&gt;documentation sur les règles de candidature&lt;/a&gt;, le rejet automatique y repose sur les réponses aux questions du formulaire. Nous n'avons pas lu la documentation des autres éditeurs. Un PDF dont le texte se sélectionne, en une colonne, est notre recommandation.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse fondée exclusivement sur des sources publiques&amp;nbsp;: pages de France Travail, de la CNIL, du Défenseur des droits et de l'Assemblée nationale, baromètre de l'Apec, rapports de HackerRank et de Harvard Business School, documentation de Greenhouse, GitHub, LinkedIn et Europass. Les chiffres d'éditeurs sont signalés comme tels, et chaque enquête est citée avec son périmètre. Les citations issues de sources en anglais sont traduites par nos soins. L'exemple de CV est fictif. Aucun test n'a été mené auprès de recruteurs ou d'ATS, aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux éditeurs mentionnés.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou une règle périmée&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 31 octobre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Markus Winkler (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cv</category>
      <category>developpeurjunior</category>
      <category>recrutement</category>
      <category>reconversion</category>
    </item>
    <item>
      <title>GitHub Actions : le tutoriel pas à pas pour tester et publier une API Node.js</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:43:59 +0000</pubDate>
      <link>https://dev.to/neurau_tech/github-actions-le-tutoriel-pas-a-pas-pour-tester-et-publier-une-api-nodejs-34ln</link>
      <guid>https://dev.to/neurau_tech/github-actions-le-tutoriel-pas-a-pas-pour-tester-et-publier-une-api-nodejs-34ln</guid>
      <description>&lt;h2&gt;
  
  
  Les six notions de GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Tout workflow GitHub Actions se lit avec six mots, définis dans la page &lt;a href="https://docs.github.com/en/actions/get-started/understand-github-actions" rel="noopener noreferrer"&gt;Understanding GitHub Actions&lt;/a&gt; de la documentation officielle (consultée le 31 octobre 2026)&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Notion&lt;/th&gt;
&lt;th&gt;Ce que c'est&lt;/th&gt;
&lt;th&gt;Dans notre exemple&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Workflow&lt;/td&gt;
&lt;td&gt;Un processus automatisé décrit par un fichier YAML dans &lt;code&gt;.github/workflows/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ci.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Événement&lt;/td&gt;
&lt;td&gt;L'activité du dépôt qui déclenche le workflow&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;push&lt;/code&gt; et &lt;code&gt;pull_request&lt;/code&gt; sur &lt;code&gt;main&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;Une suite d'étapes exécutées sur un même runner&amp;nbsp;; les jobs tournent en parallèle sauf dépendance&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;test&lt;/code&gt; et &lt;code&gt;image&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step&lt;/td&gt;
&lt;td&gt;Une commande shell ou une action, exécutées dans l'ordre&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm ci&lt;/code&gt;, &lt;code&gt;npm test&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runner&lt;/td&gt;
&lt;td&gt;La machine qui exécute un job, un seul job à la fois&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ubuntu-latest&lt;/code&gt;, hébergé par GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action&lt;/td&gt;
&lt;td&gt;Une extension réutilisable, souvent prise sur le GitHub Marketplace&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;actions/checkout&lt;/code&gt;, &lt;code&gt;docker/login-action&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Chaque exécution part d'une machine virtuelle neuve, et chaque action du Marketplace est du code tiers qui tourne avec vos droits, d'où l'étape 6.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fendm5pmx1a1ge5mnia3x.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fendm5pmx1a1ge5mnia3x.webp" alt="Schéma du workflow GitHub Actions de ce tutoriel&amp;nbsp;: un événement push ou pull_request déclenche deux jobs de test en parallèle sur Node.js 24 et 26, puis un job qui publie l'image Docker sur ghcr.io" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 1 : un premier workflow pour l'API Node.js
&lt;/h2&gt;

&lt;p&gt;Le point de départ&amp;nbsp;: récupérer le code, installer Node.js et les dépendances, lancer les tests. L'API du tutoriel déclare &lt;code&gt;"test": "node --test"&lt;/code&gt; dans son &lt;code&gt;package.json&lt;/code&gt; et exige Node.js 24 ou plus. Créez &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npm ci&lt;/code&gt; installe exactement ce que fixe &lt;code&gt;package-lock.json&lt;/code&gt;, que le dépôt doit contenir, et &lt;code&gt;npm test&lt;/code&gt; lance &lt;code&gt;node --test&lt;/code&gt;. Le &lt;code&gt;timeout-minutes&lt;/code&gt; évite qu'un test bloqué tourne jusqu'à la limite de 6&amp;nbsp;heures par job des runners hébergés, fixée par la &lt;a href="https://docs.github.com/en/actions/reference/limits" rel="noopener noreferrer"&gt;page des limites de GitHub Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pourquoi checkout v7 et pas v4&amp;nbsp;? Selon son &lt;a href="https://github.com/actions/checkout" rel="noopener noreferrer"&gt;README&lt;/a&gt;, la v5 est passée au moteur node24, la v6 range les identifiants Git hors de &lt;code&gt;.git/config&lt;/code&gt;, et la v7 refuse par défaut le code d'un fork sous &lt;code&gt;pull_request_target&lt;/code&gt; ou &lt;code&gt;workflow_run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Les dernières versions au 31 octobre 2026 sont &lt;a href="https://github.com/actions/checkout/releases" rel="noopener noreferrer"&gt;actions/checkout v7.0.1&lt;/a&gt; et &lt;a href="https://github.com/actions/setup-node/releases" rel="noopener noreferrer"&gt;actions/setup-node v7.1.0&lt;/a&gt;. L'onglet Actions affiche ensuite chaque exécution&amp;nbsp;; pour lire les journaux d'un runner Ubuntu, voyez nos &lt;a href="https://neurautech.com/commandes-linux-de-base/" rel="noopener noreferrer"&gt;commandes Linux de base&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 2 : tester sur Node.js 24 et 26 avec une matrice
&lt;/h2&gt;

&lt;p&gt;Une matrice exécute le même job plusieurs fois avec des valeurs différentes. Pour l'API, deux versions comptent&amp;nbsp;: Node.js 24, minimum exigé par le &lt;code&gt;package.json&lt;/code&gt;, et Node.js 26, passé en LTS avec la version 26.13.0 du 28 octobre 2026 selon l'&lt;a href="https://nodejs.org/dist/index.json" rel="noopener noreferrer"&gt;index des versions de Node.js&lt;/a&gt;. Remplacez le job &lt;code&gt;test&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;24&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;26&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v7&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les deux jobs tournent en parallèle. &lt;code&gt;fail-fast: false&lt;/code&gt; laisse finir le second si le premier échoue. La &lt;a href="https://docs.github.com/en/actions/reference/limits" rel="noopener noreferrer"&gt;page des limites&lt;/a&gt; plafonne une matrice à 256 jobs, et chaque job est facturé séparément.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 3 : le cache, plus rapide mais pas sans risque
&lt;/h2&gt;

&lt;p&gt;Le cache de GitHub Actions évite de retélécharger les paquets npm à chaque exécution&amp;nbsp;: ajoutez &lt;code&gt;cache: npm&lt;/code&gt; sous &lt;code&gt;with&lt;/code&gt;. D'après le &lt;a href="https://github.com/actions/setup-node" rel="noopener noreferrer"&gt;README de setup-node&lt;/a&gt;, la clé dérive du hachage de &lt;code&gt;package-lock.json&lt;/code&gt; et l'action met en cache le dossier global de npm, pas &lt;code&gt;node_modules&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les limites, lues dans la &lt;a href="https://docs.github.com/en/actions/reference/workflows-and-actions/dependency-caching" rel="noopener noreferrer"&gt;référence du cache de dépendances&lt;/a&gt; le 31 octobre 2026&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;10&amp;nbsp;Go par dépôt par défaut.&lt;/strong&gt; Une entrée non lue depuis plus de 7&amp;nbsp;jours est supprimée.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aucun secret dans le cache.&lt;/strong&gt; Toute personne ayant un accès en lecture peut ouvrir une pull request et lire un cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le mot-clé &lt;code&gt;cache-mode&lt;/code&gt; (&lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;write-only&lt;/code&gt; ou &lt;code&gt;none&lt;/code&gt;) fixe les droits d'un job sur le cache. Il se place en tête du workflow ou dans un job, par exemple &lt;code&gt;cache-mode: read&lt;/code&gt; sous &lt;code&gt;test:&lt;/code&gt;, d'après la &lt;a href="https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax" rel="noopener noreferrer"&gt;référence de syntaxe&lt;/a&gt;. Selon la documentation du cache consultée le 31 octobre 2026, un déclencheur peu fiable comme &lt;code&gt;pull_request_target&lt;/code&gt; n'obtient par défaut que la lecture, et y déclarer &lt;code&gt;write&lt;/code&gt; réintroduit le risque d'empoisonnement du cache.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 4 : secrets, variables et environnements
&lt;/h2&gt;

&lt;p&gt;Un jeton n'entre jamais dans le YAML&amp;nbsp;: il se range dans Settings, Secrets and variables, Actions, et se lit avec &lt;code&gt;${{ secrets.NOM }}&lt;/code&gt;. Une valeur non sensible, comme une URL, devient une variable lue avec &lt;code&gt;${{ vars.NOM }}&lt;/code&gt;, au niveau du dépôt, de l'organisation ou d'un environnement, selon la &lt;a href="https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-variables" rel="noopener noreferrer"&gt;page sur les variables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pour rattacher le job &lt;code&gt;image&lt;/code&gt; de l'étape 5 à un environnement &lt;code&gt;production&lt;/code&gt;, avec une variable &lt;code&gt;PRODUCTION_URL&lt;/code&gt; créée dans cet environnement, ajoutez au job&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ vars.PRODUCTION_URL }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La &lt;a href="https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax" rel="noopener noreferrer"&gt;référence de syntaxe&lt;/a&gt; autorise le contexte &lt;code&gt;vars&lt;/code&gt; dans &lt;code&gt;url&lt;/code&gt;. Mais la &lt;a href="https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments" rel="noopener noreferrer"&gt;page de gestion des environnements&lt;/a&gt; réserve les environnements aux dépôts publics sur GitHub Free&amp;nbsp;; en privé, il faut Pro, Team ou Enterprise. C'est pourquoi le fichier complet plus bas s'en passe.&lt;/p&gt;

&lt;p&gt;Notre workflow n'a besoin d'aucun secret ajouté à la main. Les tests de l'API fixent eux-mêmes &lt;code&gt;JWT_SECRET&lt;/code&gt; et &lt;code&gt;DB_PATH=':memory:'&lt;/code&gt; dans &lt;code&gt;test/api.test.js&lt;/code&gt;, et le job &lt;code&gt;image&lt;/code&gt; construit l'image sans démarrer le conteneur. Quant au &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;, créé pour chaque job, il suffit à publier sur le registre de GitHub.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 5 : construire et publier l'image Docker sur GHCR
&lt;/h2&gt;

&lt;p&gt;GHCR, le registre de conteneurs de GitHub (&lt;code&gt;ghcr.io&lt;/code&gt;), accepte un push authentifié par &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;. L'image reprend le Dockerfile multi-étapes de notre tutoriel &lt;a href="https://neurautech.com/docker-pour-debutants/" rel="noopener noreferrer"&gt;Docker pour débutants&lt;/a&gt;, basé sur &lt;code&gt;node:24-alpine&lt;/code&gt;, placé à la racine du projet. Ajoutez ce job à &lt;code&gt;ci.yml&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name == 'push' &amp;amp;&amp;amp; github.ref == 'refs/heads/main'&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/metadata-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/${{ github.repository }}&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;type=ref,event=branch&lt;/span&gt;
            &lt;span class="s"&gt;type=sha&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;
          &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.labels }}&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce que fait chaque bloc&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;needs&lt;/code&gt; et &lt;code&gt;if&lt;/code&gt;.&lt;/strong&gt; L'image n'est construite que si les deux tests réussissent, et seulement pour un push sur &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;docker/metadata-action&lt;/code&gt;.&lt;/strong&gt; L'image prend le nom du dépôt, &lt;code&gt;ghcr.io/&amp;lt;compte&amp;gt;/&amp;lt;dépôt&amp;gt;&lt;/code&gt;, soit &lt;code&gt;ghcr.io/&amp;lt;compte&amp;gt;/api-taches&lt;/code&gt; si le dépôt s'appelle comme le dossier du tutoriel. L'action produit les tags &lt;code&gt;main&lt;/code&gt; et &lt;code&gt;sha-&lt;/code&gt; suivi du commit abrégé, plus les labels OCI. Son &lt;a href="https://github.com/docker/metadata-action" rel="noopener noreferrer"&gt;README&lt;/a&gt; précise qu'il met le nom d'image en minuscules, ce qu'exige le registre.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;context: .&lt;/code&gt; avec checkout.&lt;/strong&gt; Sans checkout, build-push-action lit le dépôt par Git et ignore les modifications des étapes précédentes, selon son &lt;a href="https://github.com/docker/build-push-action" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;type=gha&lt;/code&gt;.&lt;/strong&gt; Les couches Docker vont dans le cache de GitHub Actions. Avec plusieurs images, fixez un &lt;code&gt;scope&lt;/code&gt; distinct, sinon chaque build écrase le cache du précédent, prévient la &lt;a href="https://docs.docker.com/build/cache/backends/gha/" rel="noopener noreferrer"&gt;documentation de Docker&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deux pièges documentés par GitHub sur la page du &lt;a href="https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry" rel="noopener noreferrer"&gt;Container registry&lt;/a&gt;. Un paquet publié pour la première fois est privé par défaut. Et le &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; ne peut pas pousser si une image du même nom a déjà été poussée à la main sans être reliée au dépôt.&lt;/p&gt;

&lt;p&gt;Côté coût, la &lt;a href="https://docs.github.com/en/billing/concepts/product-billing/github-packages" rel="noopener noreferrer"&gt;facturation de GitHub Packages&lt;/a&gt; indique le 31 octobre 2026 que le stockage du Container registry est gratuit pour l'instant, avec un mois de préavis avant tout changement. L'image se déploie ensuite derrière notre &lt;a href="https://neurautech.com/nginx-reverse-proxy-docker-compose/" rel="noopener noreferrer"&gt;reverse proxy Nginx avec Docker Compose&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 6 : GITHUB_TOKEN minimal et actions épinglées
&lt;/h2&gt;

&lt;p&gt;Cette étape a deux volets&amp;nbsp;: limiter ce que le jeton peut faire, et figer le code des actions exécutées.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le principe du moindre privilège
&lt;/h3&gt;

&lt;p&gt;Le &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; est limité au dépôt et valable le temps du job. Ses droits dépendent du réglage du dépôt, puis du bloc &lt;code&gt;permissions&lt;/code&gt;. La &lt;a href="https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax" rel="noopener noreferrer"&gt;référence de syntaxe&lt;/a&gt; fixe la règle utile&amp;nbsp;: dès que vous citez une permission, toutes celles que vous ne citez pas passent à &lt;code&gt;none&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;D'où notre schéma&amp;nbsp;: &lt;code&gt;contents: read&lt;/code&gt; au niveau du workflow, &lt;code&gt;packages: write&lt;/code&gt; dans le seul job &lt;code&gt;image&lt;/code&gt;. C'est ce que récompense le test Token-Permissions d'&lt;a href="https://github.com/ossf/scorecard/blob/main/docs/checks.md" rel="noopener noreferrer"&gt;OpenSSF Scorecard&lt;/a&gt;, qui classe ce risque comme élevé. L'option &lt;code&gt;persist-credentials: false&lt;/code&gt; de checkout évite en plus de conserver le jeton pour les commandes Git suivantes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Épingler les actions sur un SHA
&lt;/h3&gt;

&lt;p&gt;Un tag comme &lt;code&gt;@v7&lt;/code&gt; peut être déplacé. Selon l'&lt;a href="https://github.com/advisories/GHSA-mrrh-fwg8-r2c3" rel="noopener noreferrer"&gt;avis de sécurité GitHub GHSA-mrrh-fwg8-r2c3&lt;/a&gt; (CVE-2025-30066), des attaquants ont redirigé en mars 2025 plusieurs tags de tj-actions/changed-files vers un commit malveillant, qui affichait les secrets dans les journaux entre le 14 et le 15 mars.&lt;/p&gt;

&lt;p&gt;La &lt;a href="https://docs.github.com/en/actions/reference/security/secure-use" rel="noopener noreferrer"&gt;référence de sécurité de GitHub Actions&lt;/a&gt; recommande donc d'épingler chaque action sur l'empreinte complète d'un commit, en vérifiant qu'elle vient du dépôt de l'action et non d'un fork. La forme est &lt;code&gt;uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1&lt;/code&gt;&amp;nbsp;: l'empreinte fige le code, le commentaire garde la version lisible. Le fichier complet plus bas épingle ainsi les six actions sur les versions publiées au 31 octobre 2026. Nous avons lu ces empreintes sur l'API de GitHub&amp;nbsp;; vérifiez-les sur la page des versions de chaque action.&lt;/p&gt;

&lt;p&gt;Pour ne pas figer des versions vulnérables, laissez Dependabot proposer les mises à jour avec &lt;code&gt;.github/dependabot.yml&lt;/code&gt;, selon la &lt;a href="https://docs.github.com/en/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/auto-update-actions" rel="noopener noreferrer"&gt;documentation de Dependabot&lt;/a&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;updates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;package-ecosystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github-actions"&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
    &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weekly"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Selon la référence de sécurité, les alertes Dependabot ignorent les actions épinglées par SHA&amp;nbsp;; les mises à jour de version, elles, fonctionnent.&lt;/p&gt;

&lt;p&gt;GitHub va plus loin avec les &lt;a href="https://docs.github.com/en/actions/concepts/security/workflow-dependency-lockfiles" rel="noopener noreferrer"&gt;fichiers de verrouillage des workflows&lt;/a&gt;&amp;nbsp;: &lt;code&gt;.github/workflows/actions.lock&lt;/code&gt;, généré par l'extension &lt;code&gt;gh actions-lock&lt;/code&gt;, fige le commit de chaque action, dépendances indirectes comprises, et vérifie l'identité du dépôt source. La référence de sécurité le présente comme la meilleure protection, qui remplace l'épinglage par SHA pour les dépôts qui l'adoptent. Ce tutoriel garde les SHA&amp;nbsp;: la documentation ne dit pas si le dispositif est en préversion ou disponible pour tous, et il exige une extension de GitHub CLI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 7 : concurrence et workflows réutilisables
&lt;/h2&gt;

&lt;p&gt;Deux mots-clés évitent le gaspillage et la copie de YAML. Le premier, &lt;code&gt;concurrency&lt;/code&gt;, se place en tête de &lt;code&gt;ci.yml&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workflow }}-${{ github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'pull_request' }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;D'après la &lt;a href="https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency" rel="noopener noreferrer"&gt;page sur la concurrence&lt;/a&gt;, un seul workflow s'exécute à la fois par groupe, et une exécution en attente est remplacée par la plus récente. L'expression annule aussi l'exécution en cours, mais seulement pour une pull request. Sur &lt;code&gt;main&lt;/code&gt;, l'exécution en cours va à son terme, mais une exécution en attente reste remplacée par la plus récente&amp;nbsp;: seul le dernier push en file est publié.&lt;/p&gt;

&lt;p&gt;Le second, &lt;code&gt;workflow_call&lt;/code&gt;, transforme un workflow en brique réutilisable. Déplacez le job de test dans &lt;code&gt;.github/workflows/tests-node.yml&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tests Node.js&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_call&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;node-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Versions de Node.js, au format JSON&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[24,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;26]'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ fromJSON(inputs.node-versions) }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1&lt;/span&gt; &lt;span class="c1"&gt;# v7.0.1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1&lt;/span&gt; &lt;span class="c1"&gt;# v7.1.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans &lt;code&gt;ci.yml&lt;/code&gt;, le job &lt;code&gt;test&lt;/code&gt; devient un simple appel, et &lt;code&gt;image&lt;/code&gt; garde son &lt;code&gt;needs: test&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./.github/workflows/tests-node.yml&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;node-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[24,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;26]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La forme &lt;code&gt;./.github/workflows/{filename}&lt;/code&gt; est celle que la &lt;a href="https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows" rel="noopener noreferrer"&gt;documentation sur la réutilisation&lt;/a&gt; donne pour un workflow du même dépôt. La même page, consultée le 31 octobre 2026, mentionne aussi une forme &lt;code&gt;$/.github/workflows/{filename}&lt;/code&gt;, qu'elle dit recommandée et indisponible sur GitHub Enterprise Server&amp;nbsp;; nous ne l'avons pas testée. La &lt;a href="https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations" rel="noopener noreferrer"&gt;référence des workflows réutilisables&lt;/a&gt; fixe dix niveaux d'imbrication au plus et 50 workflows réutilisables distincts par fichier&amp;nbsp;; les variables &lt;code&gt;env&lt;/code&gt; de l'appelant ne sont pas transmises, et le workflow appelé ne peut pas élever les permissions reçues.&lt;/p&gt;




&lt;h2&gt;
  
  
  Le fichier ci.yml complet
&lt;/h2&gt;

&lt;p&gt;Voici &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; une fois les étapes 1 à 7 assemblées, avec le job &lt;code&gt;test&lt;/code&gt; écrit en entier (sans le workflow réutilisable de l'étape 7 ni l'environnement optionnel de l'étape 4)&amp;nbsp;: déclencheurs, permissions minimales, concurrence, matrice avec cache, publication sur GHCR, et chaque action épinglée sur son SHA. Sa syntaxe a été vérifiée avec le module &lt;code&gt;yaml&lt;/code&gt; de Python&amp;nbsp;; il n'a pas été exécuté sur GitHub. Avec le workflow réutilisable de l'étape 7, le job &lt;code&gt;test&lt;/code&gt; ci-dessous est remplacé par l'appel à &lt;code&gt;tests-node.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workflow }}-${{ github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'pull_request' }}&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;24&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;26&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1&lt;/span&gt; &lt;span class="c1"&gt;# v7.0.1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1&lt;/span&gt; &lt;span class="c1"&gt;# v7.1.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;

  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name == 'push' &amp;amp;&amp;amp; github.ref == 'refs/heads/main'&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1&lt;/span&gt; &lt;span class="c1"&gt;# v7.0.1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069&lt;/span&gt; &lt;span class="c1"&gt;# v4.4.1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6&lt;/span&gt; &lt;span class="c1"&gt;# v4.7.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302&lt;/span&gt; &lt;span class="c1"&gt;# v6.2.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/${{ github.repository }}&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;type=ref,event=branch&lt;/span&gt;
            &lt;span class="s"&gt;type=sha&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc&lt;/span&gt; &lt;span class="c1"&gt;# v7.4.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;
          &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.labels }}&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Combien coûte GitHub Actions en octobre 2026
&lt;/h2&gt;

&lt;p&gt;GitHub Actions reste gratuit pour les dépôts publics sur les runners standards, et sur vos propres machines. Pour les dépôts privés, la &lt;a href="https://docs.github.com/en/billing/concepts/product-billing/github-actions" rel="noopener noreferrer"&gt;page de facturation de GitHub Actions&lt;/a&gt;, lue le 31 octobre 2026, exprime désormais le quota en dollars de calcul inclus, et non plus en minutes, déduits selon le tarif de chaque runner&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offre&lt;/th&gt;
&lt;th&gt;Calcul inclus par mois&lt;/th&gt;
&lt;th&gt;Soit, sur Linux 2 cœurs&lt;/th&gt;
&lt;th&gt;Stockage d'artefacts&lt;/th&gt;
&lt;th&gt;Cache par dépôt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Free&lt;/td&gt;
&lt;td&gt;12&amp;nbsp;$&lt;/td&gt;
&lt;td&gt;2&amp;nbsp;000&amp;nbsp;minutes&lt;/td&gt;
&lt;td&gt;500&amp;nbsp;Mo&lt;/td&gt;
&lt;td&gt;10&amp;nbsp;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Pro&lt;/td&gt;
&lt;td&gt;18&amp;nbsp;$&lt;/td&gt;
&lt;td&gt;3&amp;nbsp;000&amp;nbsp;minutes&lt;/td&gt;
&lt;td&gt;1&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;10&amp;nbsp;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Team&lt;/td&gt;
&lt;td&gt;18&amp;nbsp;$&lt;/td&gt;
&lt;td&gt;3&amp;nbsp;000&amp;nbsp;minutes&lt;/td&gt;
&lt;td&gt;2&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;10&amp;nbsp;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Enterprise Cloud&lt;/td&gt;
&lt;td&gt;300&amp;nbsp;$&lt;/td&gt;
&lt;td&gt;50&amp;nbsp;000&amp;nbsp;minutes&lt;/td&gt;
&lt;td&gt;50&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;10&amp;nbsp;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La conversion est notre calcul à 0,006&amp;nbsp;$ la minute. Les 2&amp;nbsp;000&amp;nbsp;minutes souvent citées, y compris dans notre guide CI/CD, correspondent donc aux 12&amp;nbsp;$ de l'offre Free sur Linux 2 cœurs&amp;nbsp;; la &lt;a href="https://github.com/pricing" rel="noopener noreferrer"&gt;page de prix de GitHub&lt;/a&gt; affiche bien 2&amp;nbsp;000, 3&amp;nbsp;000 et 50&amp;nbsp;000&amp;nbsp;minutes pour Free, Team et Enterprise, avec Team à 4&amp;nbsp;$ et Enterprise à 21&amp;nbsp;$ par utilisateur et par mois.&lt;/p&gt;

&lt;p&gt;Le quota en dollars change la donne dès que vous quittez Linux. La &lt;a href="https://docs.github.com/en/billing/reference/actions-runner-pricing" rel="noopener noreferrer"&gt;grille des runners&lt;/a&gt; facture 0,002&amp;nbsp;$ la minute sur Linux 1 cœur, 0,005&amp;nbsp;$ sur Linux arm64 2 cœurs, 0,010&amp;nbsp;$ sur Windows 2 cœurs et 0,062&amp;nbsp;$ sur macOS 3 ou 4 cœurs. Les 12&amp;nbsp;$ de l'offre Free couvrent donc environ 193&amp;nbsp;minutes de macOS seulement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy2jsuxeo86e40pmnrro0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy2jsuxeo86e40pmnrro0.webp" alt="Graphique en barres horizontales GitHub Actions&amp;nbsp;: selon notre calcul, les 12 dollars inclus dans GitHub Free couvrent 6&amp;nbsp;000&amp;nbsp;minutes sur Linux 1 cœur, 2&amp;nbsp;400 sur Linux arm64 2 cœurs, 2&amp;nbsp;000 sur Linux 2 cœurs, 1&amp;nbsp;200 sur Windows 2 cœurs et 193 sur macOS 3 ou 4 cœurs" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notre calcul.&lt;/strong&gt; Chaque job étant arrondi à la minute supérieure, notre &lt;code&gt;ci.yml&lt;/code&gt; coûte au moins 3&amp;nbsp;minutes par push sur &lt;code&gt;main&lt;/code&gt; et 2 par pull request. À 50 de chaque par mois sur un dépôt privé, cela fait au moins 250&amp;nbsp;minutes, bien dans le quota Free. Une matrice sur trois systèmes d'exploitation changerait tout&amp;nbsp;: une minute de macOS coûte plus de dix minutes de Linux 2 cœurs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Le stockage se paie à part&amp;nbsp;: 0,25&amp;nbsp;$ par Go et par mois au-delà de l'allocation d'artefacts, 0,07&amp;nbsp;$ pour le cache si vous avez relevé sa limite de 10&amp;nbsp;Go, selon la même page.&lt;/p&gt;




&lt;h2&gt;
  
  
  Runners auto-hébergés : gratuits, pas sans danger
&lt;/h2&gt;

&lt;p&gt;Un runner auto-hébergé est une machine à vous qui exécute les jobs à la place des machines de GitHub. La &lt;a href="https://docs.github.com/en/billing/concepts/product-billing/github-actions" rel="noopener noreferrer"&gt;page de facturation&lt;/a&gt; les range, le 31 octobre 2026, parmi les usages gratuits qui ne réduisent pas le calcul inclus. GitHub avait annoncé en décembre 2025 une redevance de 0,002&amp;nbsp;$ la minute sur les runners auto-hébergés à partir du 1er mars 2026, puis a écrit la reporter «&amp;nbsp;pour prendre le temps de réévaluer notre approche&amp;nbsp;» (notre traduction), sur sa &lt;a href="https://github.com/resources/insights/2026-pricing-changes-for-github-actions" rel="noopener noreferrer"&gt;page consacrée aux changements de prix de 2026&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Selon la &lt;a href="https://docs.github.com/en/actions/concepts/runners/self-hosted-runners" rel="noopener noreferrer"&gt;page sur les runners auto-hébergés&lt;/a&gt;, vous payez la machine et mettez à jour vous-même système et outils. Checkout et setup-node, passées au moteur node24, exigent le runner v2.327.1 ou plus selon leurs README. Un job peut y tourner 5&amp;nbsp;jours d'après la &lt;a href="https://docs.github.com/en/actions/reference/limits" rel="noopener noreferrer"&gt;page des limites&lt;/a&gt;, mais le &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; ne se renouvelle que pendant 24&amp;nbsp;heures, précise la &lt;a href="https://docs.github.com/en/actions/concepts/security/github_token" rel="noopener noreferrer"&gt;page GITHUB_TOKEN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Le vrai risque est ailleurs. Selon la &lt;a href="https://docs.github.com/en/actions/reference/security/secure-use" rel="noopener noreferrer"&gt;référence de sécurité&lt;/a&gt;, un runner auto-hébergé ne garantit pas une machine propre à chaque job et peut être compromis durablement&amp;nbsp;: il ne devrait presque jamais servir à un dépôt public, où n'importe qui peut ouvrir une pull request. Elle mentionne aussi des runners éphémères, dits JIT, qui n'exécutent qu'un job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; pour cette API, les runners hébergés suffisent. Notre comparatif de l'&lt;a href="https://neurautech.com/hebergement-cloud-pour-developpeurs/" rel="noopener noreferrer"&gt;hébergement cloud pour développeurs&lt;/a&gt; aide à choisir la machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Ce tutoriel est une synthèse documentaire. Restent incertains&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L'exécution des workflows.&lt;/strong&gt; Leur syntaxe YAML a été vérifiée avec un analyseur Python, mais aucun n'a tourné sur GitHub. Testez-les d'abord sur une branche.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le sort de la redevance des runners auto-hébergés.&lt;/strong&gt; Reportée, mais nous n'avons trouvé aucune annonce qui l'annule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le prix de GitHub Pro.&lt;/strong&gt; La page de prix lue ne l'affiche pas, ni aucun prix en euros.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le statut exact des fichiers de verrouillage.&lt;/strong&gt; La documentation les décrit sans préciser s'ils sont en préversion ou disponibles pour tous.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La durée réelle des jobs.&lt;/strong&gt; Nos chiffres de minutes sont des minimums dus à l'arrondi, pas des mesures.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Combien de minutes GitHub Actions gratuites pour un dépôt privé ?
&lt;/h3&gt;

&lt;p&gt;Sur l'offre Free, 12&amp;nbsp;$ de calcul par mois, soit 2&amp;nbsp;000&amp;nbsp;minutes sur le runner Linux 2 cœurs à 0,006&amp;nbsp;$ la minute, mais 193 seulement sur macOS, selon la &lt;a href="https://docs.github.com/en/billing/concepts/product-billing/github-actions" rel="noopener noreferrer"&gt;page de facturation de GitHub&lt;/a&gt; lue le 31 octobre 2026. Les dépôts publics sur les runners standards et les runners auto-hébergés ne consomment rien.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quelle version d'actions/checkout utiliser ?
&lt;/h3&gt;

&lt;p&gt;La v7&amp;nbsp;: au 31 octobre 2026, la dernière version publiée est la &lt;a href="https://github.com/actions/checkout/releases" rel="noopener noreferrer"&gt;7.0.1&lt;/a&gt;. Épinglez-la sur son SHA complet, avec la version en commentaire.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quelle différence entre une action et un workflow ?
&lt;/h3&gt;

&lt;p&gt;Le workflow est votre fichier YAML, déclenché par un événement, qui contient des jobs. Une action est une brique appelée dans un step avec &lt;code&gt;uses&lt;/code&gt;, comme &lt;code&gt;actions/setup-node&lt;/code&gt;. Un workflow réutilisable s'appelle, lui, au niveau d'un job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment publier une image Docker avec GitHub Actions ?
&lt;/h3&gt;

&lt;p&gt;Connectez-vous à &lt;code&gt;ghcr.io&lt;/code&gt; avec docker/login-action et le &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;, puis construisez et poussez avec docker/build-push-action, dans un job qui déclare &lt;code&gt;packages: write&lt;/code&gt; (étape 5).&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse documentaire fondée exclusivement sur des sources publiques consultées le 31 octobre 2026&amp;nbsp;: documentation de GitHub Actions, de GitHub Packages et de Dependabot, pages de facturation et de prix de GitHub, README et pages de versions des actions officielles et des actions Docker, documentation de Docker, OpenSSF Scorecard, avis de sécurité de GitHub, index des versions de Node.js. Les empreintes SHA ont été lues sur l'API de GitHub. La syntaxe YAML a été vérifiée localement, mais aucun workflow n'a été exécuté. Aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux éditeurs mentionnés.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou une version périmée&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 31 octobre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Alberto Rodríguez (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>cicd</category>
      <category>node</category>
      <category>docker</category>
    </item>
    <item>
      <title>LLM local avec Ollama : installer, choisir un modèle et coder en 2026</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:43:56 +0000</pubDate>
      <link>https://dev.to/neurau_tech/llm-local-avec-ollama-installer-choisir-un-modele-et-coder-en-2026-39e8</link>
      <guid>https://dev.to/neurau_tech/llm-local-avec-ollama-installer-choisir-un-modele-et-coder-en-2026-39e8</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Un LLM local est un modèle de langage qui tourne sur votre propre machine&amp;nbsp;: vos prompts ne partent pas chez un fournisseur, et l'outil fonctionne sans connexion une fois le modèle téléchargé. Ollama est l'un des moyens les plus directs d'y arriver. Sa dernière version, la 0.40.13, est sortie le 28 octobre 2026 (&lt;a href="https://github.com/ollama/ollama/releases" rel="noopener noreferrer"&gt;versions d'Ollama sur GitHub&lt;/a&gt;, consultées le 31 octobre 2026).&lt;/p&gt;

&lt;p&gt;Ce guide repose sur la documentation officielle d'Ollama, de llama.cpp, de Hugging Face et d'Apple, avec des tailles et des licences relevées aujourd'hui. Ollama n'est pas installé sur notre poste de rédaction&amp;nbsp;: aucune commande n'a été exécutée pour cet article.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ollama est gratuit, sous licence MIT, et tourne sur Windows 10 22H2 ou plus récent, macOS Sonoma ou plus récent et Linux.&lt;/li&gt;
&lt;li&gt;La règle de base&amp;nbsp;: le fichier du modèle doit tenir dans la mémoire disponible, avec de la marge pour le contexte. Le modèle qwen3.5:9b pèse de 6,6 à 7,6&amp;nbsp;Go dans Ollama.&lt;/li&gt;
&lt;li&gt;La quantification réduit chaque poids à 4 ou 5 bits environ au lieu de 16&amp;nbsp;: c'est elle qui rend un LLM local possible sur un ordinateur portable.&lt;/li&gt;
&lt;li&gt;L'API écoute sur &lt;code&gt;127.0.0.1:11434&lt;/code&gt; sans authentification. Ne l'ouvrez pas au réseau sans proxy authentifié&amp;nbsp;: SentinelLABS et Censys ont recensé 175&amp;nbsp;108 serveurs Ollama exposés.&lt;/li&gt;
&lt;li&gt;Pour coder, l'extension officielle Ollama pour VS Code ajoute vos modèles au sélecteur du chat, à condition d'avoir VS Code 1.127 ou plus récent.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Pourquoi faire tourner un LLM local, et quand s'en passer
&lt;/h2&gt;

&lt;p&gt;Un LLM local apporte la confidentialité, un coût prévisible et le travail hors ligne. La &lt;a href="https://docs.ollama.com/faq" rel="noopener noreferrer"&gt;FAQ d'Ollama&lt;/a&gt;, consultée le 31 octobre 2026, le dit&amp;nbsp;: «&amp;nbsp;Ollama s'exécute localement. Nous ne voyons pas vos prompts ni vos données quand vous l'utilisez en local&amp;nbsp;» (notre traduction).&lt;/p&gt;

&lt;p&gt;Nuance&amp;nbsp;: Ollama propose aussi des modèles hébergés, reconnaissables au suffixe &lt;code&gt;:cloud&lt;/code&gt;. Ceux-là s'exécutent sur les serveurs d'Ollama. La même FAQ indique qu'on peut désactiver ces fonctions avec la variable &lt;code&gt;OLLAMA_NO_CLOUD=1&lt;/code&gt;, qui coupe les modèles cloud et la recherche web. Le téléchargement des modèles passe toujours par le réseau.&lt;/p&gt;

&lt;p&gt;Côté coût, le logiciel et la plupart des modèles open-weight sont gratuits&amp;nbsp;: la facture se déplace vers le matériel et l'électricité. Notre guide du &lt;a href="https://neurautech.com/meilleur-laptop-pour-developpeur-tous-budgets-2026/" rel="noopener noreferrer"&gt;meilleur laptop pour développeur&lt;/a&gt; aide à dimensionner la mémoire.&lt;/p&gt;

&lt;p&gt;Les limites sont réelles&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;La taille.&lt;/strong&gt; Les plus gros modèles de notre sélection pèsent 65&amp;nbsp;Go (gpt-oss:120b) et 81&amp;nbsp;Go (qwen3.5:122b), et la bibliothèque en compte de bien plus lourds, comme qwen3-coder:480b à 290&amp;nbsp;Go (&lt;a href="https://ollama.com/library/qwen3-coder" rel="noopener noreferrer"&gt;fiche Qwen3-Coder&lt;/a&gt;). Ils sont hors de portée d'un portable courant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les modèles fermés.&lt;/strong&gt; Les modèles phares d'OpenAI, d'Anthropic ou de Google ne se téléchargent pas. Notre &lt;a href="https://neurautech.com/chatgpt-vs-claude-vs-gemini-lequel-choisir/" rel="noopener noreferrer"&gt;comparatif ChatGPT, Claude et Gemini&lt;/a&gt; couvre ces services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le contexte.&lt;/strong&gt; Sur une carte de moins de 24&amp;nbsp;Gio de VRAM, Ollama alloue par défaut une fenêtre de 4k tokens, 32k entre 24 et 48&amp;nbsp;Gio, et 256k au-delà (&lt;a href="https://docs.ollama.com/context-length" rel="noopener noreferrer"&gt;documentation sur la longueur de contexte&lt;/a&gt;). C'est souvent bien moins que le maximum annoncé par le modèle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La vitesse.&lt;/strong&gt; Elle dépend entièrement de votre matériel. Nous ne l'avons pas mesurée.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; un LLM local est idéal pour les données sensibles, le prototypage et le travail hors connexion. Pour les tâches les plus difficiles, un modèle cloud reste en général devant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Le matériel d'un LLM local : mémoire, quantification, Mac
&lt;/h2&gt;

&lt;p&gt;La mémoire décide de tout&amp;nbsp;: un modèle doit être chargé en entier, de préférence dans la mémoire du processeur graphique (VRAM). Le guide de démarrage d'Ollama donne un repère concret&amp;nbsp;: son modèle d'exemple, &lt;code&gt;gemma4:e2b&lt;/code&gt;, pèse environ 7,2&amp;nbsp;Go et demande 8&amp;nbsp;Go de VRAM disponibles, ou de mémoire unifiée sur Mac (&lt;a href="https://docs.ollama.com/quickstart" rel="noopener noreferrer"&gt;guide de démarrage d'Ollama&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Côté GPU, la &lt;a href="https://docs.ollama.com/gpu" rel="noopener noreferrer"&gt;page d'Ollama sur les cartes graphiques&lt;/a&gt; exige pour NVIDIA une «&amp;nbsp;compute capability&amp;nbsp;» de 5.0 au minimum et un pilote 550 ou plus récent. Les cartes AMD passent par ROCm v7, et Vulkan apporte un support supplémentaire sur Windows et Linux. Sans GPU compatible, le modèle tourne sur le processeur, plus lentement.&lt;/p&gt;

&lt;p&gt;Quand le modèle ne tient pas en entier dans la VRAM, il est chargé en partie dans la mémoire système. La commande &lt;code&gt;ollama ps&lt;/code&gt; le montre dans sa colonne PROCESSOR&amp;nbsp;: «&amp;nbsp;100% GPU&amp;nbsp;» signifie que tout est sur la carte, «&amp;nbsp;48%/52% CPU/GPU&amp;nbsp;» que le modèle est réparti entre les deux (&lt;a href="https://docs.ollama.com/faq" rel="noopener noreferrer"&gt;FAQ d'Ollama&lt;/a&gt;). Attendez-vous alors à une génération plus lente, que nous n'avons pas chiffrée.&lt;/p&gt;

&lt;p&gt;Le tableau suivant croise les tailles relevées sur la bibliothèque d'Ollama avec la règle du fichier qui doit tenir en mémoire, en supposant que tout tourne sur le GPU. C'est notre lecture, pas une recommandation de l'éditeur&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mémoire disponible (VRAM ou unifiée)&lt;/th&gt;
&lt;th&gt;Exemples de modèles dont le fichier tient&lt;/th&gt;
&lt;th&gt;Taille affichée&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;qwen3.5:4b, ministral-3:3b&lt;/td&gt;
&lt;td&gt;3 à 4&amp;nbsp;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;qwen3.5:9b, ministral-3:14b, gemma4:e2b (annoncé pour 8&amp;nbsp;Go par Ollama, sans marge)&amp;nbsp;; gpt-oss:20b à la limite&lt;/td&gt;
&lt;td&gt;4,6 à 14&amp;nbsp;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;gemma4:26b, gemma4:31b, qwen3-coder:30b, qwen3.6:35b&lt;/td&gt;
&lt;td&gt;16 à 23&amp;nbsp;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;96&amp;nbsp;Go et plus&lt;/td&gt;
&lt;td&gt;gpt-oss:120b (un GPU de 80&amp;nbsp;Go selon OpenAI), qwen3.5:122b&lt;/td&gt;
&lt;td&gt;65 et 81&amp;nbsp;Go&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;En haut de chaque ligne, il reste peu de place pour le contexte. Deux repères viennent directement des éditeurs&amp;nbsp;: la fiche de gpt-oss indique que le modèle 20b fonctionne sur des systèmes dotés de 16&amp;nbsp;Go de mémoire, et que le 120b tient sur un seul GPU de 80&amp;nbsp;Go (&lt;a href="https://ollama.com/library/gpt-oss" rel="noopener noreferrer"&gt;fiche gpt-oss sur Ollama&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdzu3i3xfrp7n90dtcmb4.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdzu3i3xfrp7n90dtcmb4.webp" alt="Graphique en barres horizontales&amp;nbsp;: taille de téléchargement de 13 modèles pour un LLM local dans Ollama, de 1,2&amp;nbsp;Go pour qwen3.5:0.8b à 81&amp;nbsp;Go pour qwen3.5:122b" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  La quantification expliquée simplement
&lt;/h3&gt;

&lt;p&gt;La quantification consiste à stocker chaque poids du modèle avec moins de bits. En demi-précision (F16), un poids occupe 16 bits. Au format GGUF utilisé par Ollama et llama.cpp, le type Q4_K descend à 4,5 bits par poids, Q5_K à 5,5 bits et Q6_K à 6,5625 bits, selon la &lt;a href="https://huggingface.co/docs/hub/gguf" rel="noopener noreferrer"&gt;documentation GGUF de Hugging Face&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Notre calcul&amp;nbsp;: nombre de paramètres multiplié par les bits par poids, divisé par 8, donne des octets. Pour 9&amp;nbsp;milliards de paramètres, cela fait environ 18&amp;nbsp;Go en F16 et 5,1&amp;nbsp;Go en Q4_K. Les fichiers réels pèsent plus&amp;nbsp;: le format courant Q4_K_M mélange plusieurs types et revient à 4,89 bits par poids sur l'ensemble du fichier, selon les mesures de llama.cpp (&lt;a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md" rel="noopener noreferrer"&gt;documentation de llama-quantize&lt;/a&gt;). Certains modèles embarquent aussi un encodeur d'images&amp;nbsp;: qwen3.5:9b accepte texte et image, ce qui contribue à ses 6,6&amp;nbsp;Go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl03fc2hab22ppm0f24kx.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl03fc2hab22ppm0f24kx.webp" alt="Graphique en colonnes&amp;nbsp;: taille de Llama 3.1 8B mesurée par llama.cpp selon la quantification, de 14,96&amp;nbsp;Gio en F16 à 4,58&amp;nbsp;Gio en Q4_K_M et 2,95&amp;nbsp;Gio en Q2_K, et la hausse de perplexité mesurée sur Llama 3 8B" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le graphique compte en gibioctets (1&amp;nbsp;Gio ≈ 1,07&amp;nbsp;Go), les tailles d'Ollama en gigaoctets.&lt;/p&gt;

&lt;p&gt;Moins de bits signifie aussi une perte de précision. L'outil &lt;code&gt;llama-quantize&lt;/code&gt; l'exprime en hausse de perplexité (plus haut = pire) sur Llama 3 8B&amp;nbsp;: +0,0026 en Q8_0, +0,1754 en Q4_K_M, +0,6569 en Q3_K_M et +3,5199 en Q2_K (&lt;a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/quantize.cpp" rel="noopener noreferrer"&gt;code source de llama-quantize&lt;/a&gt;). Ce sont des chiffres du projet, sur un modèle ancien&amp;nbsp;: retenez l'ordre de grandeur. Autour de 4 à 5 bits, la perte reste faible&amp;nbsp;; à 2 bits, elle devient nette.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mac Apple Silicon : la mémoire unifiée
&lt;/h3&gt;

&lt;p&gt;Sur un Mac à puce Apple, il n'y a pas de VRAM séparée. Apple décrit sa mémoire unifiée comme un pool unique dans lequel tous les composants de la puce accèdent aux mêmes données sans les copier (&lt;a href="https://www.apple.com/newsroom/2020/11/apple-unleashes-m1/" rel="noopener noreferrer"&gt;communiqué d'Apple sur la puce M1&lt;/a&gt;). Le processeur graphique peut donc utiliser cette mémoire pour le modèle, partagée avec macOS et vos applications. Il n'en dispose pas en totalité&amp;nbsp;: Metal expose une valeur, &lt;code&gt;recommendedMaxWorkingSetSize&lt;/code&gt;, qu'Apple définit comme une approximation de la mémoire que le GPU peut allouer sans dégrader ses performances (&lt;a href="https://developer.apple.com/documentation/metal/mtldevice/recommendedmaxworkingsetsize" rel="noopener noreferrer"&gt;documentation Metal d'Apple&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Ollama exploite le GPU des puces M par l'API Metal, alors que les Mac Intel tournent sur le processeur seul (&lt;a href="https://docs.ollama.com/macos" rel="noopener noreferrer"&gt;documentation macOS d'Ollama&lt;/a&gt;). Les MacBook Pro actuels vont de 16&amp;nbsp;Go de mémoire unifiée (puce M6, 153&amp;nbsp;Go/s de bande passante) à 128&amp;nbsp;Go (M5 Max à GPU 40 cœurs, 614&amp;nbsp;Go/s) (&lt;a href="https://www.apple.com/macbook-pro/liquid-retina-xdr-display/specs/" rel="noopener noreferrer"&gt;fiche technique du MacBook Pro&lt;/a&gt;, consultée le 31 octobre 2026).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; visez au moins 16&amp;nbsp;Go de mémoire disponible pour un usage confortable, et choisissez un format autour de 4 bits avant d'envisager un modèle plus petit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installer Ollama sur Windows, macOS et Linux
&lt;/h2&gt;

&lt;p&gt;L'installation prend une commande ou un installeur, selon le système. Voici ce que dit la documentation officielle, relue le 31 octobre 2026&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows.&lt;/strong&gt; Lancez &lt;code&gt;OllamaSetup.exe&lt;/code&gt;, qui ne demande pas de droits administrateur. Il faut Windows 10 22H2 ou plus récent, et un pilote NVIDIA 551.61 ou plus récent pour le GPU. Prévoyez au moins 4&amp;nbsp;Go pour le programme, plus l'espace des modèles (&lt;a href="https://docs.ollama.com/windows" rel="noopener noreferrer"&gt;documentation Windows&lt;/a&gt;). Le dépôt GitHub propose aussi &lt;code&gt;irm https://ollama.com/install.ps1 | iex&lt;/code&gt; dans PowerShell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;macOS.&lt;/strong&gt; Ouvrez &lt;code&gt;ollama.dmg&lt;/code&gt; et glissez l'application dans Applications. Il faut macOS Sonoma (14) ou plus récent. Au premier lancement, l'application propose de créer le lien de la commande &lt;code&gt;ollama&lt;/code&gt; dans &lt;code&gt;/usr/local/bin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux.&lt;/strong&gt; Une seule commande installe le binaire et un service systemd. Comme pour tout script envoyé au shell, vous pouvez d'abord le télécharger et le lire.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
ollama &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sous Linux, la &lt;a href="https://docs.ollama.com/linux" rel="noopener noreferrer"&gt;documentation Linux d'Ollama&lt;/a&gt; demande d'installer CUDA pour une carte NVIDIA, puis de vérifier avec &lt;code&gt;nvidia-smi&lt;/code&gt;, ou ROCm v7 pour une carte AMD. La mise à jour consiste à relancer le script. Si vous débutez en ligne de commande, notre guide des &lt;a href="https://neurautech.com/commandes-linux-de-base/" rel="noopener noreferrer"&gt;commandes Linux de base&lt;/a&gt; couvre l'essentiel.&lt;/p&gt;

&lt;p&gt;Les modèles se rangent dans &lt;code&gt;~/.ollama/models&lt;/code&gt; sur macOS, &lt;code&gt;/usr/share/ollama/.ollama/models&lt;/code&gt; sur Linux et &lt;code&gt;C:\Users\%username%\.ollama\models&lt;/code&gt; sur Windows. La variable &lt;code&gt;OLLAMA_MODELS&lt;/code&gt; permet de les déplacer vers un disque plus grand (&lt;a href="https://docs.ollama.com/faq" rel="noopener noreferrer"&gt;FAQ d'Ollama&lt;/a&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Télécharger et lancer un modèle
&lt;/h2&gt;

&lt;p&gt;Deux commandes suffisent&amp;nbsp;: &lt;code&gt;pull&lt;/code&gt; télécharge, &lt;code&gt;run&lt;/code&gt; ouvre une conversation dans le terminal. Le guide de démarrage utilise &lt;code&gt;gemma4:e2b&lt;/code&gt; comme exemple&amp;nbsp;; on quitte la session avec &lt;code&gt;/bye&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull gemma4:e2b
ollama run gemma4:e2b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les commandes du quotidien, d'après la &lt;a href="https://docs.ollama.com/cli" rel="noopener noreferrer"&gt;référence de la ligne de commande&lt;/a&gt;&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Commande&lt;/th&gt;
&lt;th&gt;Rôle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama pull &amp;lt;modèle&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Télécharge un modèle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama run &amp;lt;modèle&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lance un modèle et ouvre une conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Liste les modèles installés&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Liste les modèles chargés en mémoire&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama stop &amp;lt;modèle&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Décharge un modèle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama rm &amp;lt;modèle&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supprime un modèle du disque&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollama launch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configure et lance un outil externe (VS Code, Claude Code, OpenCode...)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Deux réglages méritent d'être connus. Un modèle reste en mémoire 5&amp;nbsp;minutes après la dernière requête, durée modifiable avec &lt;code&gt;OLLAMA_KEEP_ALIVE&lt;/code&gt;. Et la taille du contexte se change avec &lt;code&gt;OLLAMA_CONTEXT_LENGTH&lt;/code&gt; pour tout le serveur, ou &lt;code&gt;/set parameter num_ctx&lt;/code&gt; dans une session (&lt;a href="https://docs.ollama.com/faq" rel="noopener noreferrer"&gt;FAQ d'Ollama&lt;/a&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Quels modèles open-weight choisir en octobre 2026
&lt;/h2&gt;

&lt;p&gt;Un modèle open-weight publie ses poids, téléchargeables et utilisables, sans forcément publier ses données d'entraînement&amp;nbsp;: «&amp;nbsp;open source&amp;nbsp;» est souvent un abus de langage. La licence compte donc autant que la taille. Voici une sélection relevée le 31 octobre 2026 sur la bibliothèque d'Ollama, avec la licence lue sur la page de l'éditeur&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modèle&lt;/th&gt;
&lt;th&gt;Tailles dans Ollama&lt;/th&gt;
&lt;th&gt;Contexte maximal&lt;/th&gt;
&lt;th&gt;Licence&lt;/th&gt;
&lt;th&gt;Point fort annoncé&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://ollama.com/library/gemma4" rel="noopener noreferrer"&gt;Gemma 4&lt;/a&gt; (Google)&lt;/td&gt;
&lt;td&gt;e2b, e4b, 12b, 26b, 31b&amp;nbsp;: 4,6 à 20&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;128K à 256K&lt;/td&gt;
&lt;td&gt;Apache 2.0 (&lt;a href="https://huggingface.co/google/gemma-4-31B-it" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Texte et image, appel d'outils&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://ollama.com/library/qwen3.5" rel="noopener noreferrer"&gt;Qwen3.5&lt;/a&gt; (Alibaba)&lt;/td&gt;
&lt;td&gt;0.8b à 122b&amp;nbsp;: 1,2 à 81&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;256K&lt;/td&gt;
&lt;td&gt;Apache 2.0 (&lt;a href="https://huggingface.co/Qwen/Qwen3.5-9B" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Large gamme de tailles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://ollama.com/library/gpt-oss" rel="noopener noreferrer"&gt;gpt-oss&lt;/a&gt; (OpenAI)&lt;/td&gt;
&lt;td&gt;20b&amp;nbsp;: 14&amp;nbsp;Go&amp;nbsp;; 120b&amp;nbsp;: 65&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;128K&lt;/td&gt;
&lt;td&gt;Apache 2.0 (&lt;a href="https://huggingface.co/openai/gpt-oss-20b" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Raisonnement et agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://ollama.com/library/ministral-3" rel="noopener noreferrer"&gt;Ministral 3&lt;/a&gt; (Mistral AI)&lt;/td&gt;
&lt;td&gt;3b, 8b, 14b&amp;nbsp;: 3 à 9,1&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;256K&lt;/td&gt;
&lt;td&gt;Apache 2.0 (&lt;a href="https://huggingface.co/mistralai/Ministral-3-8B-Instruct-2512" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Conçu pour les petites machines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://ollama.com/library/qwen3-coder" rel="noopener noreferrer"&gt;Qwen3-Coder&lt;/a&gt; (Alibaba)&lt;/td&gt;
&lt;td&gt;30b&amp;nbsp;: 19&amp;nbsp;Go (480b&amp;nbsp;: 290&amp;nbsp;Go)&lt;/td&gt;
&lt;td&gt;256K&lt;/td&gt;
&lt;td&gt;Apache 2.0 (&lt;a href="https://huggingface.co/Qwen/Qwen3-Coder-30B-A3B-Instruct" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Code et tâches agentiques&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://ollama.com/library/qwen3.6" rel="noopener noreferrer"&gt;Qwen3.6&lt;/a&gt; (Alibaba)&lt;/td&gt;
&lt;td&gt;27b, 35b&amp;nbsp;: 18 à 23&amp;nbsp;Go&lt;/td&gt;
&lt;td&gt;256K&lt;/td&gt;
&lt;td&gt;Apache 2.0 pour le 27b (&lt;a href="https://huggingface.co/Qwen/Qwen3.6-27B" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Code agentique, selon l'éditeur&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Les «&amp;nbsp;points forts&amp;nbsp;» sont revendiqués par les éditeurs&amp;nbsp;: nous n'avons lu aucun classement indépendant qui les compare. Les modèles «&amp;nbsp;MoE&amp;nbsp;», comme gemma4:26b (25,2&amp;nbsp;milliards de paramètres dont 3,8 actifs) ou Qwen3-Coder 30b (30,5&amp;nbsp;milliards dont 3,3 actifs), activent seulement une partie de leurs poids à chaque token, mais le fichier entier doit tenir en mémoire. Les étiquettes &lt;code&gt;-mlx&lt;/code&gt; visent les Mac.&lt;/p&gt;

&lt;p&gt;Pour un premier LLM local sur 8&amp;nbsp;Go, partez de &lt;code&gt;qwen3.5:4b&lt;/code&gt; ou &lt;code&gt;ministral-3:3b&lt;/code&gt;. Sur 16&amp;nbsp;Go, &lt;code&gt;qwen3.5:9b&lt;/code&gt;. Pour le code avec 32&amp;nbsp;Go, &lt;code&gt;qwen3-coder:30b&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  L'API REST locale : curl et Python
&lt;/h2&gt;

&lt;p&gt;Ollama expose une API sur &lt;code&gt;http://localhost:11434&lt;/code&gt;, sans clé en local. Elle parle trois dialectes&amp;nbsp;: son API native sous &lt;code&gt;/api&lt;/code&gt;, une API compatible OpenAI sous &lt;code&gt;/v1&lt;/code&gt; et une API compatible Anthropic (&lt;a href="https://docs.ollama.com/api/introduction" rel="noopener noreferrer"&gt;introduction à l'API&lt;/a&gt;). Un outil prévu pour OpenAI peut donc souvent viser votre LLM local en changeant seulement l'adresse.&lt;/p&gt;

&lt;p&gt;Un appel avec curl, tiré du guide de démarrage&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:11434/api/chat &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "gemma4:e2b",
    "messages": [{"role": "user", "content": "Explique une closure en une phrase."}],
    "stream": false
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La réponse se lit dans &lt;code&gt;message.content&lt;/code&gt;. Sans &lt;code&gt;"stream": false&lt;/code&gt;, &lt;code&gt;/api/chat&lt;/code&gt; diffuse la réponse par défaut, en JSON délimité par des retours à la ligne, un objet par morceau (&lt;a href="https://docs.ollama.com/api/streaming" rel="noopener noreferrer"&gt;documentation du streaming&lt;/a&gt;). Sous Windows, cette syntaxe convient à Git Bash ou WSL&amp;nbsp;; dans PowerShell, appelez &lt;code&gt;curl.exe&lt;/code&gt; et adaptez les guillemets, ou utilisez &lt;code&gt;Invoke-RestMethod&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;En Python, la bibliothèque officielle s'installe avec &lt;code&gt;pip install ollama&lt;/code&gt; et demande Python 3.8 ou plus récent (&lt;a href="https://github.com/ollama/ollama-python" rel="noopener noreferrer"&gt;dépôt ollama-python&lt;/a&gt;)&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemma4:e2b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explique une closure en une phrase.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ici, la bibliothèque renvoie la réponse complète&amp;nbsp;; elle ne diffuse morceau par morceau qu'avec &lt;code&gt;stream=True&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Avec le client OpenAI, réglez &lt;code&gt;base_url="http://localhost:11434/v1/"&lt;/code&gt; et une clé quelconque, qu'Ollama ignore (&lt;a href="https://docs.ollama.com/api/openai-compatibility" rel="noopener noreferrer"&gt;compatibilité OpenAI&lt;/a&gt;). Cette API est la brique de base d'un &lt;a href="https://neurautech.com/rag-retrieval-augmented-generation/" rel="noopener noreferrer"&gt;RAG&lt;/a&gt; entièrement local, avec un modèle d'embeddings comme &lt;a href="https://ollama.com/library/embeddinggemma-2" rel="noopener noreferrer"&gt;embeddinggemma-2&lt;/a&gt;. Notre tutoriel &lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;API REST avec Node.js et Express&lt;/a&gt; aide à construire un service autour.&lt;/p&gt;




&lt;h2&gt;
  
  
  Coder avec un modèle local dans VS Code
&lt;/h2&gt;

&lt;p&gt;La voie officielle est l'extension Ollama pour VS Code. D'après la &lt;a href="https://docs.ollama.com/integrations/vscode" rel="noopener noreferrer"&gt;page d'intégration VS Code d'Ollama&lt;/a&gt;, consultée le 31 octobre 2026, la procédure tient en quatre gestes&amp;nbsp;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installez l'extension Ollama depuis la Marketplace de VS Code.&lt;/li&gt;
&lt;li&gt;Ouvrez le chat de VS Code.&lt;/li&gt;
&lt;li&gt;Ouvrez le sélecteur de modèle en bas de la zone de saisie.&lt;/li&gt;
&lt;li&gt;Choisissez un modèle dans la section Ollama.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Il faut VS Code 1.127 ou plus récent, et Ollama 0.17.6 ou plus récent est recommandé. Pour les modèles locaux, la page conseille de régler le contexte sur au moins 64k dans les paramètres d'Ollama, puis de recharger la fenêtre. Ce contexte consomme de la mémoire&amp;nbsp;: sur une machine de 8 à 16&amp;nbsp;Go, il peut faire sortir une partie du modèle du GPU. Vérifiez avec &lt;code&gt;ollama ps&lt;/code&gt;. La commande &lt;code&gt;ollama launch vscode&lt;/code&gt; automatise la configuration.&lt;/p&gt;

&lt;p&gt;Faut-il un compte GitHub&amp;nbsp;? Pas pour le chat&amp;nbsp;: la &lt;a href="https://code.visualstudio.com/docs/agent-customization/language-models" rel="noopener noreferrer"&gt;documentation de VS Code&lt;/a&gt; indique que les modèles locaux comme ceux d'Ollama fonctionnent sans connexion à GitHub ni abonnement Copilot. Les suggestions en ligne et la recherche sémantique exigent en revanche un abonnement, et un administrateur peut bloquer cette option en entreprise.&lt;/p&gt;

&lt;p&gt;Autre option répandue&amp;nbsp;: l'extension Continue, configurable dans &lt;code&gt;~/.continue/config.yaml&lt;/code&gt; (&lt;a href="https://docs.continue.dev/customize/deep-dives/configuration" rel="noopener noreferrer"&gt;documentation de configuration&lt;/a&gt;) avec &lt;code&gt;provider: ollama&lt;/code&gt; et un rôle par modèle, par exemple un petit modèle pour l'autocomplétion (&lt;a href="https://docs.continue.dev/guides/ollama-guide" rel="noopener noreferrer"&gt;guide Ollama de Continue&lt;/a&gt;). Ses exemples de modèles sont anciens&amp;nbsp;: prenez plutôt ceux du tableau précédent.&lt;/p&gt;

&lt;p&gt;Gardez des attentes réalistes&amp;nbsp;: nous n'avons lu aucune mesure indépendante qui compare ces modèles locaux aux assistants cloud sur du code. Notre article sur &lt;a href="https://neurautech.com/comment-utiliser-lia-pour-coder-3x-plus-vite/" rel="noopener noreferrer"&gt;l'IA pour coder plus vite&lt;/a&gt; résume ce que les études mesurent vraiment, et notre sélection d'&lt;a href="https://neurautech.com/meilleures-extensions-vs-code-en-2026-pour-devs/" rel="noopener noreferrer"&gt;extensions VS Code&lt;/a&gt; complète l'environnement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ollama, LM Studio ou llama.cpp
&lt;/h2&gt;

&lt;p&gt;Les trois outils font tourner un LLM local à partir des mêmes familles de modèles, avec des philosophies différentes. Ollama mise sur la ligne de commande et l'API, LM Studio sur l'interface graphique, llama.cpp sur le contrôle fin.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Critère&lt;/th&gt;
&lt;th&gt;Ollama&lt;/th&gt;
&lt;th&gt;LM Studio&lt;/th&gt;
&lt;th&gt;llama.cpp&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Licence&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Application gratuite, y compris au travail depuis juillet 2025&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interface&lt;/td&gt;
&lt;td&gt;Ligne de commande et application&lt;/td&gt;
&lt;td&gt;Interface graphique et commande &lt;code&gt;lms&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ligne de commande&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Systèmes&lt;/td&gt;
&lt;td&gt;Windows 10 22H2+, macOS 14+, Linux&lt;/td&gt;
&lt;td&gt;macOS 14+ sur Apple Silicon, Windows x64 avec AVX2 ou ARM, Ubuntu 20.04+&lt;/td&gt;
&lt;td&gt;macOS, Windows, Linux, nombreux GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formats&lt;/td&gt;
&lt;td&gt;Bibliothèque Ollama, import GGUF ou safetensors&lt;/td&gt;
&lt;td&gt;GGUF, et MLX sur Mac&lt;/td&gt;
&lt;td&gt;GGUF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Native, OpenAI et Anthropic sur le port 11434&lt;/td&gt;
&lt;td&gt;Compatible OpenAI et Anthropic, REST&lt;/td&gt;
&lt;td&gt;Serveur compatible OpenAI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pour qui&lt;/td&gt;
&lt;td&gt;Développeurs, intégrations&lt;/td&gt;
&lt;td&gt;Débuter sans terminal&lt;/td&gt;
&lt;td&gt;Réglages avancés, matériel exotique&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sources&amp;nbsp;: &lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;dépôt d'Ollama&lt;/a&gt;, &lt;a href="https://lmstudio.ai/docs/app" rel="noopener noreferrer"&gt;documentation de LM Studio&lt;/a&gt; et ses &lt;a href="https://lmstudio.ai/docs/app/system-requirements" rel="noopener noreferrer"&gt;prérequis&lt;/a&gt;, l'&lt;a href="https://www.lmstudio.ai/blog/free-for-work" rel="noopener noreferrer"&gt;annonce de gratuité en entreprise&lt;/a&gt; du 8 juillet 2025 et le &lt;a href="https://github.com/ggml-org/llama.cpp" rel="noopener noreferrer"&gt;dépôt de llama.cpp&lt;/a&gt;, consultés le 31 octobre 2026. LM Studio recommande 16&amp;nbsp;Go de RAM et ne prend pas en charge les Mac Intel&amp;nbsp;; nous n'avons pas trouvé de licence open source pour l'application.&lt;/p&gt;

&lt;p&gt;Le README de llama.cpp écrit désormais &lt;code&gt;llama cli -hf &amp;lt;dépôt&amp;gt;&lt;/code&gt; pour lancer un modèle de Hugging Face et &lt;code&gt;llama serve&lt;/code&gt; pour ouvrir le serveur compatible OpenAI, avec une espace&amp;nbsp;; d'autres pages du projet emploient encore &lt;code&gt;llama-server&lt;/code&gt; ou &lt;code&gt;llama-quantize&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; Ollama si vous voulez une API et des intégrations prêtes, LM Studio pour explorer à la souris, llama.cpp pour tout régler soi-même.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sécurité : le port 11434 et les modèles douteux
&lt;/h2&gt;

&lt;p&gt;L'API d'Ollama n'a pas d'authentification. Par défaut, elle n'écoute que sur &lt;code&gt;127.0.0.1:11434&lt;/code&gt;, donc seule votre machine peut l'appeler. Dès que vous réglez &lt;code&gt;OLLAMA_HOST=0.0.0.0:11434&lt;/code&gt;, tout le réseau y a accès (&lt;a href="https://docs.ollama.com/faq" rel="noopener noreferrer"&gt;FAQ d'Ollama&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;SentinelLABS et Censys ont recensé 175&amp;nbsp;108 hôtes Ollama uniques accessibles depuis Internet, dans 130 pays, sur 293&amp;nbsp;jours d'analyse. Plus de 48&amp;nbsp;% annoncent la capacité d'appeler des outils (&lt;a href="https://www.sentinelone.com/labs/silent-brothers-ollama-hosts-form-anonymous-ai-network-beyond-platform-guardrails/" rel="noopener noreferrer"&gt;SentinelOne&lt;/a&gt;, page mise à jour le 6 mars 2026).&lt;/p&gt;

&lt;p&gt;Les bons réflexes&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Restez en local.&lt;/strong&gt; Pour un accès distant, préférez un tunnel SSH à l'ouverture du port.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mettez un proxy authentifié.&lt;/strong&gt; Wiz, qui a découvert la faille CVE-2024-37032 corrigée dans la version 0.1.34, recommande un proxy inverse avec authentification (&lt;a href="https://www.wiz.io/blog/probllama-ollama-vulnerability-cve-2024-37032" rel="noopener noreferrer"&gt;Wiz&lt;/a&gt;, 24 juin 2024). Notre tutoriel &lt;a href="https://neurautech.com/nginx-reverse-proxy-docker-compose/" rel="noopener noreferrer"&gt;Nginx reverse proxy avec Docker Compose&lt;/a&gt; montre la mise en place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attention à Docker.&lt;/strong&gt; Wiz note qu'avec Docker, l'API est exposée publiquement. Sans adresse, &lt;code&gt;-p 11434:11434&lt;/code&gt; publie le port sur toutes les interfaces&amp;nbsp;; écrivez &lt;code&gt;-p 127.0.0.1:11434:11434&lt;/code&gt; pour le réserver à la machine (&lt;a href="https://docs.docker.com/engine/network/port-publishing/" rel="noopener noreferrer"&gt;documentation Docker sur les ports&lt;/a&gt;). Docker précise aussi que ses ports publiés contournent ufw (&lt;a href="https://docs.docker.com/engine/network/packet-filtering-firewalls/" rel="noopener noreferrer"&gt;pare-feu et Docker&lt;/a&gt;). Notre guide &lt;a href="https://neurautech.com/docker-pour-debutants/" rel="noopener noreferrer"&gt;Docker pour débutants&lt;/a&gt; explique la publication des ports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mettez à jour.&lt;/strong&gt; La faille de 2024 permettait d'écrire des fichiers arbitraires via un registre malveillant, et celle de 2026, corrigée dans la 0.17.1, de lire la mémoire du serveur avec un fichier GGUF piégé.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les modèles eux-mêmes sont un second risque. Ollama et llama.cpp chargent du GGUF, et Ollama sait importer du safetensors (&lt;a href="https://docs.ollama.com/import" rel="noopener noreferrer"&gt;documentation d'import&lt;/a&gt;)&amp;nbsp;: pas de pickle, ce format de PyTorch qui peut exécuter du code au chargement (&lt;a href="https://huggingface.co/docs/hub/security-pickle" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;). Le GGUF n'est pas sans risque pour autant&amp;nbsp;: la faille CVE-2026-7482, corrigée dans Ollama 0.17.1, permettait à un fichier GGUF piégé envoyé à &lt;code&gt;/api/create&lt;/code&gt; de lire la mémoire du serveur (&lt;a href="https://www.cve.org/CVERecord?id=CVE-2026-7482" rel="noopener noreferrer"&gt;fiche CVE&lt;/a&gt;, publiée le 4 mai 2026). Téléchargez depuis la bibliothèque officielle ou des organisations connues.&lt;/p&gt;

&lt;p&gt;Enfin, un LLM local relié à des outils hérite des risques d'injection de prompt. Si vous lui donnez accès à vos fichiers, relisez nos articles sur les &lt;a href="https://neurautech.com/agent-ia-quest-ce-que-cest-et-comment-ca-marche/" rel="noopener noreferrer"&gt;agents IA&lt;/a&gt; et le &lt;a href="https://neurautech.com/serveur-mcp/" rel="noopener noreferrer"&gt;serveur MCP&lt;/a&gt;, qui détaillent les garde-fous.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Cet article est une synthèse documentaire. Les points suivants restent ouverts&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L'exécution.&lt;/strong&gt; Ollama n'est pas installé sur notre poste de rédaction&amp;nbsp;: aucune commande ni aucun exemple de code n'a été exécuté. Ils sont repris ou adaptés de la documentation officielle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les performances.&lt;/strong&gt; Nous n'avons mesuré ni vitesse ni qualité, et nous ne citons aucun classement&amp;nbsp;: les débits en tokens par seconde publiés ailleurs dépendent trop du matériel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La perte de qualité due à la quantification sur les modèles récents.&lt;/strong&gt; Les chiffres de llama.cpp portent sur Llama 3 8B&amp;nbsp;; nous n'en avons pas trouvé pour Gemma 4 ou Qwen3.5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La licence de qwen3.6:35b.&lt;/strong&gt; Nous l'avons lue sur la fiche du modèle 27b, pas sur celle du 35b.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La part de mémoire unifiée utilisable par le GPU.&lt;/strong&gt; Apple ne publie pas de pourcentage&amp;nbsp;; nous n'en citons pas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le pilote NVIDIA minimal.&lt;/strong&gt; La page Windows indique 551.61, la page GPU 550&amp;nbsp;: nous retenons la plus exigeante pour Windows.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quel matériel faut-il pour un LLM local ?
&lt;/h3&gt;

&lt;p&gt;Le fichier du modèle doit tenir dans la mémoire disponible, avec une marge. Le guide d'Ollama recommande 8&amp;nbsp;Go de VRAM ou de mémoire unifiée pour son modèle d'exemple de 7,2&amp;nbsp;Go, et OpenAI indique que gpt-oss:20b fonctionne avec 16&amp;nbsp;Go de mémoire. Selon notre lecture des tailles affichées par Ollama, 16&amp;nbsp;Go accueillent les modèles quantifiés de 4 à 14&amp;nbsp;milliards de paramètres, et gpt-oss:20b à la limite.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ollama est-il gratuit et open source ?
&lt;/h3&gt;

&lt;p&gt;Oui&amp;nbsp;: Ollama est publié sous licence MIT sur &lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; et l'usage local ne coûte rien. Les modèles ont leur propre licence&amp;nbsp;: Gemma 4, Qwen3.5, gpt-oss et Ministral 3 sont sous Apache 2.0, qui autorise l'usage commercial. Seuls les modèles &lt;code&gt;:cloud&lt;/code&gt; passent par les serveurs d'Ollama.&lt;/p&gt;

&lt;h3&gt;
  
  
  Peut-on faire tourner un LLM local sur un Mac ?
&lt;/h3&gt;

&lt;p&gt;Oui, sur macOS Sonoma ou plus récent. Les puces Apple M utilisent le GPU via Metal et leur mémoire unifiée, alors que les Mac Intel tournent sur le processeur seul (&lt;a href="https://docs.ollama.com/macos" rel="noopener noreferrer"&gt;documentation d'Ollama&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment appeler Ollama depuis du code ?
&lt;/h3&gt;

&lt;p&gt;Par l'API REST sur &lt;code&gt;http://localhost:11434&lt;/code&gt;&amp;nbsp;: &lt;code&gt;/api/chat&lt;/code&gt; pour l'API native, &lt;code&gt;/v1/chat/completions&lt;/code&gt; pour la compatibilité OpenAI. En Python, &lt;code&gt;pip install ollama&lt;/code&gt; installe la bibliothèque officielle. Aucune clé n'est demandée en local, d'où l'importance de ne pas exposer le port.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse fondée exclusivement sur des sources publiques consultées le 31 octobre 2026&amp;nbsp;: documentation et dépôts d'Ollama, de LM Studio, de llama.cpp et de Continue, pages de modèles d'Ollama et de Hugging Face, documentation GGUF de Hugging Face, mesures de llama-quantize, documentations d'Apple, de Docker et de VS Code, fiche CVE, recherches de SentinelLABS, Censys et Wiz. Les avantages revendiqués par les éditeurs sont signalés comme tels. Les citations issues de sources en anglais sont traduites par nos soins. Ollama n'était pas installé sur le poste de rédaction&amp;nbsp;: aucun test ni aucune exécution n'a été mené. Aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux éditeurs mentionnés.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou une version périmée&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 31 octobre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Clint Patterson (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ollama</category>
      <category>llm</category>
      <category>opensource</category>
      <category>intelligenceartificielle</category>
    </item>
    <item>
      <title>Gestionnaire de mot de passe gratuit : 6 options comparées honnêtement en 2026</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sat, 31 Oct 2026 13:42:54 +0000</pubDate>
      <link>https://dev.to/neurau_tech/gestionnaire-de-mot-de-passe-gratuit-6-options-comparees-honnetement-en-2026-441c</link>
      <guid>https://dev.to/neurau_tech/gestionnaire-de-mot-de-passe-gratuit-6-options-comparees-honnetement-en-2026-441c</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Un gestionnaire de mot de passe gratuit suffit à la plupart des particuliers en 2026, s'il couvre tous vos appareils. Bitwarden et Proton Pass synchronisent sans limite d'appareils, KeePassXC garde tout en local, et les gestionnaires de Google, d'Apple et de Microsoft sont sans doute déjà installés chez vous.&lt;/p&gt;

&lt;p&gt;Nous comparons ces six options sur leurs limites, la synchronisation, les clés d'accès, le partage, les alertes, le code et le chiffrement. Les faits viennent surtout des pages des éditeurs et des autorités françaises, complétées par l'ICO britannique, des chercheurs de l'ETH Zurich, Privacy Guides et la presse, relus les 30 et 31 octobre 2026. Aucun test maison, aucun lien affilié.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bitwarden Free et Proton Pass Free offrent mots de passe et appareils illimités. Bitwarden partage avec une personne, Proton Pass Free partage 2 coffres, chacun avec 2 autres personnes.&lt;/li&gt;
&lt;li&gt;KeePassXC 2.7.9 a obtenu en novembre 2025 une certification de premier niveau de l'ANSSI sous Windows 10, valable jusqu'en novembre 2028.&lt;/li&gt;
&lt;li&gt;Parmi les gestionnaires intégrés, seul Apple chiffre par défaut vos mots de passe de bout en bout. Chez Google, c'est une option.&lt;/li&gt;
&lt;li&gt;L'ANSSI recommande un coffre-fort de mots de passe et un mot de passe différent par service, et déconseille le SMS comme second facteur.&lt;/li&gt;
&lt;li&gt;Un export de migration est le plus souvent un fichier en clair&amp;nbsp;: supprimez-le dès l'import terminé.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Pourquoi un gestionnaire plutôt que votre mémoire
&lt;/h2&gt;

&lt;p&gt;Un mot de passe unique et robuste par service est impossible à retenir de tête&amp;nbsp;: c'est le problème que règle un gestionnaire. Le guide de l'ANSSI recommande aux utilisateurs un mot de passe différent pour chaque service (R33), un coffre-fort de mots de passe pour les générer (R34), et aucun stockage dans un fichier ou un courriel (R35) (&lt;a href="https://messervices.cyber.gouv.fr/guides/recommandations-relatives-lauthentification-multifacteur-et-aux-mots-de-passe" rel="noopener noreferrer"&gt;guide de l'ANSSI, version 2.0 du 8 octobre 2021&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Le même guide précise que les alternatives comme un fichier bureautique protégé par un mot de passe «&amp;nbsp;n'apportent pas du tout le même niveau de protection&amp;nbsp;». Cybermalveillance.gouv.fr, dans sa fiche mise à jour le 5 octobre 2026, conseille un gestionnaire sécurisé pour n'avoir plus qu'un mot de passe à retenir (&lt;a href="https://www.cybermalveillance.gouv.fr/tous-nos-contenus/bonnes-pratiques/mots-de-passe" rel="noopener noreferrer"&gt;Cybermalveillance.gouv.fr&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Le gestionnaire aide aussi contre l'hameçonnage&amp;nbsp;: en principe, il ne propose un mot de passe que sur le domaine enregistré, protection qui disparaît si vous le copiez-collez à la main. Notre guide pour &lt;a href="https://neurautech.com/phishing-comment-reconnaitre-et-eviter-les-arnaques/" rel="noopener noreferrer"&gt;reconnaître un phishing&lt;/a&gt; détaille les autres indices.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gestionnaire de mot de passe gratuit : le tableau comparatif
&lt;/h2&gt;

&lt;p&gt;Aucun gestionnaire de mot de passe gratuit ne coche toutes les cases&amp;nbsp;: chacun arbitre entre synchronisation, contrôle et confort.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offre gratuite&lt;/th&gt;
&lt;th&gt;Plateformes&lt;/th&gt;
&lt;th&gt;Synchronisation&lt;/th&gt;
&lt;th&gt;Partage&lt;/th&gt;
&lt;th&gt;Code source&lt;/th&gt;
&lt;th&gt;Éditeur&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bitwarden Free&lt;/td&gt;
&lt;td&gt;Navigateurs, ordinateur, mobile, web&lt;/td&gt;
&lt;td&gt;Appareils illimités, serveurs aux États-Unis ou dans l'UE&lt;/td&gt;
&lt;td&gt;1 autre personne&lt;/td&gt;
&lt;td&gt;Ouvert&lt;/td&gt;
&lt;td&gt;Bitwarden, Inc. (Californie)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proton Pass Free&lt;/td&gt;
&lt;td&gt;Navigateurs, ordinateur, mobile&lt;/td&gt;
&lt;td&gt;Appareils illimités&lt;/td&gt;
&lt;td&gt;2 coffres, 2 personnes par coffre&lt;/td&gt;
&lt;td&gt;Ouvert&lt;/td&gt;
&lt;td&gt;Proton AG (Suisse)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KeePassXC&lt;/td&gt;
&lt;td&gt;Windows, macOS, Linux&lt;/td&gt;
&lt;td&gt;Aucune, fichier à synchroniser soi-même&lt;/td&gt;
&lt;td&gt;Via KeeShare&lt;/td&gt;
&lt;td&gt;Ouvert (GPLv3)&lt;/td&gt;
&lt;td&gt;Équipe KeePassXC (Allemagne)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google&lt;/td&gt;
&lt;td&gt;Chrome, Android, iOS avec Chrome&lt;/td&gt;
&lt;td&gt;Appareils du compte Google&lt;/td&gt;
&lt;td&gt;Groupe familial&lt;/td&gt;
&lt;td&gt;Fermé&lt;/td&gt;
&lt;td&gt;Google&amp;nbsp;; Google Ireland pour l'UE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple Mots de passe&lt;/td&gt;
&lt;td&gt;iOS 18, macOS Sequoia, Windows via iCloud&lt;/td&gt;
&lt;td&gt;Appareils Apple et PC Windows&lt;/td&gt;
&lt;td&gt;Contacts sur iOS 17, macOS 14 ou plus&lt;/td&gt;
&lt;td&gt;Fermé&lt;/td&gt;
&lt;td&gt;Apple&amp;nbsp;; Apple Distribution International (Irlande) pour l'UE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Edge&lt;/td&gt;
&lt;td&gt;Navigateur Edge&lt;/td&gt;
&lt;td&gt;Avec un compte Microsoft&lt;/td&gt;
&lt;td&gt;Non vérifié&lt;/td&gt;
&lt;td&gt;Fermé&lt;/td&gt;
&lt;td&gt;Microsoft&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4e9ylrwzh0vpg0of1r0t.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4e9ylrwzh0vpg0of1r0t.webp" alt="Grille comparative&amp;nbsp;: ce que chaque gestionnaire de mot de passe gratuit inclut en octobre 2026, de Bitwarden Free à Microsoft Edge, avec LastPass Free en contraste, sur la synchronisation, les clés d'accès, le partage, les alertes, le code ouvert et le chiffrement" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La grille ajoute LastPass Free en contraste, sans le compter parmi les six. Les éditeurs viennent des politiques de confidentialité de &lt;a href="https://bitwarden.com/privacy/" rel="noopener noreferrer"&gt;Bitwarden&lt;/a&gt;, de &lt;a href="https://policies.google.com/privacy?hl=fr" rel="noopener noreferrer"&gt;Google&lt;/a&gt; et d'&lt;a href="https://www.apple.com/fr/legal/privacy/fr-ww/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, et du &lt;a href="https://messervices.cyber.gouv.fr/visas/ANSSI-CSPN-2025-16-rapport.pdf" rel="noopener noreferrer"&gt;rapport de l'ANSSI&lt;/a&gt; pour KeePassXC.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bitwarden Free : synchronisation illimitée et audits publiés
&lt;/h2&gt;

&lt;p&gt;Bitwarden Free annonce appareils et mots de passe illimités, gestion des clés d'accès, export chiffré et partage gratuit avec un autre utilisateur (&lt;a href="https://bitwarden.com/pricing/" rel="noopener noreferrer"&gt;tarifs de Bitwarden&lt;/a&gt;, consultés le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Les limites&amp;nbsp;: partage via une organisation gratuite de 2 utilisateurs et deux collections au plus&amp;nbsp;; double authentification par clé de sécurité, application ou e-mail. Les pièces jointes, l'authentificateur intégré et l'accès d'urgence sont réservés à Premium (&lt;a href="https://bitwarden.com/help/password-manager-plans/" rel="noopener noreferrer"&gt;documentation des offres&lt;/a&gt;). La plupart des rapports de santé du coffre aussi, mais le rapport Data Breach, qui interroge Have I Been Pwned, est «&amp;nbsp;gratuit pour tous&amp;nbsp;» (&lt;a href="https://bitwarden.com/help/reports/" rel="noopener noreferrer"&gt;rapports de Bitwarden&lt;/a&gt;, consulté le 31 octobre 2026).&lt;/p&gt;

&lt;p&gt;Bitwarden décrit un chiffrement de bout en bout AES-CBC 256 bits. La clé est dérivée du mot de passe maître par PBKDF2 avec 600&amp;nbsp;000 itérations par défaut, ou par Argon2id sur option (&lt;a href="https://bitwarden.com/help/bitwarden-security-white-paper/" rel="noopener noreferrer"&gt;livre blanc de sécurité&lt;/a&gt;). La page des audits liste Cure53 chaque année depuis 2021 (&lt;a href="https://bitwarden.com/help/is-bitwarden-audited/" rel="noopener noreferrer"&gt;page des audits&lt;/a&gt;). Le service propose une région États-Unis et une région Union européenne, à choisir à l'inscription (&lt;a href="https://bitwarden.com/help/server-geographies/" rel="noopener noreferrer"&gt;régions de serveurs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;En février 2026, des chercheurs de l'ETH Zurich ont publié 12 attaques contre Bitwarden en supposant un serveur entièrement compromis (&lt;a href="https://ethz.ch/en/news-and-events/eth-news/news/2026/02/password-managers-less-secure-than-promised.html" rel="noopener noreferrer"&gt;ETH Zurich&lt;/a&gt;, 16 février 2026). Bitwarden indique avoir corrigé ou commencé à corriger sept problèmes et en avoir accepté trois comme choix de conception (&lt;a href="https://bitwarden.com/blog/security-through-transparency-eth-zurich-audits-bitwarden-cryptography/" rel="noopener noreferrer"&gt;Bitwarden&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; le bon point de départ si vous mélangez Windows, Android, iPhone et plusieurs navigateurs. Proton Pass l'emporte si vous voulez des alias e-mail ou partager un coffre à trois.&lt;/p&gt;




&lt;h2&gt;
  
  
  Proton Pass Free : la confidentialité, avec deux coffres
&lt;/h2&gt;

&lt;p&gt;Proton Pass Free offre appareils et identifiants illimités, clés d'accès, alertes sur les mots de passe faibles et réutilisés, et 10 alias pour ne pas donner votre vraie adresse e-mail (&lt;a href="https://proton.me/pass/pricing" rel="noopener noreferrer"&gt;tarifs de Proton Pass&lt;/a&gt;, consultés le 30 octobre 2026). La surveillance du dark web et l'authentificateur intégré sont réservés à Pass Plus.&lt;/p&gt;

&lt;p&gt;Le partage passe par les coffres&amp;nbsp;: en version gratuite, vous pouvez créer et partager jusqu'à 2 coffres, avec 3 personnes au plus par coffre en vous comptant, et chaque invité doit avoir un compte Proton Pass (&lt;a href="https://proton.me/support/pass-browser-share" rel="noopener noreferrer"&gt;aide Proton&lt;/a&gt;, consultée le 31 octobre 2026).&lt;/p&gt;

&lt;p&gt;Selon Proton, chaque coffre est chiffré de bout en bout (AES-GCM 256 bits), métadonnées comprises. Les applications sont open source. Proton AG est installé à Genève et met en avant une juridiction hors des États-Unis et de l'UE&amp;nbsp;; le droit suisse s'applique (&lt;a href="https://proton.me/pass/security" rel="noopener noreferrer"&gt;sécurité de Proton Pass&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Le dernier audit public trouvé, par Cure53, date de 2023&amp;nbsp;: un seul problème de gravité moyenne est resté ouvert, lié à Android (&lt;a href="https://proton.me/blog/pass-open-source-security-audit" rel="noopener noreferrer"&gt;blog de Proton&lt;/a&gt;, 19 juillet 2023).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; idéal avec Proton Mail, ou pour des alias e-mail.&lt;/p&gt;




&lt;h2&gt;
  
  
  KeePassXC, KeePass et KeePassDX : le local certifié
&lt;/h2&gt;

&lt;p&gt;KeePassXC est la seule option de ce comparatif dont la certification ANSSI est en cours de validité. Open source sous GPLv3, il tourne sous Windows, macOS et Linux sans stocker de données à distance. La version 2.8.0 est sortie le 21 octobre 2026 (&lt;a href="https://keepassxc.org/" rel="noopener noreferrer"&gt;KeePassXC&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Le rapport ANSSI-CSPN-2025/16 du 17 novembre 2025 porte sur la version 2.7.9, évaluée par Synacktiv, et vaut pour Windows 10 jusqu'au 17 novembre 2028 (&lt;a href="https://keepassxc.org/audits/" rel="noopener noreferrer"&gt;page des audits du projet&lt;/a&gt;). L'ANSSI rappelle que la certification «&amp;nbsp;ne constitue pas en soi une recommandation du produit&amp;nbsp;» (&lt;a href="https://messervices.cyber.gouv.fr/visas/ANSSI-CSPN-2025-16-rapport.pdf" rel="noopener noreferrer"&gt;rapport de certification&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;La base est un fichier .kdbx chiffré en AES-256 ou Twofish selon la &lt;a href="https://keepassxc.org/docs/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. Le guide utilisateur recommande le format KDBX 4 avec Argon2, et décrit les clés d'accès via l'extension KeePassXC-Browser (&lt;a href="https://keepassxc.org/docs/KeePassXC_UserGuide" rel="noopener noreferrer"&gt;guide utilisateur&lt;/a&gt;). Depuis la version 2.6.0, des rapports vérifient la santé des mots de passe hors ligne et, à la demande, leur présence dans Have I Been Pwned (&lt;a href="https://keepassxc.org/blog/2020-07-07-2.6.0-released/" rel="noopener noreferrer"&gt;KeePassXC 2.6.0&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;KeePassXC n'a pas d'application mobile&amp;nbsp;: sa &lt;a href="https://keepassxc.org/docs/" rel="noopener noreferrer"&gt;page de documentation&lt;/a&gt; renvoie vers des applications tierces compatibles .kdbx, sur Android comme sur iPhone, que nous n'avons pas évaluées. Sur Android, &lt;a href="https://github.com/Kunzisoft/KeePassDX" rel="noopener noreferrer"&gt;KeePassDX&lt;/a&gt;, sous GPLv3, gère aussi ChaCha20 et les clés d'accès. Le &lt;a href="https://keepass.info/" rel="noopener noreferrer"&gt;KeePass&lt;/a&gt; d'origine, version 2.61.1 du 1er mai 2026, vise d'abord Windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; pour un poste sensible ou qui refuse tout cloud.&lt;/p&gt;




&lt;h2&gt;
  
  
  Google, Apple et Edge : les gestionnaires intégrés
&lt;/h2&gt;

&lt;p&gt;Gratuits et déjà installés, ils valent mieux que des mots de passe réutilisés, mais vous attachent à un écosystème, avec un code fermé.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gestionnaire de mots de passe de Google
&lt;/h3&gt;

&lt;p&gt;Il est intégré à Chrome et à Android. Sa fonction Check-up signale les mots de passe compromis (&lt;a href="https://passwords.google/" rel="noopener noreferrer"&gt;Google Password Manager&lt;/a&gt;, consulté le 30 octobre 2026). Les clés d'accès y sont synchronisées et chiffrées de bout en bout, sur Android et dans Chrome sous Windows, macOS, Linux et ChromeOS (&lt;a href="https://developers.google.com/identity/passkeys/supported-environments" rel="noopener noreferrer"&gt;documentation développeurs de Google&lt;/a&gt;, mise à jour le 19 mai 2025).&lt;/p&gt;

&lt;p&gt;Le partage est réservé à votre groupe familial, dont les membres reçoivent une copie du mot de passe (&lt;a href="https://www.clubic.com/actualite-528114-google-deploie-le-partage-de-mots-de-passe-en-famille.html" rel="noopener noreferrer"&gt;Clubic&lt;/a&gt;, 28 mai 2024). Pour les mots de passe, Google a lancé en 2022 un chiffrement sur l'appareil, avant envoi à ses serveurs (&lt;a href="https://blog.google/innovation-and-ai/technology/safety-security/security-myth-busting-and-spring-cleaning/" rel="noopener noreferrer"&gt;blog de Google&lt;/a&gt;, 5 mai 2022). Selon &lt;a href="https://www.privacyguides.org/en/passwords/" rel="noopener noreferrer"&gt;Privacy Guides&lt;/a&gt;, ce chiffrement de bout en bout reste optionnel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apple Mots de passe
&lt;/h3&gt;

&lt;p&gt;L'application est disponible depuis iOS 18 et macOS Sequoia, et sur Windows via iCloud pour Windows. Elle gère les clés d'accès, les codes de validation, et alerte sur les mots de passe faibles, réutilisés ou apparus dans des fuites (&lt;a href="https://support.apple.com/fr-fr/120758" rel="noopener noreferrer"&gt;assistance Apple&lt;/a&gt;). Apple classe «&amp;nbsp;Mots de passe et Trousseau&amp;nbsp;» parmi les données chiffrées de bout en bout dès la protection standard (&lt;a href="https://support.apple.com/fr-fr/102651" rel="noopener noreferrer"&gt;sécurité des données iCloud&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Le partage en groupe est réservé à des personnes de vos contacts équipées d'un appareil sous iOS 17, iPadOS 17, macOS 14 ou plus récent (&lt;a href="https://support.apple.com/guide/passwords/share-with-a-group-mchlc00a3602/mac" rel="noopener noreferrer"&gt;guide Mots de passe d'Apple&lt;/a&gt;)&amp;nbsp;: autrement dit, entre utilisateurs Apple. La page d'Apple ne mentionne pas Android.&lt;/p&gt;

&lt;h3&gt;
  
  
  Microsoft Edge
&lt;/h3&gt;

&lt;p&gt;Edge chiffre les mots de passe sur le disque en AES, avec une clé gardée par le système (DPAPI sous Windows). Microsoft précise qu'un logiciel malveillant actif dans votre session peut les lire. Avant synchronisation, les mots de passe sont chiffrés sur l'appareil (&lt;a href="https://learn.microsoft.com/en-us/deployedge/microsoft-edge-security-password-manager-security" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;, mis à jour le 15 juin 2026), mais la page ne parle pas de chiffrement de bout en bout, et Privacy Guides estime qu'Edge n'en offre pas (&lt;a href="https://www.privacyguides.org/en/passwords/" rel="noopener noreferrer"&gt;Privacy Guides&lt;/a&gt;, 12 septembre 2025).&lt;/p&gt;

&lt;p&gt;La Surveillance de mot de passe compare vos identifiants à une base de fuites si vous êtes connecté avec un compte Microsoft (&lt;a href="https://support.microsoft.com/fr-fr/edge/protect-your-online-accounts-using-password-monitor" rel="noopener noreferrer"&gt;assistance Microsoft&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict&amp;nbsp;:&lt;/strong&gt; un bon début dans un seul écosystème&amp;nbsp;; entre iPhone et PC Windows, ou Android et Mac, préférez un gestionnaire indépendant.&lt;/p&gt;




&lt;h2&gt;
  
  
  LastPass Free et les autres offres limitées
&lt;/h2&gt;

&lt;p&gt;LastPass Free, montré en contraste, fonctionne sur un seul type d'appareil, ordinateur ou mobile, et ne partage qu'un mot de passe avec une personne à la fois (&lt;a href="https://www.lastpass.com/pricing" rel="noopener noreferrer"&gt;tarifs de LastPass&lt;/a&gt;, consultés le 30 octobre 2026). Les pages de NordPass ont refusé notre accès&amp;nbsp;: nous ne citons pas ses limites.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que l'incident LastPass enseigne
&lt;/h2&gt;

&lt;p&gt;Selon LastPass, un attaquant a volé du code source en août 2022, puis s'en est servi pour copier une sauvegarde des coffres clients, annoncée le 22 décembre 2022. Les champs sensibles restaient chiffrés en AES 256 bits, mais les adresses des sites ne l'étaient pas. LastPass prévenait que l'attaquant pouvait tenter de deviner les mots de passe maîtres par force brute (&lt;a href="https://blog.lastpass.com/posts/notice-of-recent-security-incident" rel="noopener noreferrer"&gt;avis de LastPass&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Le 11 décembre 2025, l'ICO, régulateur britannique, a infligé 1,2&amp;nbsp;million de livres d'amende à LastPass UK Ltd pour une fuite touchant jusqu'à 1,6&amp;nbsp;million d'utilisateurs britanniques. Son enquête n'a trouvé «&amp;nbsp;aucune preuve&amp;nbsp;» que l'attaquant ait pu déchiffrer les mots de passe (&lt;a href="https://ico.org.uk/about-the-ico/media-centre/news-and-blogs/2025/12/password-manager-provider-fined" rel="noopener noreferrer"&gt;ICO&lt;/a&gt;, notre traduction).&lt;/p&gt;

&lt;p&gt;Trois leçons pratiques&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Un coffre volé peut être attaqué hors ligne, sans limite de temps.&lt;/strong&gt; Seule la robustesse du mot de passe maître le protège alors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les métadonnées comptent&lt;/strong&gt;&amp;nbsp;: des adresses de sites en clair révèlent où vous avez un compte, de quoi préparer un hameçonnage ciblé.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prévoyez de changer vite vos mots de passe importants.&lt;/strong&gt; Notre panorama des &lt;a href="https://neurautech.com/cyberattaques-les-plus-frequentes-comment-proteger/" rel="noopener noreferrer"&gt;cyberattaques les plus fréquentes&lt;/a&gt; remet ce risque en contexte.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quel gestionnaire choisir selon votre profil
&lt;/h2&gt;

&lt;p&gt;Le bon choix dépend de vos appareils&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows, Android et iPhone mélangés&amp;nbsp;:&lt;/strong&gt; Bitwarden Free, appareils illimités et audits annuels publiés.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Masquer votre adresse e-mail ou partager un coffre à trois&amp;nbsp;:&lt;/strong&gt; Proton Pass Free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aucun cloud, ou des données sensibles&amp;nbsp;:&lt;/strong&gt; KeePassXC sur ordinateur, KeePassDX sur Android.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100&amp;nbsp;% Apple&amp;nbsp;:&lt;/strong&gt; l'application Mots de passe, chiffrée de bout en bout par défaut.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100&amp;nbsp;% Android et Chrome&amp;nbsp;:&lt;/strong&gt; Google, en activant le chiffrement sur l'appareil.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beaucoup de partage en famille&amp;nbsp;:&lt;/strong&gt; les offres familiales payantes, dont les prix figurent plus bas.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Migrer sans exposer vos mots de passe
&lt;/h2&gt;

&lt;p&gt;Le moment le plus risqué d'une migration est le fichier d'export, le plus souvent un CSV ou un JSON en clair. Bitwarden propose un export chiffré, mais un import vers un autre outil passe en général par un format lisible. Le guide de KeePassXC rappelle que les exports CSV, Bitwarden ou Proton Pass qu'il importe ne sont pas chiffrés et doivent être supprimés ensuite (&lt;a href="https://keepassxc.org/docs/KeePassXC_UserGuide" rel="noopener noreferrer"&gt;guide utilisateur de KeePassXC&lt;/a&gt;).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Exportez&lt;/strong&gt; sur un ordinateur à jour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Importez aussitôt.&lt;/strong&gt; Bitwarden accepte jusqu'à 40&amp;nbsp;000 éléments par fichier, et une organisation gratuite limitée à deux collections peut faire échouer l'import (&lt;a href="https://bitwarden.com/help/import-data/" rel="noopener noreferrer"&gt;aide à l'import de Bitwarden&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vérifiez quelques entrées.&lt;/strong&gt; &lt;a href="https://www.privacyguides.org/en/passwords/" rel="noopener noreferrer"&gt;Privacy Guides&lt;/a&gt; signale que les exports CSV de KeePassXC peuvent perdre des données.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supprimez le fichier&lt;/strong&gt; et videz la corbeille.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Désactivez l'enregistrement dans le navigateur&lt;/strong&gt; pour éviter deux coffres concurrents.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Les clés d'accès se migrent mal par fichier. La FIDO Alliance prépare un protocole d'échange, le Credential Exchange Protocol&amp;nbsp;; la version consultée est un brouillon d'octobre 2024 (&lt;a href="https://fidoalliance.org/specs/cx/cxp-v1.0-wd-20241003.html" rel="noopener noreferrer"&gt;FIDO Alliance&lt;/a&gt;). Notre article sur les &lt;a href="https://neurautech.com/passkeys/" rel="noopener noreferrer"&gt;passkeys&lt;/a&gt; explique leur fonctionnement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mot de passe maître et double authentification
&lt;/h2&gt;

&lt;p&gt;Le mot de passe maître ouvre tout le coffre&amp;nbsp;: l'ANSSI le qualifie de «&amp;nbsp;très critique&amp;nbsp;», et recommande de ne pas recevoir de facteur d'authentification par SMS (R8) (&lt;a href="https://messervices.cyber.gouv.fr/guides/recommandations-relatives-lauthentification-multifacteur-et-aux-mots-de-passe" rel="noopener noreferrer"&gt;guide de l'ANSSI&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;La CNIL fixe un minimum de 80 bits d'entropie pour un mot de passe seul&amp;nbsp;: 12&amp;nbsp;caractères des quatre types, 14&amp;nbsp;caractères sans caractère spécial obligatoire, ou une phrase de passe d'au moins 7&amp;nbsp;mots (&lt;a href="https://www.cnil.fr/fr/mots-de-passe-recommandations-pour-maitriser-sa-securite" rel="noopener noreferrer"&gt;CNIL&lt;/a&gt;, 14 octobre 2022).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choisissez une phrase de passe&lt;/strong&gt; de 7&amp;nbsp;mots ou plus, utilisée nulle part ailleurs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Activez la double authentification&lt;/strong&gt; avec une application ou une clé de sécurité plutôt qu'un SMS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notez votre code de secours sur papier&lt;/strong&gt;, rangé chez vous.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protégez votre messagerie&lt;/strong&gt;&amp;nbsp;: l'ANSSI rappelle que sa compromission ouvre les comptes qui y sont liés (R36).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si un escroc vous a déjà soutiré un mot de passe ou un code, notre guide sur l'&lt;a href="https://neurautech.com/arnaque-en-ligne/" rel="noopener noreferrer"&gt;arnaque en ligne&lt;/a&gt; détaille les démarches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que coûte le passage au payant
&lt;/h2&gt;

&lt;p&gt;Le payant reste bon marché, mais il n'est pas indispensable seul. Bitwarden Premium coûte 19,80&amp;nbsp;$ par an et Families 47,88&amp;nbsp;$ pour 6 utilisateurs, hors taxes (&lt;a href="https://bitwarden.com/pricing/" rel="noopener noreferrer"&gt;tarifs de Bitwarden&lt;/a&gt;). Proton Pass Plus coûte 35,88&amp;nbsp;$ par an et Pass Family 59,88&amp;nbsp;$ pour 6 comptes (&lt;a href="https://proton.me/pass/pricing" rel="noopener noreferrer"&gt;tarifs de Proton Pass&lt;/a&gt;). Prix relevés en dollars le 30 octobre 2026.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv145ggmh2fnc9xe1af8r.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv145ggmh2fnc9xe1af8r.webp" alt="Graphique en barres&amp;nbsp;: prix annuel affiché de Bitwarden Premium, Proton Pass Plus, Bitwarden Families et Proton Pass Family au 30 octobre 2026" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Payer se justifie pour les rapports de Bitwarden Premium, la surveillance du dark web de Proton, l'accès d'urgence ou un partage familial large. Comme pour un &lt;a href="https://neurautech.com/vpn-gratuit-vs-payant-lequel-choisir-honnete-2026/" rel="noopener noreferrer"&gt;VPN gratuit ou payant&lt;/a&gt;, le gratuit convient quand le modèle économique est clair.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les limites de NordPass Free et de RoboForm Free&lt;/strong&gt;, dont nous n'avons pas pu lire les pages officielles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les clés d'accès et le partage dans Edge&lt;/strong&gt;, et qui détient sa clé de synchronisation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Un audit de Proton Pass postérieur à 2023.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La certification de KeePassXC 2.8.0&lt;/strong&gt;&amp;nbsp;: elle ne couvre que la version 2.7.9.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les prix en euros&lt;/strong&gt;&amp;nbsp;: les pages consultées affichaient des dollars.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quel est le meilleur gestionnaire de mot de passe gratuit ?
&lt;/h3&gt;

&lt;p&gt;Il n'y en a pas un seul. Bitwarden Free et Proton Pass Free synchronisent sans limite d'appareils, et KeePassXC a une certification de l'&lt;a href="https://keepassxc.org/audits/" rel="noopener noreferrer"&gt;ANSSI&lt;/a&gt; valable pour sa version 2.7.9. Le meilleur gestionnaire de mot de passe gratuit est celui qui couvre tous vos appareils.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le gestionnaire de mots de passe de Google est-il sûr ?
&lt;/h3&gt;

&lt;p&gt;Il est bien plus sûr que des mots de passe réutilisés, et ses clés d'accès sont chiffrées de bout en bout. Pour les mots de passe, ce chiffrement est une option selon &lt;a href="https://www.privacyguides.org/en/passwords/" rel="noopener noreferrer"&gt;Privacy Guides&lt;/a&gt;, alors qu'Apple l'applique par défaut.&lt;/p&gt;

&lt;h3&gt;
  
  
  Un gestionnaire de mot de passe gratuit peut-il être piraté ?
&lt;/h3&gt;

&lt;p&gt;L'éditeur peut l'être, gratuit ou payant&amp;nbsp;: LastPass l'a été en 2022. L'&lt;a href="https://ico.org.uk/about-the-ico/media-centre/news-and-blogs/2025/12/password-manager-provider-fined" rel="noopener noreferrer"&gt;ICO&lt;/a&gt; n'a trouvé aucune preuve de déchiffrement, mais un coffre volé reste exposé à des essais hors ligne. Un mot de passe maître solide et la double authentification restent la meilleure protection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment passer de Chrome à Bitwarden ?
&lt;/h3&gt;

&lt;p&gt;Exportez depuis les paramètres du gestionnaire de Google, puis importez dans Bitwarden, qui lit le format de Chrome (&lt;a href="https://bitwarden.com/help/import-data/" rel="noopener noreferrer"&gt;aide de Bitwarden&lt;/a&gt;), puis supprimez le fichier exporté.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse fondée sur des sources publiques consultées les 30 et 31 octobre 2026&amp;nbsp;: pages des éditeurs, rapport et guide de l'ANSSI, CNIL, Cybermalveillance.gouv.fr, décision de l'ICO, publication de l'ETH Zurich, Privacy Guides et presse spécialisée. Les affirmations des éditeurs sont présentées comme telles. Aucun logiciel n'a été testé, aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux éditeurs mentionnés.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 31 octobre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image d'en-tête&amp;nbsp;: Sasun Bughdaryan (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gestionnairedemotsdepasse</category>
      <category>bitwarden</category>
      <category>keepassxc</category>
      <category>protonpass</category>
    </item>
    <item>
      <title>Nginx reverse proxy avec Docker Compose : le tutoriel HTTPS pas à pas (2026)</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sat, 31 Oct 2026 13:42:52 +0000</pubDate>
      <link>https://dev.to/neurau_tech/nginx-reverse-proxy-avec-docker-compose-le-tutoriel-https-pas-a-pas-2026-3i04</link>
      <guid>https://dev.to/neurau_tech/nginx-reverse-proxy-avec-docker-compose-le-tutoriel-https-pas-a-pas-2026-3i04</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Un Nginx reverse proxy reçoit les requêtes des clients à la place de votre application, termine le chiffrement HTTPS, puis transmet chaque requête au bon service interne. Avec Docker Compose, c'est le seul conteneur qui publie des ports&amp;nbsp;: votre API reste invisible depuis l'extérieur.&lt;/p&gt;

&lt;p&gt;Ce tutoriel place nginx devant l'API Node.js de notre guide pour &lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;créer une API REST avec Node.js et Express&lt;/a&gt;, qui écoute sur le port 3000 et expose une route &lt;code&gt;/health&lt;/code&gt;, conteneurisée comme dans notre guide &lt;a href="https://neurautech.com/docker-pour-debutants/" rel="noopener noreferrer"&gt;Docker pour débutants&lt;/a&gt;. Il repose sur la documentation de nginx.org, de Docker, de Certbot et de Let's Encrypt, consultée le 30 octobre 2026. Les fichiers suivent ces documentations, mais nous ne les avons pas exécutés&amp;nbsp;: la méthode est détaillée en fin d'article.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nginx 1.30.6 (stable) et 1.31.7 (mainline) ont corrigé cinq vulnérabilités le 14 octobre 2026.&lt;/li&gt;
&lt;li&gt;Quatre en-têtes sont à transmettre&amp;nbsp;: &lt;code&gt;Host&lt;/code&gt;, &lt;code&gt;X-Real-IP&lt;/code&gt;, &lt;code&gt;X-Forwarded-For&lt;/code&gt; et &lt;code&gt;X-Forwarded-Proto&lt;/code&gt;. Côté Express, &lt;code&gt;TRUST_PROXY=1&lt;/code&gt; lit l'adresse du client un saut plus loin.&lt;/li&gt;
&lt;li&gt;Une &lt;code&gt;location&lt;/code&gt; qui définit un seul &lt;code&gt;proxy_set_header&lt;/code&gt; perd tous ceux du niveau supérieur&amp;nbsp;: c'est le piège classique des WebSocket.&lt;/li&gt;
&lt;li&gt;Les certificats Let's Encrypt du profil par défaut, &lt;code&gt;classic&lt;/code&gt;, passeront de 90 à 64&amp;nbsp;jours le 10 février 2027, puis à 45&amp;nbsp;jours le 16 février 2028&amp;nbsp;: le renouvellement doit être automatique.&lt;/li&gt;
&lt;li&gt;Les erreurs 502, 504 et 413 ont chacune une cause typique, visible dans les journaux.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Reverse proxy, proxy direct, répartiteur de charge
&lt;/h2&gt;

&lt;p&gt;Un reverse proxy agit pour le compte des serveurs, alors qu'un proxy direct agit pour le compte des clients. MDN résume la différence ainsi&amp;nbsp;: un proxy direct peut masquer l'identité des clients, un reverse proxy masque celle des serveurs, et sert à la répartition de charge, à l'authentification, au déchiffrement ou au cache (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Proxy_servers_and_tunneling" rel="noopener noreferrer"&gt;MDN, serveurs proxy et tunnels&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Le répartiteur de charge est une fonction qu'un reverse proxy assure quand plusieurs instances tournent derrière lui&amp;nbsp;: sans méthode précisée, nginx répartit à tour de rôle (round-robin), avec &lt;code&gt;least_conn&lt;/code&gt; et &lt;code&gt;ip_hash&lt;/code&gt; comme alternatives (&lt;a href="https://nginx.org/en/docs/http/load_balancing.html" rel="noopener noreferrer"&gt;nginx.org, répartition de charge&lt;/a&gt;).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Proxy direct (forward)&lt;/th&gt;
&lt;th&gt;Reverse proxy&lt;/th&gt;
&lt;th&gt;Répartiteur de charge&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agit pour&lt;/td&gt;
&lt;td&gt;Les clients&lt;/td&gt;
&lt;td&gt;Les serveurs&lt;/td&gt;
&lt;td&gt;Les serveurs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Placé&lt;/td&gt;
&lt;td&gt;Côté réseau du client&lt;/td&gt;
&lt;td&gt;Devant l'application&lt;/td&gt;
&lt;td&gt;Devant plusieurs instances&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage typique&lt;/td&gt;
&lt;td&gt;Filtrage, cache sortant, anonymat&lt;/td&gt;
&lt;td&gt;TLS, routage par domaine ou chemin, en-têtes&lt;/td&gt;
&lt;td&gt;Répartir le trafic, tolérer une panne&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exemple&lt;/td&gt;
&lt;td&gt;Proxy d'entreprise&lt;/td&gt;
&lt;td&gt;nginx devant une API&lt;/td&gt;
&lt;td&gt;nginx avec un bloc &lt;code&gt;upstream&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pour une seule API, le Nginx reverse proxy apporte un point d'entrée HTTPS unique, des en-têtes fiables et un endroit où régler délais et limites.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les versions à utiliser en octobre 2026
&lt;/h2&gt;

&lt;p&gt;Le 14 octobre 2026, nginx.org a publié la 1.30.6 stable et la 1.31.7 mainline avec les mêmes cinq correctifs de sécurité&amp;nbsp;: CVE-2026-15176 (&lt;code&gt;alias&lt;/code&gt; combiné à &lt;code&gt;rewrite&lt;/code&gt;), CVE-2026-93479 (expressions avec variables), CVE-2026-95101 (corps de requête en HTTP/3), CVE-2026-97629 (rotation des clés de tickets TLS) et CVE-2026-97205 (SSI avec AIO). La 1.31.8 du 21 octobre n'apporte ensuite que des changements et corrections de bugs (&lt;a href="https://nginx.org/en/CHANGES-1.30" rel="noopener noreferrer"&gt;CHANGES-1.30&lt;/a&gt; et &lt;a href="https://nginx.org/en/CHANGES" rel="noopener noreferrer"&gt;CHANGES&lt;/a&gt;, consultés le 30 octobre 2026).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Composant&lt;/th&gt;
&lt;th&gt;État au 30 octobre 2026&lt;/th&gt;
&lt;th&gt;Image Docker utilisée&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;nginx stable&lt;/td&gt;
&lt;td&gt;1.30.6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nginx:1.30.6-alpine&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nginx mainline&lt;/td&gt;
&lt;td&gt;1.31.8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nginx:1.31.8-alpine&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Certbot&lt;/td&gt;
&lt;td&gt;5.9.0, publiée le 28 octobre 2026&lt;/td&gt;
&lt;td&gt;&lt;code&gt;certbot/certbot:v5.9.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js 26&lt;/td&gt;
&lt;td&gt;LTS depuis le 28 octobre 2026 (26.13.0)&lt;/td&gt;
&lt;td&gt;Non utilisée ici&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js 24&lt;/td&gt;
&lt;td&gt;LTS en maintenance depuis le 20 octobre 2026, jusqu'au 30 avril 2028&lt;/td&gt;
&lt;td&gt;&lt;code&gt;node:24-alpine&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Les étiquettes viennent de la &lt;a href="https://hub.docker.com/_/nginx" rel="noopener noreferrer"&gt;page de l'image nginx sur Docker Hub&lt;/a&gt;, la version de Certbot de son &lt;a href="https://github.com/certbot/certbot/blob/main/certbot/CHANGELOG.md" rel="noopener noreferrer"&gt;journal des modifications&lt;/a&gt;, et les dates de Node.js du &lt;a href="https://github.com/nodejs/Release" rel="noopener noreferrer"&gt;calendrier officiel des versions&lt;/a&gt;. Nous gardons &lt;code&gt;node:24-alpine&lt;/code&gt;, l'image de nos deux tutoriels&amp;nbsp;: la ligne 24 reste prise en charge et l'API exige Node.js 24 au minimum.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 1 : la configuration minimale d'un Nginx reverse proxy
&lt;/h2&gt;

&lt;p&gt;La directive centrale est &lt;code&gt;proxy_pass&lt;/code&gt;, qui indique l'adresse du serveur vers lequel nginx transmet la requête. Voici le fichier &lt;code&gt;nginx/conf.d/api.conf&lt;/code&gt; de départ, en HTTP seulement&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Servira au défi Let's Encrypt de l'étape 3&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/.well-known/acme-challenge/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/certbot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://api:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt;              &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt;         &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt;   &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;api&lt;/code&gt; est le nom du service dans Docker Compose, et 3000 le port interne de l'API. L'ordre des deux blocs &lt;code&gt;location&lt;/code&gt; n'a pas d'importance&amp;nbsp;: parmi les préfixes, nginx retient celui qui correspond le plus longuement à l'adresse demandée (&lt;a href="https://nginx.org/en/docs/http/ngx_http_core_module.html" rel="noopener noreferrer"&gt;module core HTTP&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Les quatre en-têtes ne sont pas décoratifs. Par défaut, nginx remplace l'en-tête &lt;code&gt;Host&lt;/code&gt; par &lt;code&gt;$proxy_host&lt;/code&gt;, c'est-à-dire &lt;code&gt;api:3000&lt;/code&gt; ici, et l'application ne verrait jamais le vrai domaine (&lt;a href="https://nginx.org/en/docs/http/ngx_http_proxy_module.html" rel="noopener noreferrer"&gt;module proxy de nginx&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;En-tête&lt;/th&gt;
&lt;th&gt;Valeur envoyée&lt;/th&gt;
&lt;th&gt;Ce que l'application en tire&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;$host&lt;/code&gt;, le domaine demandé&lt;/td&gt;
&lt;td&gt;Les liens absolus et le routage par domaine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Real-IP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;$remote_addr&lt;/code&gt;, l'adresse vue par nginx&lt;/td&gt;
&lt;td&gt;L'adresse du client, en une seule valeur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Forwarded-For&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;La valeur reçue, plus &lt;code&gt;$remote_addr&lt;/code&gt; à la fin&lt;/td&gt;
&lt;td&gt;La chaîne des adresses traversées&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Forwarded-Proto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;$scheme&lt;/code&gt;, &lt;code&gt;http&lt;/code&gt; ou &lt;code&gt;https&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Savoir si le client était en HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Le piège de la barre oblique finale
&lt;/h3&gt;

&lt;p&gt;Une barre oblique de trop dans &lt;code&gt;proxy_pass&lt;/code&gt; change l'adresse transmise&amp;nbsp;: si &lt;code&gt;proxy_pass&lt;/code&gt; contient une URI, même réduite à &lt;code&gt;/&lt;/code&gt;, la partie qui correspond au bloc &lt;code&gt;location&lt;/code&gt; est remplacée par cette URI&amp;nbsp;; sans URI, la requête part telle que le client l'a envoyée.&lt;/p&gt;

&lt;p&gt;Avec &lt;code&gt;location /api/&lt;/code&gt; et &lt;code&gt;proxy_pass http://api:3000/;&lt;/code&gt;, une requête vers &lt;code&gt;/api/v1/taches&lt;/code&gt; arrive sous la forme &lt;code&gt;/v1/taches&lt;/code&gt;. L'API du tutoriel attend &lt;code&gt;/api/v1/...&lt;/code&gt;&amp;nbsp;: pas de barre finale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rester cohérent avec TRUST_PROXY
&lt;/h3&gt;

&lt;p&gt;Notre API Express ne lit ces en-têtes que si la variable &lt;code&gt;TRUST_PROXY&lt;/code&gt; est réglée. La page &lt;a href="https://expressjs.com/en/guide/behind-proxies/" rel="noopener noreferrer"&gt;Express derrière un proxy&lt;/a&gt; explique qu'avec un nombre, Express fait confiance à ce nombre de sauts, en lisant &lt;code&gt;X-Forwarded-For&lt;/code&gt; de droite à gauche. Avec un seul nginx devant, &lt;code&gt;TRUST_PROXY=1&lt;/code&gt; désigne la dernière adresse de la liste, celle que nginx ajoute lui-même&amp;nbsp;: un client ne peut pas la falsifier.&lt;/p&gt;

&lt;p&gt;La même page précise que &lt;code&gt;req.protocol&lt;/code&gt; reflète &lt;code&gt;X-Forwarded-Proto&lt;/code&gt;. Le cas d'un CDN devant nginx est traité plus bas.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 2 : la pile Docker Compose
&lt;/h2&gt;

&lt;p&gt;Trois prérequis&amp;nbsp;: un serveur Linux avec Docker Engine et Compose, un enregistrement DNS A (et AAAA en IPv6) qui fait pointer &lt;code&gt;api.example.com&lt;/code&gt; vers lui, et les ports 80 et 443 ouverts, le 80 servant au défi de Let's Encrypt.&lt;/p&gt;

&lt;p&gt;Seul nginx publie des ports, et c'est voulu. La page d'installation de Docker pour Ubuntu prévient que les ports publiés par des conteneurs contournent les règles des pare-feu ufw et firewalld (&lt;a href="https://docs.docker.com/engine/install/ubuntu/" rel="noopener noreferrer"&gt;installer Docker Engine sur Ubuntu&lt;/a&gt;)&amp;nbsp;: un port 3000 publié par erreur serait joignable malgré votre pare-feu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe37awrewwx7epdvqixd1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe37awrewwx7epdvqixd1.webp" alt="Schéma de la pile Docker Compose&amp;nbsp;: le navigateur se connecte au Nginx reverse proxy sur les ports 80 et 443, qui transmet en HTTP au service api sur le port 3000 interne, Certbot partageant les certificats par des dossiers montés" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voici l'arborescence, placée par exemple dans &lt;code&gt;/srv/pile&lt;/code&gt;&amp;nbsp;; le dossier &lt;code&gt;api-taches&lt;/code&gt; est le projet du tutoriel Node.js, avec le &lt;code&gt;Dockerfile&lt;/code&gt; et le &lt;code&gt;.dockerignore&lt;/code&gt; de notre guide Docker&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/srv/pile/
  compose.yaml
  .env                  JWT_SECRET, jamais dans Git
  api-taches/           package.json, src/, Dockerfile, .dockerignore
  nginx/conf.d/api.conf
  certbot/www/          défis Let's Encrypt
  certbot/conf/         certificats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce &lt;code&gt;compose.yaml&lt;/code&gt; remplace celui du guide Docker, qui vivait dans &lt;code&gt;api-taches&lt;/code&gt; et publiait &lt;code&gt;127.0.0.1:3000&lt;/code&gt;&amp;nbsp;: ici, l'API ne publie plus rien. Comme le dossier du projet change de nom (&lt;code&gt;pile&lt;/code&gt; au lieu de &lt;code&gt;api-taches&lt;/code&gt;), Compose crée un nouveau volume &lt;code&gt;pile_api-data&lt;/code&gt;&amp;nbsp;: la base SQLite du guide Docker n'est pas reprise. Rappel du Dockerfile multi-étapes de ce guide&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:24-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deps&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:24-alpine&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DB_PATH=/app/data/data.db&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=deps /app/node_modules ./node_modules&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src ./src&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /app/data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chown &lt;/span&gt;node:node /app/data
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "src/server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puis &lt;code&gt;/srv/pile/compose.yaml&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./api-taches&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${JWT_SECRET:?definir JWT_SECRET dans .env}&lt;/span&gt;
      &lt;span class="na"&gt;DB_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app/data/data.db&lt;/span&gt;
      &lt;span class="na"&gt;TRUST_PROXY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api-data:/app/data&lt;/span&gt;
    &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-e"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fetch('http://127.0.0.1:3000/health').then(r&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;process.exit(r.ok&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1)).catch(()&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;process.exit(1))"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.30.6-alpine&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx/conf.d:/etc/nginx/conf.d:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./certbot/www:/var/www/certbot:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./certbot/conf:/etc/letsencrypt:ro&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;

  &lt;span class="na"&gt;certbot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;certbot/certbot:v5.9.0&lt;/span&gt;
    &lt;span class="na"&gt;profiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;outils"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./certbot/www:/var/www/certbot&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./certbot/conf:/etc/letsencrypt&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le secret vient du fichier &lt;code&gt;.env&lt;/code&gt; placé à côté de &lt;code&gt;compose.yaml&lt;/code&gt;&amp;nbsp;: Compose le lit dans le dossier du projet pour remplacer &lt;code&gt;${JWT_SECRET}&lt;/code&gt;, et &lt;code&gt;${VAR:?message}&lt;/code&gt; arrête tout si la variable est vide (&lt;a href="https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/" rel="noopener noreferrer"&gt;interpolation des variables&lt;/a&gt;). Le &lt;code&gt;.env&lt;/code&gt; du tutoriel Node.js, rangé dans &lt;code&gt;api-taches&lt;/code&gt;, n'est pas lu. Seules les variables listées sous &lt;code&gt;environment&lt;/code&gt; entrent dans le conteneur (&lt;a href="https://docs.docker.com/reference/compose-file/services/" rel="noopener noreferrer"&gt;référence des services&lt;/a&gt;). Générez le secret sans installer Node.js sur le serveur&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /srv/pile
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"JWT_SECRET=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; node:24-alpine node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"console.log(require('node:crypto').randomBytes(32).toString('hex'))"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les autres choix, tous tirés de la documentation de Docker&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pas de clé &lt;code&gt;ports&lt;/code&gt; sur l'API.&lt;/strong&gt; C'est elle qui l'isole&amp;nbsp;: Compose relie les services sur un réseau interne où chacun est joignable par son nom et par le port du conteneur (&lt;a href="https://docs.docker.com/compose/how-tos/networking/" rel="noopener noreferrer"&gt;réseau dans Compose&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;init: true&lt;/code&gt;.&lt;/strong&gt; Un processus d'init transmet les signaux à Node.js, comme dans le guide Docker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;depends_on&lt;/code&gt; avec &lt;code&gt;service_healthy&lt;/code&gt;.&lt;/strong&gt; nginx attend que le test de santé, qui appelle &lt;code&gt;/health&lt;/code&gt;, réussisse. &lt;code&gt;fetch&lt;/code&gt; est stable depuis Node.js 21 (&lt;a href="https://nodejs.org/docs/latest-v24.x/api/globals.html" rel="noopener noreferrer"&gt;globales de Node.js 24&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;restart: unless-stopped&lt;/code&gt;.&lt;/strong&gt; La valeur par défaut, &lt;code&gt;no&lt;/code&gt;, ne relance jamais un conteneur arrêté.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le profil du service &lt;code&gt;certbot&lt;/code&gt;.&lt;/strong&gt; Il ne démarre pas avec &lt;code&gt;docker compose up&lt;/code&gt;, mais &lt;code&gt;docker compose run certbot&lt;/code&gt; le lance quand on le cible (&lt;a href="https://docs.docker.com/compose/how-tos/profiles/" rel="noopener noreferrer"&gt;profils&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Monter &lt;code&gt;./nginx/conf.d&lt;/code&gt; masque le &lt;code&gt;default.conf&lt;/code&gt; de l'image. Créez &lt;code&gt;certbot/www&lt;/code&gt; et &lt;code&gt;certbot/conf&lt;/code&gt;, puis lancez &lt;code&gt;docker compose up -d --build&lt;/code&gt; et &lt;code&gt;curl -i http://api.example.com/health&lt;/code&gt;, qui doit répondre &lt;code&gt;{"statut":"ok"}&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 3 : obtenir et renouveler le certificat Let's Encrypt
&lt;/h2&gt;

&lt;p&gt;Le HTTPS pose un problème d'œuf et de poule&amp;nbsp;: nginx refuse de démarrer si un bloc &lt;code&gt;server&lt;/code&gt; cite un certificat absent, et Certbot a besoin de nginx pour prouver que vous contrôlez le domaine. C'est pourquoi le fichier de l'étape 1 sert déjà &lt;code&gt;/.well-known/acme-challenge/&lt;/code&gt; en HTTP&amp;nbsp;: le défi HTTP-01 de Let's Encrypt ne passe que par le port 80, à cette adresse (&lt;a href="https://letsencrypt.org/docs/challenge-types/" rel="noopener noreferrer"&gt;types de défis&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Lancez Certbot avec le greffon webroot, qui dépose un fichier temporaire dans le dossier partagé avec nginx (&lt;a href="https://eff-certbot.readthedocs.io/en/stable/using.html" rel="noopener noreferrer"&gt;documentation de Certbot&lt;/a&gt;)&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /srv/pile
docker compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; certbot certonly &lt;span class="nt"&gt;--webroot&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; /var/www/certbot &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; api.example.com &lt;span class="nt"&gt;--email&lt;/span&gt; vous@example.com &lt;span class="nt"&gt;--agree-tos&lt;/span&gt; &lt;span class="nt"&gt;--no-eff-email&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La &lt;a href="https://eff-certbot.readthedocs.io/en/stable/install.html" rel="noopener noreferrer"&gt;page d'installation de Certbot&lt;/a&gt; prévient que l'image Docker ne sait pas configurer le serveur web&amp;nbsp;: seule la commande &lt;code&gt;certonly&lt;/code&gt; est utilisable. Le certificat arrive dans &lt;code&gt;certbot/conf/live/api.example.com/&lt;/code&gt;, vu par nginx sous &lt;code&gt;/etc/letsencrypt/live/api.example.com/&lt;/code&gt;, avec &lt;code&gt;fullchain.pem&lt;/code&gt; et &lt;code&gt;privkey.pem&lt;/code&gt;. L'étape 5 les branche.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatiser le renouvellement
&lt;/h3&gt;

&lt;p&gt;Le renouvellement doit être automatique, car les certificats raccourcissent. La &lt;a href="https://letsencrypt.org/docs/faq/" rel="noopener noreferrer"&gt;FAQ de Let's Encrypt&lt;/a&gt; indique des certificats de 90&amp;nbsp;jours par défaut. Son &lt;a href="https://letsencrypt.org/2025/12/02/from-90-to-45" rel="noopener noreferrer"&gt;calendrier publié en décembre 2025&lt;/a&gt; fait passer le profil par défaut, &lt;code&gt;classic&lt;/code&gt;, à 64&amp;nbsp;jours le 10 février 2027, puis à 45&amp;nbsp;jours le 16 février 2028, et déconseille le renouvellement manuel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvu28awjxog3qq9j6e0v0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvu28awjxog3qq9j6e0v0.webp" alt="Graphique en barres&amp;nbsp;: durée des certificats Let's Encrypt du profil classic, 90&amp;nbsp;jours en octobre 2026, 64&amp;nbsp;jours au 10 février 2027 et 45&amp;nbsp;jours au 16 février 2028, avec le dernier tiers où Certbot renouvelle selon notre calcul" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depuis sa version 4.0.0, &lt;code&gt;certbot renew&lt;/code&gt; renouvelle quand il reste moins d'un tiers de la durée de vie&amp;nbsp;; depuis la 4.1.0, il interroge aussi l'ARI (ACME Renewal Information), qui peut avancer la date (&lt;a href="https://github.com/certbot/certbot/blob/main/certbot/CHANGELOG.md" rel="noopener noreferrer"&gt;journal des modifications de Certbot&lt;/a&gt;). Selon notre calcul, sans ARI, cela donne un renouvellement à 30&amp;nbsp;jours de l'échéance aujourd'hui, à environ 21&amp;nbsp;jours avec 64&amp;nbsp;jours de validité et à 15&amp;nbsp;jours avec 45. Une tâche planifiée quotidienne sur l'hôte suffit&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# crontab de l'hôte, chaque jour à 3 h 17&lt;/span&gt;
17 3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /srv/pile &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; certbot renew &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-T&lt;/span&gt; nginx nginx &lt;span class="nt"&gt;-s&lt;/span&gt; reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le rechargement est indispensable, nginx gardant l'ancien certificat en mémoire. &lt;code&gt;cd /srv/pile &amp;amp;&amp;amp; docker compose run --rm certbot renew --dry-run&lt;/code&gt; teste le tout sur le serveur de test de Let's Encrypt. Notre guide pour &lt;a href="https://neurautech.com/guide-complet-pour-automatiser-ses-deploiements/" rel="noopener noreferrer"&gt;automatiser ses déploiements en CI/CD&lt;/a&gt; montre comment rejouer ces commandes depuis un pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 4 : WebSocket, en-têtes de sécurité, délais et tailles
&lt;/h2&gt;

&lt;p&gt;Cette étape explique chaque réglage et où il se place&amp;nbsp;; l'étape 5 les assemble dans un seul fichier. Les fichiers de &lt;code&gt;conf.d&lt;/code&gt; sont inclus dans le bloc &lt;code&gt;http&lt;/code&gt; du &lt;code&gt;nginx.conf&lt;/code&gt; des paquets de nginx.org (&lt;a href="https://github.com/nginx/pkg-oss" rel="noopener noreferrer"&gt;pkg-oss&lt;/a&gt;)&amp;nbsp;: une directive écrite hors de tout bloc &lt;code&gt;server&lt;/code&gt; y est donc en contexte &lt;code&gt;http&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les WebSocket
&lt;/h3&gt;

&lt;p&gt;Une connexion WebSocket commence par une requête HTTP qui demande un changement de protocole. La &lt;a href="https://nginx.org/en/docs/http/websocket.html" rel="noopener noreferrer"&gt;page WebSocket de nginx.org&lt;/a&gt; explique que &lt;code&gt;Upgrade&lt;/code&gt; et &lt;code&gt;Connection&lt;/code&gt; sont des en-têtes «&amp;nbsp;de saut à saut&amp;nbsp;» (notre traduction), donc non transmis&amp;nbsp;: il faut les poser avec un &lt;code&gt;map&lt;/code&gt;, autorisé seulement en contexte &lt;code&gt;http&lt;/code&gt; (&lt;a href="https://nginx.org/en/docs/http/ngx_http_map_module.html" rel="noopener noreferrer"&gt;module map&lt;/a&gt;), et deux &lt;code&gt;proxy_set_header&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Le piège est l'héritage&amp;nbsp;: selon le module proxy, les &lt;code&gt;proxy_set_header&lt;/code&gt; sont hérités du niveau supérieur si et seulement si le niveau courant n'en définit aucun. Une &lt;code&gt;location /ws/&lt;/code&gt; qui ajoute &lt;code&gt;Upgrade&lt;/code&gt; perd donc &lt;code&gt;Host&lt;/code&gt; et les &lt;code&gt;X-Forwarded-*&lt;/code&gt;&amp;nbsp;: il faut les répéter.&lt;/p&gt;

&lt;p&gt;Depuis nginx 1.29.7, &lt;code&gt;proxy_http_version&lt;/code&gt; vaut 1.1 par défaut, ce qu'exigent les WebSocket&amp;nbsp;: inutile de l'écrire avec la 1.30.6. nginx coupe après 60&amp;nbsp;secondes sans données&amp;nbsp;: relevez &lt;code&gt;proxy_read_timeout&lt;/code&gt; sur cette &lt;code&gt;location&lt;/code&gt;, ou envoyez des trames ping. L'API du tutoriel n'a pas de WebSocket&amp;nbsp;: ce bloc sert si vous en ajoutez.&lt;/p&gt;

&lt;h3&gt;
  
  
  En-têtes de sécurité
&lt;/h3&gt;

&lt;p&gt;Ne doublez pas ce que l'application envoie déjà. Notre API utilise Helmet, qui pose par défaut &lt;code&gt;Strict-Transport-Security: max-age=31536000; includeSubDomains&lt;/code&gt;, &lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt; et &lt;code&gt;X-Frame-Options: SAMEORIGIN&lt;/code&gt;, entre autres (&lt;a href="https://helmet.js.org/" rel="noopener noreferrer"&gt;documentation de Helmet&lt;/a&gt;, consultée le 30 octobre 2026). Deux réglages suffisent côté nginx, avec un piège à connaître&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;server_tokens off&lt;/code&gt;&lt;/strong&gt; (contexte &lt;code&gt;http&lt;/code&gt;) retire le numéro de version des pages d'erreur et de l'en-tête &lt;code&gt;Server&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;proxy_hide_header Strict-Transport-Security&lt;/code&gt;&lt;/strong&gt; puis &lt;strong&gt;&lt;code&gt;add_header ... always&lt;/code&gt;&lt;/strong&gt; (bloc &lt;code&gt;server&lt;/code&gt; du port 443)&amp;nbsp;: nginx envoie un seul en-tête HSTS, y compris sur ses propres pages 502 ou 504. Sans &lt;code&gt;always&lt;/code&gt;, &lt;code&gt;add_header&lt;/code&gt; ne s'applique qu'à certains codes, dont 200, 301 et 302 (&lt;a href="https://nginx.org/en/docs/http/ngx_http_headers_module.html" rel="noopener noreferrer"&gt;module headers&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L'héritage, encore.&lt;/strong&gt; Un &lt;code&gt;add_header&lt;/code&gt; placé dans une &lt;code&gt;location&lt;/code&gt; annule ceux du &lt;code&gt;server&lt;/code&gt;. Depuis nginx 1.29.3, &lt;code&gt;add_header_inherit merge;&lt;/code&gt; cumule les deux niveaux, selon le même &lt;a href="https://nginx.org/en/docs/http/ngx_http_headers_module.html" rel="noopener noreferrer"&gt;module headers&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Délais et taille des requêtes
&lt;/h3&gt;

&lt;p&gt;Ces directives acceptent les contextes &lt;code&gt;http&lt;/code&gt;, &lt;code&gt;server&lt;/code&gt; et &lt;code&gt;location&lt;/code&gt;&amp;nbsp;; nous les plaçons dans le &lt;code&gt;server&lt;/code&gt; du port 443&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;client_max_body_size&lt;/code&gt;&lt;/strong&gt; vaut 1&amp;nbsp;Mo par défaut, au-delà duquel nginx renvoie l'erreur 413. L'API refuse déjà tout JSON de plus de 10 ko&amp;nbsp;: une limite de 16 ko côté nginx écarte les gros corps avant qu'ils n'atteignent Node.js. Relevez-la si vous ajoutez un envoi de fichiers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;proxy_connect_timeout&lt;/code&gt;&lt;/strong&gt; vaut 60&amp;nbsp;secondes par défaut et ne peut généralement pas dépasser 75&amp;nbsp;secondes. Sur un réseau Docker local, 5&amp;nbsp;secondes suffisent pour échouer vite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;proxy_read_timeout&lt;/code&gt;&lt;/strong&gt; vaut 60&amp;nbsp;secondes et mesure le silence entre deux lectures, pas la durée totale de la réponse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour les menaces que ces réglages contrent, voyez notre point sur les &lt;a href="https://neurautech.com/cyberattaques-les-plus-frequentes-comment-proteger/" rel="noopener noreferrer"&gt;cyberattaques les plus fréquentes&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Étape 5 : le fichier api.conf final
&lt;/h2&gt;

&lt;p&gt;Voici &lt;code&gt;nginx/conf.d/api.conf&lt;/code&gt; complet, qui remplace celui de l'étape 1. Les trois premières directives sont en contexte &lt;code&gt;http&lt;/code&gt;&amp;nbsp;; tout le reste vit dans les deux blocs &lt;code&gt;server&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Contexte http (fichier inclus par nginx.conf)&lt;/span&gt;
&lt;span class="k"&gt;map&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt; &lt;span class="nv"&gt;$connection_upgrade&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;default&lt;/span&gt; &lt;span class="s"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;''&lt;/span&gt;      &lt;span class="s"&gt;close&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;log_format&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="nv"&gt;$remote_addr&lt;/span&gt; &lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$time_local&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;
                 &lt;span class="s"&gt;'upstream=&lt;/span&gt;&lt;span class="nv"&gt;$upstream_status&lt;/span&gt; &lt;span class="s"&gt;rt=&lt;/span&gt;&lt;span class="nv"&gt;$upstream_response_time&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;
                 &lt;span class="s"&gt;'ct=&lt;/span&gt;&lt;span class="nv"&gt;$upstream_connect_time&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;server_tokens&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;# Port 80 : défi Let's Encrypt, puis redirection vers HTTPS&lt;/span&gt;
&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/.well-known/acme-challenge/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/certbot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Port 443 : le reverse proxy&lt;/span&gt;
&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;http2&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt;     &lt;span class="n"&gt;/etc/letsencrypt/live/api.example.com/fullchain.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/live/api.example.com/privkey.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="n"&gt;/var/log/nginx/access.log&lt;/span&gt; &lt;span class="s"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;client_max_body_size&lt;/span&gt;  &lt;span class="mi"&gt;16k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_connect_timeout&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_read_timeout&lt;/span&gt;    &lt;span class="s"&gt;60s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;proxy_hide_header&lt;/span&gt; &lt;span class="s"&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class="s"&gt;"max-age=31536000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;includeSubDomains"&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Hérités par toute location qui ne définit aucun proxy_set_header&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt;              &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt;         &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt;   &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://api:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/ws/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://api:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt;              &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt;         &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt;   &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt;           &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt;        &lt;span class="nv"&gt;$connection_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_read_timeout&lt;/span&gt; &lt;span class="s"&gt;1h&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous avons relu le contexte autorisé de chaque directive sur nginx.org&amp;nbsp;: &lt;code&gt;map&lt;/code&gt; et &lt;code&gt;log_format&lt;/code&gt; n'acceptent que &lt;code&gt;http&lt;/code&gt;, &lt;code&gt;return&lt;/code&gt; accepte &lt;code&gt;server&lt;/code&gt; et &lt;code&gt;location&lt;/code&gt;, &lt;code&gt;ssl_certificate&lt;/code&gt; et &lt;code&gt;http2&lt;/code&gt; acceptent &lt;code&gt;http&lt;/code&gt; et &lt;code&gt;server&lt;/code&gt;. Le paramètre &lt;code&gt;http2&lt;/code&gt; de &lt;code&gt;listen&lt;/code&gt; est déprécié au profit de &lt;code&gt;http2 on;&lt;/code&gt;, apparu dans nginx 1.25.1 (&lt;a href="https://nginx.org/en/docs/http/ngx_http_v2_module.html" rel="noopener noreferrer"&gt;module HTTP/2&lt;/a&gt;), et &lt;code&gt;ssl_protocols&lt;/code&gt; vaut déjà &lt;code&gt;TLSv1.2 TLSv1.3&lt;/code&gt; par défaut (&lt;a href="https://nginx.org/en/docs/http/ngx_http_ssl_module.html" rel="noopener noreferrer"&gt;module SSL&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Vérifiez, rechargez, puis testez&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;nginx nginx &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;nginx nginx &lt;span class="nt"&gt;-s&lt;/span&gt; reload
curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://api.example.com/health     &lt;span class="c"&gt;# attendu : 301 vers https&lt;/span&gt;
curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://api.example.com/health    &lt;span class="c"&gt;# attendu : 200 et {"statut":"ok"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si &lt;code&gt;nginx -t&lt;/code&gt; signale une erreur, le rechargement n'a pas lieu et l'ancienne configuration continue de tourner&amp;nbsp;: selon le &lt;a href="https://nginx.org/en/docs/beginners_guide.html" rel="noopener noreferrer"&gt;guide du débutant de nginx&lt;/a&gt;, le processus maître revient à l'ancienne configuration quand la nouvelle échoue.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nginx Proxy Manager, Caddy ou Traefik ?
&lt;/h2&gt;

&lt;p&gt;Le Nginx reverse proxy écrit à la main n'est pas la seule voie. Trois alternatives reviennent dans les recherches, avec des compromis différents.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Outil&lt;/th&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Certificats&lt;/th&gt;
&lt;th&gt;Point d'attention&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;nginx écrit à la main&lt;/td&gt;
&lt;td&gt;Fichiers texte, versionnés dans Git&lt;/td&gt;
&lt;td&gt;Certbot, à brancher soi-même&lt;/td&gt;
&lt;td&gt;Le plus de contrôle, le plus de travail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://nginxproxymanager.com/guide/" rel="noopener noreferrer"&gt;Nginx Proxy Manager&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Interface web sur le port 81&lt;/td&gt;
&lt;td&gt;Let's Encrypt intégré&lt;/td&gt;
&lt;td&gt;Configuration stockée en base, moins facile à relire dans Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://caddyserver.com/docs/automatic-https" rel="noopener noreferrer"&gt;Caddy&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Fichier Caddyfile court&lt;/td&gt;
&lt;td&gt;Obtention et renouvellement automatiques par défaut&lt;/td&gt;
&lt;td&gt;Une syntaxe de plus à apprendre&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Étiquettes sur les conteneurs&lt;/td&gt;
&lt;td&gt;Résolveur ACME, suivi des expirations automatique&lt;/td&gt;
&lt;td&gt;Accès à l'API Docker à protéger&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Le guide de Nginx Proxy Manager utilise l'image &lt;code&gt;jc21/nginx-proxy-manager:2.16.0&lt;/code&gt;. Caddy s'appuie sur Let's Encrypt ou ZeroSSL et redirige vers HTTPS sans réglage. Traefik avertit qu'un accès sans restriction à l'API Docker pose un problème de sécurité.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notre lecture&amp;nbsp;:&lt;/strong&gt; pour un projet ou deux, Caddy ou Nginx Proxy Manager font gagner du temps. Dès que la configuration doit être relue, versionnée et testée en CI, le fichier nginx explicite reste le plus prévisible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Erreurs courantes et lecture des journaux
&lt;/h2&gt;

&lt;p&gt;Chaque code d'erreur renvoyé par nginx pointe vers une cause précise, que les journaux nomment presque toujours. L'image officielle relie &lt;code&gt;access.log&lt;/code&gt; à la sortie standard et &lt;code&gt;error.log&lt;/code&gt; à la sortie d'erreur (&lt;a href="https://github.com/nginx/docker-nginx" rel="noopener noreferrer"&gt;dépôt docker-nginx&lt;/a&gt;)&amp;nbsp;: &lt;code&gt;docker compose logs --tail=50 nginx&lt;/code&gt; affiche les deux.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptôme&lt;/th&gt;
&lt;th&gt;Cause la plus fréquente&lt;/th&gt;
&lt;th&gt;Correctif&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;502 Bad Gateway&lt;/td&gt;
&lt;td&gt;L'API est arrêtée, écoute sur un autre port, ou son conteneur a changé d'adresse&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker compose ps&lt;/code&gt;, vérifier le port 3000, recharger nginx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;504 Gateway Timeout&lt;/td&gt;
&lt;td&gt;L'API ne répond pas dans le délai de &lt;code&gt;proxy_read_timeout&lt;/code&gt; (60 s par défaut)&lt;/td&gt;
&lt;td&gt;Accélérer la route, ou relever le délai sur cette seule &lt;code&gt;location&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;413 Request Entity Too Large&lt;/td&gt;
&lt;td&gt;Corps de requête au-dessus de &lt;code&gt;client_max_body_size&lt;/code&gt; (16 ko dans notre fichier)&lt;/td&gt;
&lt;td&gt;Relever la limite dans le &lt;code&gt;server&lt;/code&gt; ou la &lt;code&gt;location&lt;/code&gt; concernée&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boucle de redirections derrière Cloudflare&lt;/td&gt;
&lt;td&gt;Mode SSL Flexible, et redirection HTTP vers HTTPS sur l'origine&lt;/td&gt;
&lt;td&gt;Passer en mode Full (strict)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toutes les IP clientes identiques&lt;/td&gt;
&lt;td&gt;En-têtes absents, ou CDN devant nginx sans module realip&lt;/td&gt;
&lt;td&gt;Vérifier les quatre en-têtes, configurer &lt;code&gt;set_real_ip_from&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;nginx résout le nom &lt;code&gt;api&lt;/code&gt; au chargement de sa configuration&amp;nbsp;: si le conteneur est recréé avec une nouvelle adresse, un &lt;code&gt;nginx -s reload&lt;/code&gt; règle le 502. Pour s'en passer, nginx accepte depuis la 1.27.3 le paramètre &lt;code&gt;resolve&lt;/code&gt; dans un bloc &lt;code&gt;upstream&lt;/code&gt; doté d'une &lt;code&gt;zone&lt;/code&gt;, avec une directive &lt;code&gt;resolver&lt;/code&gt; (&lt;a href="https://nginx.org/en/docs/http/ngx_http_upstream_module.html" rel="noopener noreferrer"&gt;module upstream&lt;/a&gt;)&amp;nbsp;; le DNS intégré de Docker répond à l'adresse 127.0.0.11 (&lt;a href="https://docs.docker.com/engine/network/" rel="noopener noreferrer"&gt;réseau Docker&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Derrière Cloudflare
&lt;/h3&gt;

&lt;p&gt;La boucle de redirections est documentée par Cloudflare&amp;nbsp;: en mode Flexible, Cloudflare contacte l'origine en HTTP, l'origine redirige vers HTTPS, et le cycle recommence. Le correctif conseillé est le mode Full ou Full (strict), qui exige un certificat sur l'origine, celui de Certbot convient (&lt;a href="https://developers.cloudflare.com/ssl/troubleshooting/too-many-redirects/" rel="noopener noreferrer"&gt;Cloudflare, trop de redirections&lt;/a&gt;, mis à jour le 1er septembre 2026).&lt;/p&gt;

&lt;p&gt;Pour retrouver l'adresse réelle des visiteurs, Cloudflare recommande le module realip de nginx, avec ses plages d'adresses publiées sur &lt;a href="https://www.cloudflare.com/ips/" rel="noopener noreferrer"&gt;cloudflare.com/ips&lt;/a&gt; (&lt;a href="https://developers.cloudflare.com/support/troubleshooting/restoring-visitor-ips/restoring-original-visitor-ips/" rel="noopener noreferrer"&gt;Cloudflare, adresses des visiteurs&lt;/a&gt;). Ces lignes vont dans le &lt;code&gt;server&lt;/code&gt; du port 443&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Une ligne par plage publiée par Cloudflare, liste à tenir à jour&lt;/span&gt;
&lt;span class="k"&gt;set_real_ip_from&lt;/span&gt; &lt;span class="mf"&gt;192.0&lt;/span&gt;&lt;span class="s"&gt;.2.0/24&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;real_ip_header&lt;/span&gt;   &lt;span class="s"&gt;CF-Connecting-IP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le module n'est pas compilé par défaut, mais les paquets de nginx.org, base de l'image officielle, l'incluent. &lt;code&gt;$remote_addr&lt;/code&gt; devient l'adresse du visiteur, nginx l'ajoute en fin de &lt;code&gt;X-Forwarded-For&lt;/code&gt;, et &lt;code&gt;TRUST_PROXY=1&lt;/code&gt; reste juste côté Express.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lire les journaux utilement
&lt;/h3&gt;

&lt;p&gt;Le format &lt;code&gt;proxy&lt;/code&gt; du fichier final ajoute au journal d'accès le statut et les temps de l'API, grâce aux variables du module upstream. Un 502 accompagné, dans le journal d'erreurs, d'un échec de connexion vers &lt;code&gt;api:3000&lt;/code&gt; signale que nginx n'a pas joint l'API. Un &lt;code&gt;rt=&lt;/code&gt; proche de 60&amp;nbsp;secondes avant un 504 désigne une route trop lente.&lt;/p&gt;

&lt;p&gt;Pour filtrer ces lignes, nos &lt;a href="https://neurautech.com/commandes-linux-de-base/" rel="noopener noreferrer"&gt;commandes Linux de base&lt;/a&gt; couvrent &lt;code&gt;grep&lt;/code&gt; et &lt;code&gt;tail&lt;/code&gt;. Pour héberger la pile, voyez notre guide de l'&lt;a href="https://neurautech.com/hebergement-cloud-pour-developpeurs/" rel="noopener noreferrer"&gt;hébergement cloud pour développeurs&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Ce tutoriel est une synthèse de documentation, pas un retour d'expérience. Les points suivants restent à confirmer de votre côté&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L'exécution des fichiers.&lt;/strong&gt; Ni Docker ni nginx n'étaient disponibles sur notre poste&amp;nbsp;: &lt;code&gt;nginx -t&lt;/code&gt;, &lt;code&gt;docker compose up&lt;/code&gt; et Certbot n'ont pas été lancés.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les droits du volume SQLite.&lt;/strong&gt; La documentation de Docker dit qu'un volume vide reçoit le contenu du dossier de l'image&amp;nbsp;; la page lue ne précise pas que le propriétaire &lt;code&gt;node&lt;/code&gt; de &lt;code&gt;/app/data&lt;/code&gt; est conservé.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le défi HTTP-01 derrière Cloudflare en mode proxy.&lt;/strong&gt; Nous n'avons pas trouvé de page de Cloudflare qui le décrive pour ce cas précis. En cas d'échec, désactivez le proxy le temps de l'émission ou passez au défi DNS-01.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les plages d'adresses de Cloudflare.&lt;/strong&gt; Elles changent&amp;nbsp;: &lt;code&gt;192.0.2.0/24&lt;/code&gt; est une adresse de documentation, à remplacer par la liste officielle.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Qu'est-ce qu'un Nginx reverse proxy ?
&lt;/h3&gt;

&lt;p&gt;C'est un serveur nginx placé devant une ou plusieurs applications&amp;nbsp;: il reçoit les requêtes des clients, termine HTTPS et les transmet au bon service avec &lt;code&gt;proxy_pass&lt;/code&gt;. Selon &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Proxy_servers_and_tunneling" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;, un reverse proxy masque l'identité des serveurs, là où un proxy direct masque celle des clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faut-il nginx stable ou mainline en 2026 ?
&lt;/h3&gt;

&lt;p&gt;Les deux reçoivent les correctifs de sécurité&amp;nbsp;: le 14 octobre 2026, &lt;a href="https://nginx.org/" rel="noopener noreferrer"&gt;nginx.org&lt;/a&gt; a publié la 1.30.6 stable et la 1.31.7 mainline avec les mêmes corrections. La branche stable change moins souvent, d'où notre choix de l'étiquette &lt;code&gt;nginx:1.30.6-alpine&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment corriger une erreur 413 avec nginx ?
&lt;/h3&gt;

&lt;p&gt;Relevez &lt;code&gt;client_max_body_size&lt;/code&gt;, qui vaut 1&amp;nbsp;Mo par défaut selon la &lt;a href="https://nginx.org/en/docs/http/ngx_http_core_module.html" rel="noopener noreferrer"&gt;documentation de nginx&lt;/a&gt;, dans le bloc &lt;code&gt;server&lt;/code&gt; ou dans la seule &lt;code&gt;location&lt;/code&gt; qui reçoit des fichiers. Vérifiez ensuite la limite de l'application, qui peut être plus basse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les certificats Let's Encrypt sont-ils toujours valables 90 jours ?
&lt;/h3&gt;

&lt;p&gt;Oui pour l'instant avec le profil par défaut, &lt;code&gt;classic&lt;/code&gt;. Le &lt;a href="https://letsencrypt.org/2025/12/02/from-90-to-45" rel="noopener noreferrer"&gt;calendrier de Let's Encrypt&lt;/a&gt; prévoit 64&amp;nbsp;jours à partir du 10 février 2027 et 45&amp;nbsp;jours à partir du 16 février 2028. Un &lt;code&gt;certbot renew&lt;/code&gt; planifié chaque jour absorbe ce changement sans modification.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Tutoriel fondé sur des sources publiques consultées le 30 octobre 2026&amp;nbsp;: nginx.org, Docker, Certbot, Node.js, Let's Encrypt, Cloudflare, Express, Helmet, Nginx Proxy Manager, Caddy et Traefik. Le Dockerfile reprend celui de notre guide Docker. Aucune commande n'a été exécutée, Docker n'étant pas installé sur le poste de rédaction&amp;nbsp;: la syntaxe nginx n'a pas été validée par &lt;code&gt;nginx -t&lt;/code&gt;, seulement relue directive par directive contre la documentation de nginx.org. Les citations en anglais sont traduites par nos soins. Aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux éditeurs cités.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou une version périmée&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 30 octobre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Scott Rodgerson (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>docker</category>
      <category>devops</category>
      <category>https</category>
    </item>
    <item>
      <title>SQL débutant : le cours pas à pas et 12 exercices corrigés</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sat, 31 Oct 2026 13:42:13 +0000</pubDate>
      <link>https://dev.to/neurau_tech/sql-debutant-le-cours-pas-a-pas-et-12-exercices-corriges-2di2</link>
      <guid>https://dev.to/neurau_tech/sql-debutant-le-cours-pas-a-pas-et-12-exercices-corriges-2di2</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Ce cours SQL débutant part d'une petite boutique en ligne&amp;nbsp;: trois tables, une vingtaine de lignes, et un script à copier pour la créer chez vous. Chaque notion est illustrée par une requête et par le résultat exact qu'elle renvoie, puis 12 exercices corrigés vous font pratiquer. Toutes les requêtes ont été exécutées sur SQLite avant publication.&lt;/p&gt;

&lt;p&gt;Il traite aussi la sécurité, les valeurs NULL et les écarts entre SQLite, PostgreSQL et MySQL.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL est le deuxième langage de l'enquête Stack Overflow 2026, à 58,4&amp;nbsp;%, et PostgreSQL, SQLite et MySQL y sont les trois bases les plus utilisées.&lt;/li&gt;
&lt;li&gt;Une requête de lecture s'écrit toujours dans cet ordre&amp;nbsp;: &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;FROM&lt;/code&gt;, &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt;, &lt;code&gt;HAVING&lt;/code&gt;, &lt;code&gt;ORDER BY&lt;/code&gt;, &lt;code&gt;LIMIT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INNER JOIN&lt;/code&gt; ne garde que les lignes qui ont une correspondance, &lt;code&gt;LEFT JOIN&lt;/code&gt; garde toutes les lignes de la table de gauche.&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;UPDATE&lt;/code&gt; ou un &lt;code&gt;DELETE&lt;/code&gt; sans &lt;code&gt;WHERE&lt;/code&gt; touche toute la table&amp;nbsp;: travaillez dans une transaction.&lt;/li&gt;
&lt;li&gt;Ne collez jamais une saisie utilisateur dans une requête&amp;nbsp;: passez-la en paramètre.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Pourquoi apprendre SQL en 2026
&lt;/h2&gt;

&lt;p&gt;SQL est le langage commun des bases de données relationnelles, et son usage reste stable. L'enquête annuelle de Stack Overflow a été menée du 23 juin au 5 août 2026 et retient 30&amp;nbsp;903 réponses valides (&lt;a href="https://survey.stackoverflow.co/2026/methodology" rel="noopener noreferrer"&gt;méthodologie de l'enquête 2026&lt;/a&gt;, consultée le 30 octobre 2026). SQL y est utilisé par 58,4&amp;nbsp;% des 14&amp;nbsp;099 répondants à la question sur les langages, au coude-à-coude avec HTML/CSS et Python, derrière JavaScript à 62,0&amp;nbsp;% (&lt;a href="https://survey.stackoverflow.co/2026/technology" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2026&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;L'édition 2025 donnait 58,6&amp;nbsp;% pour SQL, sur un échantillon différent (&lt;a href="https://survey.stackoverflow.co/2025/technology" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2025&lt;/a&gt;, consulté le 30 octobre 2026)&amp;nbsp;: un usage stable, pas une hausse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5sr1208k8o0vk9k3u1s6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5sr1208k8o0vk9k3u1s6.webp" alt="Graphique en sucettes&amp;nbsp;: SQL est le deuxième langage le plus utilisé dans l'enquête Stack Overflow 2026, à 58,4&amp;nbsp;%, au coude-à-coude avec HTML/CSS et Python et derrière JavaScript à 62,0&amp;nbsp;%, d'où l'intérêt d'un cours SQL débutant" width="799" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SQL est déclaratif&amp;nbsp;: vous décrivez le résultat voulu, et la base choisit comment l'obtenir. C'est ce qui le rend abordable pour un profil SQL débutant, même sans autre langage derrière.&lt;/p&gt;

&lt;p&gt;Pour comparer les métiers, notre analyse des &lt;a href="https://neurautech.com/salaires-developpeurs-france-2026-grille-complete/" rel="noopener noreferrer"&gt;salaires des développeurs en France&lt;/a&gt; recoupe six études.&lt;/p&gt;




&lt;h2&gt;
  
  
  Préparer votre poste en cinq minutes
&lt;/h2&gt;

&lt;p&gt;Le plus simple pour débuter est SQLite&amp;nbsp;: la base tient dans un seul fichier, sans processus serveur, un choix assumé par sa documentation (&lt;a href="https://www.sqlite.org/quirks.html" rel="noopener noreferrer"&gt;SQLite, Quirks&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Deux options, au choix&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L'outil en ligne de commande &lt;code&gt;sqlite3&lt;/code&gt;.&lt;/strong&gt; Il se télécharge sur la &lt;a href="https://www.sqlite.org/download.html" rel="noopener noreferrer"&gt;page officielle de SQLite&lt;/a&gt; (paquet «&amp;nbsp;sqlite-tools&amp;nbsp;» pour Windows, macOS ou Linux). Lancez &lt;code&gt;sqlite3 boutique.db&lt;/code&gt;, tapez &lt;code&gt;PRAGMA foreign_keys = ON;&lt;/code&gt; pour activer les clés étrangères (à refaire à chaque ouverture), puis &lt;code&gt;.read boutique.sql&lt;/code&gt; pour charger le script, et &lt;code&gt;.mode box&lt;/code&gt; pour afficher des tableaux lisibles. Notre guide des &lt;a href="https://neurautech.com/commandes-linux-de-base/" rel="noopener noreferrer"&gt;commandes Linux de base&lt;/a&gt; aide à démarrer avec le terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python, déjà installé chez beaucoup.&lt;/strong&gt; Le module &lt;code&gt;sqlite3&lt;/code&gt; fait partie de la bibliothèque standard. Le code ci-dessous crée la base à partir du script, clés étrangères activées.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boutique.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PRAGMA foreign_keys = ON&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executescript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boutique.sql&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si vous découvrez Python, notre guide pour &lt;a href="https://neurautech.com/apprendre-python-guide-complet-pour-debutants/" rel="noopener noreferrer"&gt;apprendre Python de zéro&lt;/a&gt; couvre l'installation.&lt;/p&gt;




&lt;h2&gt;
  
  
  La base d'exemple : clients, produits, commandes
&lt;/h2&gt;

&lt;p&gt;La base contient trois tables&amp;nbsp;: &lt;code&gt;clients&lt;/code&gt;, &lt;code&gt;produits&lt;/code&gt; et &lt;code&gt;commandes&lt;/code&gt;, chaque commande reliant un client à un produit. Enregistrez ce script sous le nom &lt;code&gt;boutique.sql&lt;/code&gt;. Il est écrit en SQL portable (les trois bases acceptent aussi &lt;code&gt;DROP TABLE IF EXISTS&lt;/code&gt;, qui n'est pas dans la norme) et fonctionne tel quel sur SQLite&amp;nbsp;; les adaptations pour PostgreSQL et MySQL sont détaillées plus bas. Les trois &lt;code&gt;DROP TABLE IF EXISTS&lt;/code&gt; du début effacent les tables existantes&amp;nbsp;: relancer le script remet donc la base dans son état d'origine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;               &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;nom&lt;/span&gt;              &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ville&lt;/span&gt;            &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt;            &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;date_inscription&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;        &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;nom&lt;/span&gt;       &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;categorie&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;prix&lt;/span&gt;      &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;            &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;client_id&lt;/span&gt;     &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;produit_id&lt;/span&gt;    &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;quantite&lt;/span&gt;      &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantite&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;date_commande&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;produit_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ville&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_inscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Alice Martin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'Paris'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="s1"&gt;'alice@exemple.fr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-01-15'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Karim Benali'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'Lyon'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="s1"&gt;'karim@exemple.fr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-02-03'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Sophie Durand'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Paris'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="s1"&gt;'sophie@exemple.fr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2026-03-20'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Lucas Petit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s1"&gt;'Marseille'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="s1"&gt;'2026-04-11'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Emma Leroy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="s1"&gt;'emma@exemple.fr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s1"&gt;'2026-05-02'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Yanis Moreau'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'Lyon'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="s1"&gt;'yanis@exemple.fr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-06-18'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categorie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Clavier mécanique'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'Informatique'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Souris sans fil'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="s1"&gt;'Informatique'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Écran 27 pouces'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="s1"&gt;'Informatique'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;249&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Cahier A5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="s1"&gt;'Papeterie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Stylos gel (x10)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s1"&gt;'Papeterie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Lampe de bureau'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="s1"&gt;'Mobilier'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Chaise ergonomique'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mobilier'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="mi"&gt;199&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;produit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_commande&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-07-02'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-07-02'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-07-15'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2026-08-01'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-08-01'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-08-20'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-09-05'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-09-12'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-09-30'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'2026-10-03'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deux pièges sont posés exprès&amp;nbsp;: Lucas n'a pas d'e-mail et Emma pas de ville. Ils serviront dans la partie sur NULL. Yanis n'a rien commandé et la chaise n'a jamais été vendue&amp;nbsp;: les jointures les feront apparaître.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lire des données : SELECT, WHERE, ORDER BY, LIMIT
&lt;/h2&gt;

&lt;p&gt;Une requête de lecture dit quelles colonnes afficher (&lt;code&gt;SELECT&lt;/code&gt;), depuis quelle table (&lt;code&gt;FROM&lt;/code&gt;), pour quelles lignes (&lt;code&gt;WHERE&lt;/code&gt;), dans quel ordre (&lt;code&gt;ORDER BY&lt;/code&gt;) et combien (&lt;code&gt;LIMIT&lt;/code&gt;). L'astérisque sélectionne toutes les colonnes&amp;nbsp;; en pratique, nommez celles dont vous avez besoin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ville&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Paris'&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alice Martin&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:alice@exemple.fr"&gt;alice@exemple.fr&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sophie Durand&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:sophie@exemple.fr"&gt;sophie@exemple.fr&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Les chaînes s'écrivent entre apostrophes simples. Les guillemets doubles servent aux noms de colonnes dans la norme&amp;nbsp;; SQLite les accepte pour des chaînes, mais sa documentation le classe parmi ses bizarreries, car une faute de frappe dans un nom de colonne devient alors une chaîne au lieu d'une erreur (&lt;a href="https://www.sqlite.org/quirks.html" rel="noopener noreferrer"&gt;SQLite, Quirks&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WHERE&lt;/code&gt; combine les conditions avec &lt;code&gt;AND&lt;/code&gt;, &lt;code&gt;OR&lt;/code&gt; et &lt;code&gt;NOT&lt;/code&gt;, et dispose de raccourcis utiles&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Opérateur&lt;/th&gt;
&lt;th&gt;Exemple&lt;/th&gt;
&lt;th&gt;Ce qu'il garde&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;=&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;prix &amp;lt; 100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Comparaison simple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ville IN ('Lyon', 'Marseille')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Une valeur parmi une liste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BETWEEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date_commande BETWEEN '2026-08-01' AND '2026-08-31'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Un intervalle, bornes comprises&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LIKE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nom LIKE 'S%'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Un motif&amp;nbsp;: &lt;code&gt;%&lt;/code&gt; pour n'importe quelle suite, &lt;code&gt;_&lt;/code&gt; pour un caractère&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IS NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;email IS NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Une valeur absente&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pour trier et limiter, ajoutez &lt;code&gt;ORDER BY&lt;/code&gt; puis &lt;code&gt;LIMIT&lt;/code&gt;. Voici les trois produits les plus chers&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;prix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Écran 27 pouces&lt;/td&gt;
&lt;td&gt;249&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chaise ergonomique&lt;/td&gt;
&lt;td&gt;199&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clavier mécanique&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SQLite affiche &lt;code&gt;249&lt;/code&gt; et non &lt;code&gt;249.00&lt;/code&gt;&amp;nbsp;: il ne stocke pas de type décimal fixe, nous y revenons plus bas. Sans &lt;code&gt;ORDER BY&lt;/code&gt;, l'ordre des lignes n'est pas garanti. La documentation de PostgreSQL insiste&amp;nbsp;: avec &lt;code&gt;LIMIT&lt;/code&gt;, un tri qui donne un ordre unique est nécessaire, sinon vous obtenez un sous-ensemble imprévisible des lignes (&lt;a href="https://www.postgresql.org/docs/current/queries-limit.html" rel="noopener noreferrer"&gt;PostgreSQL 18, LIMIT et OFFSET&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Enfin, &lt;code&gt;SELECT DISTINCT ville FROM clients&lt;/code&gt; renvoie chaque ville une seule fois. Sur notre base, il renvoie quatre lignes&amp;nbsp;: Lyon, Marseille, Paris, plus une ligne NULL pour Emma, car &lt;code&gt;DISTINCT&lt;/code&gt; traite NULL comme une valeur à part entière.&lt;/p&gt;




&lt;h2&gt;
  
  
  Compter et regrouper : GROUP BY et HAVING
&lt;/h2&gt;

&lt;p&gt;Les fonctions d'agrégat résument plusieurs lignes en une valeur&amp;nbsp;: &lt;code&gt;COUNT&lt;/code&gt;, &lt;code&gt;SUM&lt;/code&gt;, &lt;code&gt;AVG&lt;/code&gt;, &lt;code&gt;MIN&lt;/code&gt; et &lt;code&gt;MAX&lt;/code&gt;. Sans &lt;code&gt;GROUP BY&lt;/code&gt;, elles portent sur toute la table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;nb_produits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;moins_cher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;plus_cher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;prix_moyen&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nb_produits&lt;/th&gt;
&lt;th&gt;moins_cher&lt;/th&gt;
&lt;th&gt;plus_cher&lt;/th&gt;
&lt;th&gt;prix_moyen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;4.5&lt;/td&gt;
&lt;td&gt;249&lt;/td&gt;
&lt;td&gt;88.39&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;GROUP BY&lt;/code&gt; calcule un agrégat par groupe. Ici, le nombre de produits et le prix moyen par catégorie&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;categorie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;nb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;prix_moyen&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;categorie&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;nb&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categorie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;categorie&lt;/th&gt;
&lt;th&gt;nb&lt;/th&gt;
&lt;th&gt;prix_moyen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Informatique&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;121.17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobilier&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;119.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Papeterie&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;8.63&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La règle à retenir&amp;nbsp;: toute colonne du &lt;code&gt;SELECT&lt;/code&gt; qui n'est pas dans un agrégat doit figurer dans le &lt;code&gt;GROUP BY&lt;/code&gt;. SQLite tolère qu'on l'oublie et prend alors une valeur d'une des lignes du groupe (&lt;a href="https://www.sqlite.org/quirks.html" rel="noopener noreferrer"&gt;SQLite, Quirks&lt;/a&gt;). PostgreSQL refuse ce genre de requête (&lt;a href="https://www.postgresql.org/docs/current/sql-select.html" rel="noopener noreferrer"&gt;PostgreSQL 18, SELECT&lt;/a&gt;), tout comme MySQL 8.4, dont le mode SQL par défaut inclut &lt;code&gt;ONLY_FULL_GROUP_BY&lt;/code&gt; (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/sql-mode.html" rel="noopener noreferrer"&gt;MySQL 8.4, modes SQL&lt;/a&gt;). Prenez la bonne habitude dès le départ.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WHERE&lt;/code&gt; filtre les lignes avant le regroupement, &lt;code&gt;HAVING&lt;/code&gt; filtre les groupes après. Les clients qui ont passé au moins trois commandes&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;nb_commandes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;client_id&lt;/th&gt;
&lt;th&gt;nb_commandes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Croiser les tables : INNER JOIN et LEFT JOIN
&lt;/h2&gt;

&lt;p&gt;Une jointure assemble les lignes de deux tables qui partagent une valeur, ici l'identifiant du client ou du produit. Sans elle, la table &lt;code&gt;commandes&lt;/code&gt; ne montre que des numéros.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;produit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantite&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;montant&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;  &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;produit_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date_commande&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2026-09-01'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date_commande&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;client&lt;/th&gt;
&lt;th&gt;produit&lt;/th&gt;
&lt;th&gt;quantite&lt;/th&gt;
&lt;th&gt;montant&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lucas Petit&lt;/td&gt;
&lt;td&gt;Lampe de bureau&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alice Martin&lt;/td&gt;
&lt;td&gt;Cahier A5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;22.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emma Leroy&lt;/td&gt;
&lt;td&gt;Clavier mécanique&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sophie Durand&lt;/td&gt;
&lt;td&gt;Écran 27 pouces&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;249&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Les alias (&lt;code&gt;co&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;) lèvent l'ambiguïté entre les colonnes &lt;code&gt;nom&lt;/code&gt; et &lt;code&gt;id&lt;/code&gt; des trois tables. Les dates au format ISO &lt;code&gt;AAAA-MM-JJ&lt;/code&gt; se comparent comme des chaînes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;INNER JOIN&lt;/code&gt; écarte les lignes sans correspondance&amp;nbsp;: Yanis, qui n'a rien commandé, n'apparaît jamais. &lt;code&gt;LEFT JOIN&lt;/code&gt; garde toutes les lignes de la table de gauche et remplit les colonnes manquantes avec NULL&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;nb_commandes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;nb_commandes&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;nb_commandes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alice Martin&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sophie Durand&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Karim Benali&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emma Leroy&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lucas Petit&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yanis Moreau&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notez &lt;code&gt;COUNT(co.id)&lt;/code&gt;&amp;nbsp;: &lt;code&gt;COUNT(*)&lt;/code&gt; compterait 1 pour Yanis, dont la ligne existe même sans commande. Pour ne garder que les clients sans commande, ajoutez &lt;code&gt;WHERE co.id IS NULL&lt;/code&gt;&amp;nbsp;: la requête renvoie alors Yanis Moreau seul.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RIGHT JOIN&lt;/code&gt; est le miroir de &lt;code&gt;LEFT JOIN&lt;/code&gt;. La documentation de MySQL recommande &lt;code&gt;LEFT JOIN&lt;/code&gt; pour que le code reste portable (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/join.html" rel="noopener noreferrer"&gt;MySQL 8.4, JOIN&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;




&lt;h2&gt;
  
  
  Modifier les données sans rien casser
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;INSERT&lt;/code&gt; ajoute des lignes, &lt;code&gt;UPDATE&lt;/code&gt; les modifie et &lt;code&gt;DELETE&lt;/code&gt; les supprime. Les deux derniers agissent sur toutes les lignes que sélectionne leur &lt;code&gt;WHERE&lt;/code&gt;, et sur toute la table s'il n'y en a pas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ville&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_inscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Inès Garcia'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Toulouse'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ines@exemple.fr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2026-10-30'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;ville&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Bordeaux'&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sans &lt;code&gt;id&lt;/code&gt; fourni, SQLite a attribué le numéro 7 à la nouvelle cliente&amp;nbsp;; l'&lt;code&gt;UPDATE&lt;/code&gt; puis le &lt;code&gt;DELETE&lt;/code&gt; ont chacun touché une ligne.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Attention.&lt;/strong&gt; Avant tout &lt;code&gt;UPDATE&lt;/code&gt; ou &lt;code&gt;DELETE&lt;/code&gt;, lancez un &lt;code&gt;SELECT&lt;/code&gt; avec le même &lt;code&gt;WHERE&lt;/code&gt; et vérifiez le nombre de lignes. Travaillez dans une transaction&amp;nbsp;: si le résultat vous surprend, &lt;code&gt;ROLLBACK&lt;/code&gt; annule tout. Faites une copie de la base avant une opération en production.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- oubli du WHERE : 7 lignes modifiées&lt;/span&gt;
&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                       &lt;span class="c1"&gt;-- les prix d'origine sont rétablis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous avons exécuté ce scénario&amp;nbsp;: l'&lt;code&gt;UPDATE&lt;/code&gt; a modifié les 7 produits, et le &lt;code&gt;ROLLBACK&lt;/code&gt; a rendu les prix d'origine. &lt;code&gt;COMMIT&lt;/code&gt; valide la transaction quand le résultat est correct.&lt;/p&gt;




&lt;h2&gt;
  
  
  Clés primaires, clés étrangères et index
&lt;/h2&gt;

&lt;p&gt;La clé primaire identifie chaque ligne de façon unique&amp;nbsp;: ici la colonne &lt;code&gt;id&lt;/code&gt;. La clé étrangère garantit qu'une valeur pointe vers une ligne existante&amp;nbsp;: &lt;code&gt;commandes.client_id&lt;/code&gt; doit correspondre à un &lt;code&gt;clients.id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sur notre base, avec les clés étrangères actives, deux opérations échouent avec l'erreur &lt;code&gt;FOREIGN KEY constraint failed&lt;/code&gt;&amp;nbsp;: supprimer Alice, qui a des commandes, et créer une commande pour un client 99 qui n'existe pas. De même, insérer un second client avec l'adresse d'Alice échoue sur la contrainte &lt;code&gt;UNIQUE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Le piège propre à SQLite&amp;nbsp;: les clés étrangères sont désactivées par défaut, pour des raisons de compatibilité, et doivent être activées à chaque connexion avec &lt;code&gt;PRAGMA foreign_keys = ON&lt;/code&gt; (&lt;a href="https://www.sqlite.org/foreignkeys.html" rel="noopener noreferrer"&gt;SQLite, Foreign Key Support&lt;/a&gt;, consulté le 30 octobre 2026). Sans cette ligne, notre commande pour le client 99 a été acceptée sans erreur. Le module &lt;code&gt;node:sqlite&lt;/code&gt; de Node.js fait l'inverse&amp;nbsp;: son option &lt;code&gt;enableForeignKeyConstraints&lt;/code&gt; vaut &lt;code&gt;true&lt;/code&gt; par défaut (&lt;a href="https://nodejs.org/api/sqlite.html" rel="noopener noreferrer"&gt;documentation de Node.js&lt;/a&gt;, consultée le 30 octobre 2026), ce dont profite notre tutoriel d'&lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;API REST avec Node.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PostgreSQL applique les clés étrangères d'office (&lt;a href="https://www.postgresql.org/docs/current/ddl-constraints.html" rel="noopener noreferrer"&gt;PostgreSQL 18, contraintes&lt;/a&gt;). MySQL aussi avec InnoDB, son moteur par défaut, puisque la variable &lt;code&gt;foreign_key_checks&lt;/code&gt; y est activée par défaut, à une condition&amp;nbsp;: selon la documentation de MySQL 8.4, il analyse puis ignore les &lt;code&gt;REFERENCES&lt;/code&gt; écrits dans la définition d'une colonne, et n'accepte que des clauses &lt;code&gt;FOREIGN KEY (...) REFERENCES ...&lt;/code&gt; séparées (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/create-table.html" rel="noopener noreferrer"&gt;MySQL 8.4, CREATE TABLE&lt;/a&gt; et &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/create-table-foreign-keys.html" rel="noopener noreferrer"&gt;clés étrangères&lt;/a&gt;). Notre script utilise donc cette forme, valable sur les trois bases.&lt;/p&gt;

&lt;p&gt;Un index accélère la recherche sur une colonne, comme l'index d'un livre. La commande &lt;code&gt;EXPLAIN QUERY PLAN&lt;/code&gt; de SQLite montre la différence&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="n"&gt;QUERY&lt;/span&gt; &lt;span class="n"&gt;PLAN&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- SCAN commandes          (lecture de toute la table)&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_commandes_client&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="n"&gt;QUERY&lt;/span&gt; &lt;span class="n"&gt;PLAN&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- SEARCH commandes USING INDEX idx_commandes_client (client_id=?)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un index a un coût&amp;nbsp;: selon la documentation de PostgreSQL, la base doit le tenir à jour, ce qui alourdit les écritures (&lt;a href="https://www.postgresql.org/docs/current/indexes-intro.html" rel="noopener noreferrer"&gt;PostgreSQL 18, index&lt;/a&gt;, consulté le 30 octobre 2026). Indexez les colonnes souvent filtrées ou jointes, pas toutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les pièges de NULL
&lt;/h2&gt;

&lt;p&gt;NULL ne signifie pas zéro ni chaîne vide, mais «&amp;nbsp;valeur inconnue&amp;nbsp;». Toute comparaison avec NULL donne NULL, donc ni vrai ni faux. La documentation de MySQL le dit en une phrase&amp;nbsp;: on ne peut pas tester NULL avec &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt; ou &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/working-with-null.html" rel="noopener noreferrer"&gt;MySQL 8.4, Working with NULL Values&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Cinq pièges, tous vérifiés sur notre base&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requête&lt;/th&gt;
&lt;th&gt;Résultat obtenu&lt;/th&gt;
&lt;th&gt;Explication&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE ville = NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0 ligne&lt;/td&gt;
&lt;td&gt;La comparaison donne NULL&amp;nbsp;; il faut &lt;code&gt;IS NULL&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE ville IS NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Emma Leroy&lt;/td&gt;
&lt;td&gt;La bonne écriture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE ville &amp;lt;&amp;gt; 'Paris'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3 lignes, sans Emma&lt;/td&gt;
&lt;td&gt;Une ville inconnue n'est pas «&amp;nbsp;différente de Paris&amp;nbsp;»&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;COUNT(*)&lt;/code&gt;, &lt;code&gt;COUNT(email)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;6 et 5&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COUNT(colonne)&lt;/code&gt; ignore les NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHERE id NOT IN (SELECT NULL UNION SELECT 1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0 ligne&lt;/td&gt;
&lt;td&gt;Un seul NULL dans la liste vide un &lt;code&gt;NOT IN&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pour remplacer une valeur absente à l'affichage, utilisez &lt;code&gt;COALESCE(ville, 'inconnue')&lt;/code&gt;, qui renvoie le premier argument non NULL. Pour inclure Emma dans la recherche, écrivez &lt;code&gt;WHERE ville IS NULL OR ville &amp;lt;&amp;gt; 'Paris'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Le tri des NULL varie selon la base. SQLite les considère plus petits que toute autre valeur, donc en tête d'un tri croissant (&lt;a href="https://www.sqlite.org/lang_select.html" rel="noopener noreferrer"&gt;SQLite, SELECT&lt;/a&gt;)&amp;nbsp;; MySQL aussi (&lt;a href="https://dev.mysql.com/doc/refman/8.4/en/working-with-null.html" rel="noopener noreferrer"&gt;MySQL 8.4, Working with NULL Values&lt;/a&gt;). PostgreSQL les traite comme plus grands, donc en fin de tri croissant (&lt;a href="https://www.postgresql.org/docs/current/queries-order.html" rel="noopener noreferrer"&gt;PostgreSQL 18, tri&lt;/a&gt;). &lt;code&gt;NULLS FIRST&lt;/code&gt; ou &lt;code&gt;NULLS LAST&lt;/code&gt; fixent le comportement là où ils existent.&lt;/p&gt;




&lt;h2&gt;
  
  
  L'injection SQL et les requêtes paramétrées
&lt;/h2&gt;

&lt;p&gt;L'injection SQL survient quand une saisie utilisateur est collée dans le texte d'une requête&amp;nbsp;: l'attaquant écrit du SQL au lieu d'une valeur.&lt;/p&gt;

&lt;p&gt;Ce programme Python a été exécuté sur notre base&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boutique.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;saisie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; OR &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# ce qu'un attaquant tape dans le formulaire
&lt;/span&gt;
&lt;span class="c1"&gt;# À NE JAMAIS FAIRE : la saisie devient du code SQL
&lt;/span&gt;&lt;span class="n"&gt;requete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT nom, email FROM clients WHERE email = &lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;saisie&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"'"&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requete&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;   &lt;span class="c1"&gt;# 6 : toute la table
&lt;/span&gt;
&lt;span class="c1"&gt;# La bonne méthode : un paramètre lié par le pilote
&lt;/span&gt;&lt;span class="n"&gt;lignes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT nom, email FROM clients WHERE email = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;saisie&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lignes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;                             &lt;span class="c1"&gt;# 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La première version produit &lt;code&gt;WHERE email = 'x' OR '1'='1'&lt;/code&gt;, une condition toujours vraie&amp;nbsp;: les six clients et leurs e-mails fuient. La seconde envoie la saisie comme une simple valeur, qui ne correspond à aucun e-mail.&lt;/p&gt;

&lt;p&gt;L'OWASP place les requêtes préparées avec paramètres en tête des défenses, et classe l'échappement manuel des saisies comme «&amp;nbsp;fortement déconseillé&amp;nbsp;» (notre traduction) (&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP, SQL Injection Prevention Cheat Sheet&lt;/a&gt;, consulté le 30 octobre 2026). La documentation de Python demande elle aussi d'utiliser toujours les marqueurs plutôt que des chaînes formatées (&lt;a href="https://docs.python.org/fr/3/library/sqlite3.html" rel="noopener noreferrer"&gt;module sqlite3&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Le principe vaut dans tous les langages. Notre tutoriel pour &lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;créer une API REST avec Node.js&lt;/a&gt; l'applique à une base SQLite&amp;nbsp;: toutes ses requêtes passent par &lt;code&gt;db.prepare()&lt;/code&gt; avec des paramètres &lt;code&gt;?&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  SQLite, PostgreSQL et MySQL : ce qui change
&lt;/h2&gt;

&lt;p&gt;Les trois bases parlent SQL, mais pas exactement le même. PostgreSQL arrive en tête de l'enquête Stack Overflow 2026 avec 57,9&amp;nbsp;% des 12&amp;nbsp;061 répondants à la question sur les bases, devant SQLite à 42,1&amp;nbsp;% et MySQL à 35,1&amp;nbsp;% (&lt;a href="https://survey.stackoverflow.co/2026/technology" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2026&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhebw71t6pty6lwflgd38.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhebw71t6pty6lwflgd38.webp" alt="Graphique en barres&amp;nbsp;: bases de données les plus utilisées dans l'enquête Stack Overflow 2026, PostgreSQL à 57,9&amp;nbsp;%, SQLite à 42,1&amp;nbsp;%, MySQL à 35,1&amp;nbsp;%, devant Redis et Microsoft SQL Server" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Les écarts qui comptent pour un débutant, relevés dans la documentation officielle de chaque base&amp;nbsp;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Point&lt;/th&gt;
&lt;th&gt;SQLite&lt;/th&gt;
&lt;th&gt;PostgreSQL&lt;/th&gt;
&lt;th&gt;MySQL 8.4&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Identifiant automatique&lt;/td&gt;
&lt;td&gt;&lt;code&gt;INTEGER PRIMARY KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GENERATED ALWAYS AS IDENTITY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AUTO_INCREMENT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clés étrangères&lt;/td&gt;
&lt;td&gt;Désactivées par défaut&lt;/td&gt;
&lt;td&gt;Actives&lt;/td&gt;
&lt;td&gt;Actives avec InnoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Types&lt;/td&gt;
&lt;td&gt;Souples, sauf tables &lt;code&gt;STRICT&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Stricts&lt;/td&gt;
&lt;td&gt;Stricts avec le mode par défaut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NULL dans un tri croissant&lt;/td&gt;
&lt;td&gt;En tête&lt;/td&gt;
&lt;td&gt;En fin&lt;/td&gt;
&lt;td&gt;En tête&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Depuis la version 3.39.0&lt;/td&gt;
&lt;td&gt;Oui&lt;/td&gt;
&lt;td&gt;Absent de la syntaxe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`\&lt;/td&gt;
&lt;td&gt;\&lt;/td&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;td&gt;Concatène&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Les sources de ce tableau&amp;nbsp;: les pages &lt;a href="https://www.sqlite.org/quirks.html" rel="noopener noreferrer"&gt;Quirks&lt;/a&gt;, &lt;a href="https://www.sqlite.org/autoinc.html" rel="noopener noreferrer"&gt;AUTOINCREMENT&lt;/a&gt; et &lt;a href="https://www.sqlite.org/releaselog/3_39_0.html" rel="noopener noreferrer"&gt;notes de version 3.39.0&lt;/a&gt; de SQLite&amp;nbsp;; les pages &lt;a href="https://www.postgresql.org/docs/current/ddl-identity-columns.html" rel="noopener noreferrer"&gt;colonnes d'identité&lt;/a&gt;, &lt;a href="https://www.postgresql.org/docs/current/ddl-constraints.html" rel="noopener noreferrer"&gt;contraintes&lt;/a&gt;, &lt;a href="https://www.postgresql.org/docs/current/typeconv-overview.html" rel="noopener noreferrer"&gt;conversion de types&lt;/a&gt; et &lt;a href="https://www.postgresql.org/docs/current/sql-select.html" rel="noopener noreferrer"&gt;SELECT&lt;/a&gt; de PostgreSQL&amp;nbsp;; les pages &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/example-auto-increment.html" rel="noopener noreferrer"&gt;AUTO_INCREMENT&lt;/a&gt;, &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/sql-mode.html" rel="noopener noreferrer"&gt;modes SQL&lt;/a&gt; et &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/logical-operators.html" rel="noopener noreferrer"&gt;opérateurs logiques&lt;/a&gt; de MySQL, toutes consultées le 30 octobre 2026. La page de conversion de types de PostgreSQL rappelle que SQL est un langage fortement typé&amp;nbsp;; le mode SQL par défaut de MySQL 8.4 inclut &lt;code&gt;STRICT_TRANS_TABLES&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pour rejouer notre script sur PostgreSQL ou MySQL, le point délicat est l'identifiant. Comme les &lt;code&gt;id&lt;/code&gt; y sont écrits à la main, c'est surtout l'&lt;code&gt;INSERT&lt;/code&gt; sans &lt;code&gt;id&lt;/code&gt; du chapitre suivant qui demande une adaptation.&lt;/p&gt;

&lt;p&gt;Sur PostgreSQL, une colonne &lt;code&gt;GENERATED ALWAYS AS IDENTITY&lt;/code&gt; refuse les &lt;code&gt;id&lt;/code&gt; écrits à la main, sauf avec &lt;code&gt;OVERRIDING SYSTEM VALUE&lt;/code&gt;. Avec &lt;code&gt;GENERATED BY DEFAULT&lt;/code&gt;, la valeur fournie l'emporte, mais elle ne passe pas par la séquence, et la documentation prévient qu'une colonne d'identité ne garantit alors plus l'unicité. Le plus simple&amp;nbsp;: déclarez les &lt;code&gt;id&lt;/code&gt; en identité et retirez-les des trois &lt;code&gt;INSERT&lt;/code&gt; (&lt;code&gt;clients&lt;/code&gt;, &lt;code&gt;produits&lt;/code&gt; et &lt;code&gt;commandes&lt;/code&gt;)&amp;nbsp;; sur une table neuve, les lignes reçoivent en pratique les mêmes numéros. Sur MySQL, ajoutez &lt;code&gt;AUTO_INCREMENT&lt;/code&gt; aux &lt;code&gt;id&lt;/code&gt;&amp;nbsp;: il accepte les valeurs écrites à la main et reprend ensuite la numérotation après la plus grande.&lt;/p&gt;

&lt;p&gt;La souplesse de SQLite surprend&amp;nbsp;: il a accepté le texte &lt;code&gt;'gratuit'&lt;/code&gt; dans la colonne &lt;code&gt;prix&lt;/code&gt;, déclarée &lt;code&gt;DECIMAL&lt;/code&gt;, ce que sa documentation assume. Elle note aussi que &lt;code&gt;upper()&lt;/code&gt; et &lt;code&gt;lower()&lt;/code&gt; ne traitent que les caractères ASCII&amp;nbsp;: sur notre base, &lt;code&gt;lower('Écran')&lt;/code&gt; renvoie &lt;code&gt;Écran&lt;/code&gt;, la majuscule accentuée reste.&lt;/p&gt;

&lt;p&gt;Enfin, &lt;code&gt;LIMIT&lt;/code&gt; n'est pas standard&amp;nbsp;: la norme SQL:2008 prévoit &lt;code&gt;FETCH FIRST n ROWS ONLY&lt;/code&gt;, selon la page &lt;code&gt;SELECT&lt;/code&gt; de PostgreSQL. Pour lancer PostgreSQL sans l'installer, notre tutoriel &lt;a href="https://neurautech.com/docker-pour-debutants/" rel="noopener noreferrer"&gt;Docker pour débutants&lt;/a&gt; le démarre dans Docker Compose.&lt;/p&gt;




&lt;h2&gt;
  
  
  12 exercices SQL débutant corrigés
&lt;/h2&gt;

&lt;p&gt;Ces 12 exercices SQL débutant portent sur la base d'exemple. Relancez le script &lt;code&gt;boutique.sql&lt;/code&gt; avant de commencer, pour repartir des données d'origine, cherchez chaque requête sans regarder, puis comparez votre résultat au tableau. Toutes les solutions ont été exécutées dans l'ordre, sur la base d'origine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exercice 1 : les produits de papeterie
&lt;/h3&gt;

&lt;p&gt;Affichez le nom et le prix des produits de la catégorie Papeterie.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;categorie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Papeterie'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;prix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cahier A5&lt;/td&gt;
&lt;td&gt;4.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stylos gel (x10)&lt;/td&gt;
&lt;td&gt;12.75&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Exercice 2 : les deux derniers inscrits
&lt;/h3&gt;

&lt;p&gt;Affichez le nom et la date d'inscription des deux clients inscrits le plus récemment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_inscription&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;date_inscription&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;date_inscription&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Yanis Moreau&lt;/td&gt;
&lt;td&gt;2026-06-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emma Leroy&lt;/td&gt;
&lt;td&gt;2026-05-02&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Exercice 3 : une fourchette de prix
&lt;/h3&gt;

&lt;p&gt;Listez les produits entre 20 et 100 euros, du moins cher au plus cher.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;prix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Souris sans fil&lt;/td&gt;
&lt;td&gt;25.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lampe de bureau&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clavier mécanique&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Exercice 4 : les clients sans e-mail
&lt;/h3&gt;

&lt;p&gt;Trouvez les clients dont l'e-mail n'est pas renseigné.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lucas Petit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Écrire &lt;code&gt;email = NULL&lt;/code&gt; aurait renvoyé zéro ligne.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exercice 5 : le bilan de juillet
&lt;/h3&gt;

&lt;p&gt;Comptez les commandes passées en juillet 2026 et le nombre d'articles commandés.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;nb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;date_commande&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="s1"&gt;'2026-07-01'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="s1"&gt;'2026-07-31'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nb&lt;/th&gt;
&lt;th&gt;articles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Exercice 6 : les clients par ville
&lt;/h3&gt;

&lt;p&gt;Comptez les clients par ville, en ignorant les villes inconnues, du plus grand nombre au plus petit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ville&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;nb_clients&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ville&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ville&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;nb_clients&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ville&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ville&lt;/th&gt;
&lt;th&gt;nb_clients&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lyon&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paris&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marseille&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Exercice 7 : le total dépensé par client
&lt;/h3&gt;

&lt;p&gt;Calculez le montant total dépensé par chaque client, du plus gros au plus petit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantite&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;  &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;produit_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sophie Durand&lt;/td&gt;
&lt;td&gt;319.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Karim Benali&lt;/td&gt;
&lt;td&gt;274.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alice Martin&lt;/td&gt;
&lt;td&gt;162.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emma Leroy&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lucas Petit&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;JOIN&lt;/code&gt; seul équivaut à &lt;code&gt;INNER JOIN&lt;/code&gt;. Yanis n'apparaît pas, puisqu'il n'a rien commandé.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exercice 8 : le produit jamais vendu
&lt;/h3&gt;

&lt;p&gt;Trouvez les produits qui n'apparaissent dans aucune commande.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;produit_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chaise ergonomique&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Exercice 9 : le chiffre d'affaires par catégorie
&lt;/h3&gt;

&lt;p&gt;Calculez le chiffre d'affaires de chaque catégorie et ne gardez que celles qui dépassent 50 euros.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;categorie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantite&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;chiffre_affaires&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;produit_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;categorie&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantite&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;chiffre_affaires&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;categorie&lt;/th&gt;
&lt;th&gt;chiffre_affaires&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Informatique&lt;/td&gt;
&lt;td&gt;752.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Papeterie&lt;/td&gt;
&lt;td&gt;93.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Le mobilier, à 39 euros, est filtré par &lt;code&gt;HAVING&lt;/code&gt;. Un &lt;code&gt;WHERE&lt;/code&gt; ne pourrait pas le faire, car le total n'existe qu'après le regroupement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exercice 10 : la dernière commande de chaque client
&lt;/h3&gt;

&lt;p&gt;Affichez chaque client avec la date de sa dernière commande, y compris ceux qui n'ont jamais commandé.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date_commande&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;derniere_commande&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;commandes&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;co&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nom&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;derniere_commande&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alice Martin&lt;/td&gt;
&lt;td&gt;2026-09-12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Karim Benali&lt;/td&gt;
&lt;td&gt;2026-08-20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sophie Durand&lt;/td&gt;
&lt;td&gt;2026-10-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lucas Petit&lt;/td&gt;
&lt;td&gt;2026-09-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emma Leroy&lt;/td&gt;
&lt;td&gt;2026-09-30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yanis Moreau&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Exercice 11 : plus cher que la moyenne
&lt;/h3&gt;

&lt;p&gt;Listez les produits dont le prix dépasse le prix moyen de la table, sans écrire ce prix moyen à la main.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;prix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Écran 27 pouces&lt;/td&gt;
&lt;td&gt;249&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chaise ergonomique&lt;/td&gt;
&lt;td&gt;199&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clavier mécanique&lt;/td&gt;
&lt;td&gt;89&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La sous-requête calcule la moyenne, 88,39 euros, puis la requête principale compare chaque prix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exercice 12 : une hausse de prix en sécurité
&lt;/h3&gt;

&lt;p&gt;Augmentez de 10&amp;nbsp;% les prix du mobilier, vérifiez le résultat, puis annulez.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;categorie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Mobilier'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prix&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;produits&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;categorie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Mobilier'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;nom&lt;/th&gt;
&lt;th&gt;prix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lampe de bureau&lt;/td&gt;
&lt;td&gt;42.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chaise ergonomique&lt;/td&gt;
&lt;td&gt;218.9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;L'&lt;code&gt;UPDATE&lt;/code&gt; a touché deux lignes. Remplacez &lt;code&gt;ROLLBACK&lt;/code&gt; par &lt;code&gt;COMMIT&lt;/code&gt; pour garder la hausse.&lt;/p&gt;

&lt;p&gt;Pour continuer, passez à nos &lt;a href="https://neurautech.com/exercices-python-debutant-corriges/" rel="noopener noreferrer"&gt;19 exercices Python débutant corrigés&lt;/a&gt;, puis reliez Python et SQL avec le module &lt;code&gt;sqlite3&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Les points suivants restent ouverts&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les résultats sur PostgreSQL et MySQL.&lt;/strong&gt; Les requêtes ont été exécutées sur SQLite 3.45.1 seulement. Les écarts avec PostgreSQL et MySQL viennent de leur documentation, pas d'une exécution. L'affichage des nombres y sera différent (&lt;code&gt;249.00&lt;/code&gt; au lieu de &lt;code&gt;249&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La dernière version de SQLite.&lt;/strong&gt; La page de téléchargement proposait la version 3.54.1 le 30 octobre 2026. Nos requêtes n'utilisent aucune fonction récente, mais nous ne les avons pas rejouées sur cette version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La portée de l'enquête Stack Overflow.&lt;/strong&gt; Ses répondants sont des volontaires venus de la communauté du site, pas un échantillon représentatif de tous les développeurs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Combien de temps faut-il pour apprendre SQL quand on est débutant ?
&lt;/h3&gt;

&lt;p&gt;Selon notre estimation, pas une mesure, les notions de ce cours s'apprennent en quelques séances si vous pratiquez chaque requête. La maîtrise vient ensuite avec de vraies bases, plus grandes que notre exemple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quelle base choisir pour un cours SQL débutant ?
&lt;/h3&gt;

&lt;p&gt;SQLite pour démarrer, car il n'y a rien à configurer et toute la base tient dans un fichier. Passez ensuite à PostgreSQL, la base la plus utilisée de l'enquête Stack Overflow 2026, pour vous habituer à des types stricts et à un serveur.&lt;/p&gt;

&lt;h3&gt;
  
  
  Existe-t-il une formation SQL débutant gratuite ?
&lt;/h3&gt;

&lt;p&gt;Oui. Ce cours et ses 12 exercices sont gratuits, tout comme les documentations officielles de &lt;a href="https://www.sqlite.org/docs.html" rel="noopener noreferrer"&gt;SQLite&lt;/a&gt; et de &lt;a href="https://www.postgresql.org/docs/current/tutorial.html" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt;, dont le tutoriel couvre les mêmes bases. La Wild Code School propose aussi un &lt;a href="https://www.wildcodeschool.com/cours-gratuit-sql" rel="noopener noreferrer"&gt;cours SQL en libre accès&lt;/a&gt; d'environ 5&amp;nbsp;h&amp;nbsp;30, avec quiz.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faut-il connaître un langage de programmation avant SQL ?
&lt;/h3&gt;

&lt;p&gt;Non. SQL se pratique seul, dans l'outil &lt;code&gt;sqlite3&lt;/code&gt;. Un langage comme Python devient utile pour intégrer les requêtes dans une application, et c'est là que les requêtes paramétrées s'imposent.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Le cours et les exercices ont été écrits pour cet article. Le script de la base, chaque requête du cours et chaque solution ont été exécutés sur SQLite 3.45.1, via le module &lt;code&gt;sqlite3&lt;/code&gt; de Python 3.11.9, et les résultats recopiés tels quels. Les différences avec PostgreSQL et MySQL viennent de leur documentation officielle (PostgreSQL 18, MySQL 8.4)&amp;nbsp;; les recommandations de sécurité, de l'OWASP&amp;nbsp;; les chiffres d'usage, de l'enquête Stack Overflow 2026. Aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux éditeurs mentionnés.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur dans une solution&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 30 octobre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image&amp;nbsp;: Sunder Muthukumaran (Unsplash License). Graphiques&amp;nbsp;: NeurauTech, d'après le Stack Overflow Developer Survey 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>basededonnees</category>
      <category>sqlite</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Apprendre JavaScript en 2026 : les bases, un plan de 10 semaines et les ressources gratuites</title>
      <dc:creator>Neurau Tech</dc:creator>
      <pubDate>Sat, 31 Oct 2026 13:42:10 +0000</pubDate>
      <link>https://dev.to/neurau_tech/apprendre-javascript-en-2026-les-bases-un-plan-de-10-semaines-et-les-ressources-gratuites-3fo5</link>
      <guid>https://dev.to/neurau_tech/apprendre-javascript-en-2026-les-bases-un-plan-de-10-semaines-et-les-ressources-gratuites-3fo5</guid>
      <description>&lt;h2&gt;
  
  
  L'essentiel en une minute
&lt;/h2&gt;

&lt;p&gt;Apprendre JavaScript en 2026 demande trois outils gratuits (un navigateur, VS Code et Node.js) et une progression dans le bon ordre&amp;nbsp;: le langage d'abord, la page web ensuite, le code asynchrone en dernier. Ce guide suit cet ordre, avec la sortie exacte de chaque exemple.&lt;/p&gt;

&lt;p&gt;Tous les exemples non graphiques ont été exécutés avec Node.js avant publication, et l'exemple de page web a été exécuté dans un navigateur Chromium. Les chiffres viennent de sources lues le 30 octobre 2026, notamment l'enquête Stack Overflow 2026, le site officiel de Node.js et la documentation de MDN. Si vous hésitez encore entre deux langages, notre guide pour &lt;a href="https://neurautech.com/apprendre-python-guide-complet-pour-debutants/" rel="noopener noreferrer"&gt;apprendre Python&lt;/a&gt; suit le même format.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Points clés&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript est le langage le plus utilisé de l'enquête Stack Overflow 2026, par 62,0&amp;nbsp;% des répondants à la question, comme dans les éditions précédentes.&lt;/li&gt;
&lt;li&gt;Installez la version LTS de Node.js proposée par nodejs.org&amp;nbsp;: la 26.13.0 au 30 octobre 2026.&lt;/li&gt;
&lt;li&gt;Apprenez dans cet ordre&amp;nbsp;: variables, conditions, boucles, fonctions, tableaux et objets, puis le DOM, puis fetch et async/await, puis les modules.&lt;/li&gt;
&lt;li&gt;Quatre pièges classiques du débutant&amp;nbsp;: &lt;code&gt;==&lt;/code&gt; au lieu de &lt;code&gt;===&lt;/code&gt;, &lt;code&gt;var&lt;/code&gt;, la valeur de &lt;code&gt;this&lt;/code&gt; et l'ordre d'exécution du code asynchrone.&lt;/li&gt;
&lt;li&gt;Passez à TypeScript et à un framework quand vous savez écrire une petite application sans tutoriel, pas avant.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Pourquoi apprendre JavaScript en 2026
&lt;/h2&gt;

&lt;p&gt;JavaScript reste le langage que le plus de développeurs utilisent. Dans l'enquête Stack Overflow 2026, 62,0&amp;nbsp;% des 14&amp;nbsp;099 personnes qui ont répondu à la question sur les langages disent avoir beaucoup travaillé en JavaScript au cours de l'année. Il arrive en tête (&lt;a href="https://survey.stackoverflow.co/2026/technology/data/languages" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2026&lt;/a&gt;, consulté le 30 octobre 2026), comme dans les éditions précédentes&amp;nbsp;: l'édition 2023 parlait déjà de sa onzième année consécutive à la première place (&lt;a href="https://survey.stackoverflow.co/2023/" rel="noopener noreferrer"&gt;Stack Overflow Developer Survey 2023&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fntxkdtqmqoy2dzlpljf6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fntxkdtqmqoy2dzlpljf6.webp" alt="Graphique en barres&amp;nbsp;: JavaScript utilisé par 62,0&amp;nbsp;% des répondants de l'enquête Stack Overflow 2026, devant SQL, HTML/CSS et Python, TypeScript à 43,8&amp;nbsp;%" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TypeScript, une surcouche de JavaScript, est sixième avec 43,8&amp;nbsp;%&amp;nbsp;: apprendre JavaScript, c'est aussi préparer TypeScript.&lt;/p&gt;

&lt;p&gt;L'enquête a été menée du 23 juin au 5 août 2026 et compte 30&amp;nbsp;903 réponses valides venues de 169 pays (&lt;a href="https://survey.stackoverflow.co/2026/methodology" rel="noopener noreferrer"&gt;méthodologie de l'enquête&lt;/a&gt;). Elle mesure les habitudes des personnes qui la remplissent, pas l'ensemble du marché.&lt;/p&gt;

&lt;p&gt;Un autre indicateur donne une image différente. Selon le rapport Octoverse de GitHub, TypeScript est devenu en août 2025 le langage le plus utilisé sur GitHub en nombre de contributeurs mensuels, avec 2&amp;nbsp;636&amp;nbsp;006 contributeurs, contre 2,15&amp;nbsp;millions pour JavaScript (&lt;a href="https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/" rel="noopener noreferrer"&gt;GitHub Octoverse&lt;/a&gt;, 28 octobre 2025). Les deux mesures ne comptent pas la même chose&amp;nbsp;: l'enquête recueille un usage déclaré par des développeurs, Octoverse compte les contributeurs actifs sur GitHub, où les projets récents sont souvent écrits en TypeScript.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce qu'il faut installer
&lt;/h2&gt;

&lt;p&gt;Trois logiciels gratuits suffisent, et le premier est déjà sur votre machine.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Outil&lt;/th&gt;
&lt;th&gt;Rôle&lt;/th&gt;
&lt;th&gt;Où le trouver&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Un navigateur récent (Firefox, Chrome, Edge)&lt;/td&gt;
&lt;td&gt;Exécuter le JavaScript des pages, console de développement&lt;/td&gt;
&lt;td&gt;Déjà installé&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual Studio Code&lt;/td&gt;
&lt;td&gt;Écrire le code&lt;/td&gt;
&lt;td&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;code.visualstudio.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js, version LTS&lt;/td&gt;
&lt;td&gt;Exécuter JavaScript hors du navigateur&lt;/td&gt;
&lt;td&gt;&lt;a href="https://nodejs.org/fr/download" rel="noopener noreferrer"&gt;nodejs.org&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Le navigateur.&lt;/strong&gt; Ouvrez les outils de développement avec F12 (Cmd+Option+J dans Chrome sur macOS), puis l'onglet Console. Tapez &lt;code&gt;2 + 2&lt;/code&gt; et Entrée&amp;nbsp;: vous venez d'exécuter du JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VS Code.&lt;/strong&gt; La FAQ de l'éditeur indique qu'il est gratuit pour un usage privé ou commercial et qu'il fonctionne sous Windows, macOS et Linux (&lt;a href="https://code.visualstudio.com/docs/supporting/faq" rel="noopener noreferrer"&gt;FAQ de VS Code&lt;/a&gt;, consultée le 30 octobre 2026). Pour comparer, voyez notre comparatif des &lt;a href="https://neurautech.com/meilleurs-ide-editeurs-code-2026-comparatif/" rel="noopener noreferrer"&gt;meilleurs IDE et éditeurs de code&lt;/a&gt;. Pour bien l'équiper, notre sélection d'&lt;a href="https://neurautech.com/meilleures-extensions-vs-code-en-2026-pour-devs/" rel="noopener noreferrer"&gt;extensions VS Code&lt;/a&gt; suffit&amp;nbsp;: n'en installez pas vingt le premier jour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node.js.&lt;/strong&gt; La page de téléchargement propose par défaut la version 26.13.0, étiquetée LTS. La branche 26 est passée en LTS le 28 octobre 2026, et le site officiel précise que les applications en production ne devraient utiliser que des versions LTS (&lt;a href="https://nodejs.org/en/about/previous-releases" rel="noopener noreferrer"&gt;versions de Node.js&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz9yg3agfaqwfdq1juapo.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz9yg3agfaqwfdq1juapo.webp" alt="Calendrier des versions LTS de Node.js&amp;nbsp;: la 22 en maintenance jusqu'au 30 avril 2027, la 24 jusqu'au 30 avril 2028, la 26 en LTS active depuis le 28 octobre 2026 et maintenue jusqu'au 30 avril 2029" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Une version déjà installée peut suffire pour débuter. Les branches 22 et 24 sont encore maintenues, jusqu'au 30 avril 2027 et au 30 avril 2028 selon le &lt;a href="https://github.com/nodejs/Release" rel="noopener noreferrer"&gt;calendrier officiel&lt;/a&gt;&amp;nbsp;; la 24 est passée en maintenance le 20 octobre 2026. Le cycle change aussi&amp;nbsp;: le projet a annoncé qu'à partir de Node.js 27, il publierait une version majeure par an au lieu de deux, et que chaque version deviendrait LTS (&lt;a href="https://nodejs.org/en/blog/announcements/evolving-the-nodejs-release-schedule" rel="noopener noreferrer"&gt;blog de Node.js&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Vérifiez l'installation dans un terminal&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; v26.13.0 si vous avez installé la LTS actuelle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Les bases du langage, avec des exemples exécutés
&lt;/h2&gt;

&lt;p&gt;Chaque bloc ci-dessous tient dans un fichier, par exemple &lt;code&gt;essai.js&lt;/code&gt;, que vous lancez avec &lt;code&gt;node essai.js&lt;/code&gt;. Le commentaire &lt;code&gt;// -&amp;gt;&lt;/code&gt; indique la sortie obtenue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variables : let et const
&lt;/h3&gt;

&lt;p&gt;Utilisez &lt;code&gt;const&lt;/code&gt; par défaut et &lt;code&gt;let&lt;/code&gt; quand la valeur doit changer. Le mot-clé &lt;code&gt;var&lt;/code&gt; existe encore, mais il est source d'erreurs (voir plus bas).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prenom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Inès&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prenom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; a &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; points`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; Inès a 15 points&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les accents graves délimitent un gabarit de chaîne&amp;nbsp;: &lt;code&gt;${...}&lt;/code&gt; y insère une valeur.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les types
&lt;/h3&gt;

&lt;p&gt;JavaScript a des types (nombre, chaîne, booléen, &lt;code&gt;undefined&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;, objet), mais il les convertit tout seul, ce qui surprend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; number string boolean&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// -&amp;gt; undefined object&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nc"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;   &lt;span class="c1"&gt;// -&amp;gt; bigint symbol&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// -&amp;gt; 53 2 8&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                           &lt;span class="c1"&gt;// -&amp;gt; 0.30000000000000004&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les types &lt;code&gt;bigint&lt;/code&gt; (grands entiers) et &lt;code&gt;symbol&lt;/code&gt; (identifiants uniques) existent aussi, mais un débutant les croise rarement. &lt;code&gt;typeof null&lt;/code&gt; qui renvoie &lt;code&gt;object&lt;/code&gt; est une bizarrerie historique. Le &lt;code&gt;+&lt;/code&gt; colle les chaînes au lieu d'additionner&amp;nbsp;: convertissez avec &lt;code&gt;Number()&lt;/code&gt; les valeurs saisies par un utilisateur.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les conditions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accès autorisé&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accès avec autorisation parentale&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; s'affiche&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Accès refusé&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;statut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;majeur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mineur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statut&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; mineur&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La dernière ligne utilise l'opérateur ternaire, utile pour choisir entre deux valeurs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les boucles
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Tour &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; Tour 1, Tour 2, Tour 3 (une ligne par valeur)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;langages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JavaScript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Python&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Go&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;langage&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;langages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;langage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; JAVASCRIPT, PYTHON, GO (une ligne par valeur)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;for...of&lt;/code&gt; parcourt les valeurs d'un tableau. C'est la boucle à préférer pour une liste.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fonctions et fonctions fléchées
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;prixTTC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prixHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;taux&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prixHT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;taux&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;double&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prixTTC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;                  &lt;span class="c1"&gt;// -&amp;gt; 120&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prixTTC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.055&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 105.50&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;                    &lt;span class="c1"&gt;// -&amp;gt; 42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;taux = 0.2&lt;/code&gt; est une valeur par défaut. La fonction fléchée &lt;code&gt;(x) =&amp;gt; x * 2&lt;/code&gt; est une écriture courte, très utilisée avec les tableaux. MDN détaille ses différences avec une fonction classique, notamment pour &lt;code&gt;this&lt;/code&gt; (&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Functions/Arrow_functions" rel="noopener noreferrer"&gt;fonctions fléchées sur MDN&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Tableaux et objets
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;titre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Apprendre JavaScript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;vues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;publie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;titre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vues&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; Apprendre JavaScript 120&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reussies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bonus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moyenne&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;somme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;somme&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reussies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bonus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moyenne&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; [ 12, 15, 17 ] [ 13, 16, 9, 18 ] 13&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;titre&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vues&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;titre&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;vues&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; vues`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; Apprendre JavaScript : 120 vues&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; et &lt;code&gt;reduce&lt;/code&gt; remplacent la plupart des boucles sur des listes&amp;nbsp;; la déstructuration extrait des propriétés en une ligne. Ces trois méthodes reviennent dans presque tout le code moderne&amp;nbsp;: passez du temps dessus.&lt;/p&gt;




&lt;h2&gt;
  
  
  JavaScript dans la page : DOM et événements
&lt;/h2&gt;

&lt;p&gt;Le DOM est la représentation de la page HTML que JavaScript peut lire et modifier. Créez un fichier &lt;code&gt;index.html&lt;/code&gt; et ouvrez-le dans le navigateur&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"bouton"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cliquer&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"compteur"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0 clic&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bouton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#bouton&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;texte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#compteur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;clics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;bouton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;clics&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;texte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;clics&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; clic&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;clics&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Après trois clics, le paragraphe affiche «&amp;nbsp;3 clics&amp;nbsp;». MDN l'explique dans son &lt;a href="https://developer.mozilla.org/fr/docs/Web/API/Document_Object_Model" rel="noopener noreferrer"&gt;introduction au DOM&lt;/a&gt; et sa page sur &lt;a href="https://developer.mozilla.org/fr/docs/Web/API/EventTarget/addEventListener" rel="noopener noreferrer"&gt;addEventListener&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ce code ne fonctionne pas dans Node.js&amp;nbsp;: &lt;code&gt;document&lt;/code&gt; n'existe que dans le navigateur.&lt;/p&gt;




&lt;h2&gt;
  
  
  Le code asynchrone : fetch, async et await
&lt;/h2&gt;

&lt;p&gt;Une requête réseau prend du temps, et JavaScript n'attend pas&amp;nbsp;: il continue d'exécuter la suite. &lt;code&gt;async&lt;/code&gt; et &lt;code&gt;await&lt;/code&gt; permettent d'écrire ce code dans l'ordre où on le lit. Fichier &lt;code&gt;tache.mjs&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;chargerTache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;reponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Erreur HTTP &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;reponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;chargerTache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 1 delectus aut autem false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;erreur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Échec :&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;erreur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;fetch&lt;/code&gt; ne lève pas d'erreur sur une réponse 404&amp;nbsp;: il faut tester &lt;code&gt;reponse.ok&lt;/code&gt;. Le &lt;code&gt;try...catch&lt;/code&gt; attrape les pannes réseau. Et &lt;code&gt;await&lt;/code&gt; hors d'une fonction n'est accepté que dans un module, d'où l'extension &lt;code&gt;.mjs&lt;/code&gt;. Le guide &lt;a href="https://developer.mozilla.org/fr/docs/Web/API/Fetch_API/Using_Fetch" rel="noopener noreferrer"&gt;Utiliser l'API Fetch&lt;/a&gt; de MDN va plus loin.&lt;/p&gt;

&lt;p&gt;Quand vous saurez consommer une API, l'étape suivante est d'en écrire une. Notre tutoriel pour &lt;a href="https://neurautech.com/creer-une-api-rest-avec-node-js-et-express-en-2026/" rel="noopener noreferrer"&gt;créer une API REST avec Node.js et Express&lt;/a&gt; part exactement de ce niveau.&lt;/p&gt;




&lt;h2&gt;
  
  
  Organiser son code en modules
&lt;/h2&gt;

&lt;p&gt;Un module est un fichier qui exporte des fonctions ou des valeurs, et que d'autres fichiers importent. Deux fichiers dans le même dossier&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// calcul.mjs&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;somme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TVA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;somme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TVA&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./calcul.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;somme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;TVA&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 5 0.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;node main.mjs&lt;/code&gt; affiche &lt;code&gt;5 0.2&lt;/code&gt;. Dans un navigateur, la balise doit porter &lt;code&gt;type="module"&lt;/code&gt;, et la page doit être servie par un serveur local, par exemple l'extension Live Server de VS Code ou &lt;code&gt;npx serve&lt;/code&gt;. Le &lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer"&gt;guide des modules de MDN&lt;/a&gt; prévient qu'un fichier HTML ouvert directement (adresse &lt;code&gt;file://&lt;/code&gt;) provoque des erreurs CORS.&lt;/p&gt;




&lt;h2&gt;
  
  
  Un plan réaliste en 10 semaines
&lt;/h2&gt;

&lt;p&gt;Ce plan suppose environ une heure par jour. Les durées sont une proposition de notre part, pas une mesure&amp;nbsp;: nous n'avons trouvé aucune étude fiable sur le temps nécessaire pour apprendre JavaScript.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Semaines&lt;/th&gt;
&lt;th&gt;Notions&lt;/th&gt;
&lt;th&gt;Livrable à la fin&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Console, variables, types, conditions&lt;/td&gt;
&lt;td&gt;Un convertisseur de températures en console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Boucles, fonctions, fonctions fléchées&lt;/td&gt;
&lt;td&gt;Un jeu «&amp;nbsp;devinez le nombre&amp;nbsp;»&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Tableaux, objets, filter, map, reduce&lt;/td&gt;
&lt;td&gt;Un calcul de statistiques sur une liste de notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;HTML et DOM, événements&lt;/td&gt;
&lt;td&gt;Un compteur et une liste de tâches dans la page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Formulaires, validation, localStorage&lt;/td&gt;
&lt;td&gt;La liste de tâches qui survit au rechargement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Asynchrone, promesses, fetch, async/await&lt;/td&gt;
&lt;td&gt;Une page météo ou de recherche avec gestion d'erreur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Modules, npm, débogage dans VS Code&lt;/td&gt;
&lt;td&gt;Le projet de la semaine 6 découpé en modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Node.js côté serveur&lt;/td&gt;
&lt;td&gt;Un script qui lit un fichier JSON et produit un rapport&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9 et 10&lt;/td&gt;
&lt;td&gt;Projet personnel, Git, mise en ligne&lt;/td&gt;
&lt;td&gt;Une application complète publiée&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Et en 30&amp;nbsp;jours&amp;nbsp;?&lt;/strong&gt; Prenons un défi connu, &lt;a href="https://github.com/Asabeneh/30-Days-Of-JavaScript" rel="noopener noreferrer"&gt;30 Days of JavaScript&lt;/a&gt;, publié sur GitHub par Asabeneh Yetayeh, en anglais. Ses jours 1 à 20 couvrent les types, les conditions, les boucles, les fonctions, les objets, les classes, JSON et les promesses&amp;nbsp;; les jours 21 à 23 le DOM et les événements&amp;nbsp;; les suivants sont des mini-projets. Son auteur prévient lui-même que le défi peut prendre plus de 100&amp;nbsp;jours et invite à suivre son propre rythme (notre traduction). Ses 23 premiers jours couvrent donc à peu près les semaines 1 à 7 de notre plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;De A à Z&amp;nbsp;?&lt;/strong&gt; Personne ne connaît tout JavaScript. Le bon critère est pratique&amp;nbsp;: savoir construire seul les livrables des semaines 4 à 6 sans copier un tutoriel. Les projets des semaines 9 et 10 alimentent ensuite votre &lt;a href="https://neurautech.com/portfolio-developpeur-comment-le-construire-2026/" rel="noopener noreferrer"&gt;portfolio de développeur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pour la méthode générale (exercices, notes, projets), notre guide Python et ses &lt;a href="https://neurautech.com/exercices-python-debutant-corriges/" rel="noopener noreferrer"&gt;exercices corrigés&lt;/a&gt; proposent une démarche qui se transpose telle quelle à JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fji1z2vgetp20b7ah1m3a.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fji1z2vgetp20b7ah1m3a.webp" alt="Lignes de code JavaScript colorées sur un écran sombre, le quotidien de celui qui veut apprendre JavaScript" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Les ressources gratuites en français
&lt;/h2&gt;

&lt;p&gt;Vous pouvez apprendre JavaScript gratuitement et en français&amp;nbsp;: les ressources ci-dessous ont été ouvertes le 30 octobre 2026.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ressource&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Accès&lt;/th&gt;
&lt;th&gt;Pour quoi faire&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.mozilla.org/fr/docs/Learn_web_development/Core/Scripting" rel="noopener noreferrer"&gt;MDN, Premiers pas en JavaScript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Cours écrit, 8 articles et une auto-évaluation&lt;/td&gt;
&lt;td&gt;Gratuit&lt;/td&gt;
&lt;td&gt;Le parcours de départ, si vous connaissez déjà HTML et CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide" rel="noopener noreferrer"&gt;MDN, Guide JavaScript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Guide en 16 chapitres&lt;/td&gt;
&lt;td&gt;Gratuit&lt;/td&gt;
&lt;td&gt;La référence à garder ouverte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://openclassrooms.com/fr/courses/7696886-apprenez-a-programmer-avec-javascript" rel="noopener noreferrer"&gt;OpenClassrooms, Apprenez à programmer avec JavaScript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Cours de 12&amp;nbsp;heures avec quiz&lt;/td&gt;
&lt;td&gt;Libre accès&lt;/td&gt;
&lt;td&gt;Un premier cours guidé, jusqu'au formulaire&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://fr.javascript.info/" rel="noopener noreferrer"&gt;javascript.info en français&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tutoriel écrit, projet open source&lt;/td&gt;
&lt;td&gt;Lecture en ligne libre&lt;/td&gt;
&lt;td&gt;Approfondir le langage puis le navigateur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grafikart.fr/formations/formation-javascript" rel="noopener noreferrer"&gt;Grafikart, Apprendre le JavaScript&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;23 vidéos, 7&amp;nbsp;h&amp;nbsp;28 annoncées&lt;/td&gt;
&lt;td&gt;En partie gratuit, abonnement Premium (vidéos réservées non précisées)&lt;/td&gt;
&lt;td&gt;Apprendre en vidéo, des bases aux promesses et à fetch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Quelques précisions. La page de MDN suppose que vous connaissez HTML et CSS et indique que la traduction est communautaire. OpenClassrooms a mis à jour son cours le 29 juillet 2025.&lt;/p&gt;

&lt;p&gt;Grafikart annonce rendre «&amp;nbsp;un maximum de contenu gratuit et public&amp;nbsp;» et vend un abonnement Premium à 5&amp;nbsp;€ par mois ou 50&amp;nbsp;€ par an, qui donne entre autres accès à des vidéos réservées (&lt;a href="https://grafikart.fr/premium" rel="noopener noreferrer"&gt;page Premium de Grafikart&lt;/a&gt;). La page de la formation n'indique pas lesquelles le sont.&lt;/p&gt;

&lt;p&gt;En anglais, le cours &lt;a href="https://web.dev/learn/javascript" rel="noopener noreferrer"&gt;Learn JavaScript de web.dev&lt;/a&gt;, publié par Google, couvre les types, les fonctions, les objets, les collections et les classes. Pour les PDF «&amp;nbsp;JavaScript de A à Z&amp;nbsp;» qui remontent dans les résultats de recherche, vérifiez qu'un auteur et une date sont indiqués avant de vous y fier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Les 4 erreurs classiques du débutant
&lt;/h2&gt;

&lt;p&gt;Ces quatre pièges reviennent souvent chez les débutants. Chaque sortie ci-dessous a été obtenue en exécutant le code.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Comparer avec == au lieu de ===
&lt;/h3&gt;

&lt;p&gt;L'opérateur &lt;code&gt;==&lt;/code&gt; convertit les types avant de comparer, &lt;code&gt;===&lt;/code&gt; non.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// -&amp;gt; true true false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; false true false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Comparaison&lt;/th&gt;
&lt;th&gt;Avec ==&lt;/th&gt;
&lt;th&gt;Avec ===&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; et &lt;code&gt;""&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; et &lt;code&gt;"0"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;null&lt;/code&gt; et &lt;code&gt;undefined&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Avec &lt;code&gt;==&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt; égale &lt;code&gt;""&lt;/code&gt; et &lt;code&gt;"0"&lt;/code&gt;, mais &lt;code&gt;""&lt;/code&gt; n'égale pas &lt;code&gt;"0"&lt;/code&gt;&amp;nbsp;: la relation n'est même pas cohérente. Utilisez toujours &lt;code&gt;===&lt;/code&gt; et &lt;code&gt;!==&lt;/code&gt; (&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Operators/Strict_equality" rel="noopener noreferrer"&gt;égalité stricte sur MDN&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Utiliser var
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;var&lt;/code&gt; a une portée de fonction, pas de bloc. Dans une boucle, toutes les itérations partagent la même variable&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;var :&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; var : 3, var : 3, var : 3 (une ligne par valeur)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;let :&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; let : 0, let : 1, let : 2 (une ligne par valeur)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avec &lt;code&gt;let&lt;/code&gt;, chaque tour a sa propre variable. Il n'y a plus de raison d'écrire &lt;code&gt;var&lt;/code&gt; dans du code neuf (&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Statements/let" rel="noopener noreferrer"&gt;let sur MDN&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Perdre la valeur de this
&lt;/h3&gt;

&lt;p&gt;Dans une fonction classique ou une méthode, &lt;code&gt;this&lt;/code&gt; dépend de la façon dont la fonction est appelée, pas de l'endroit où elle est écrite. Une fonction fléchée, elle, garde le &lt;code&gt;this&lt;/code&gt; de l'endroit où elle est écrite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compteur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;valeur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;incrementer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valeur&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valeur&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compteur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incrementer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 1&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detachee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compteur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;incrementer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// detachee(); // ligne commentée exprès, elle lève :&lt;/span&gt;
&lt;span class="c1"&gt;// TypeError: Cannot read properties of undefined (reading 'valeur')&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;liee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compteur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;incrementer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compteur&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;liee&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// -&amp;gt; 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La méthode copiée dans une variable a perdu son objet&amp;nbsp;; &lt;code&gt;bind&lt;/code&gt; le lui rend. À l'inverse, une fonction fléchée passée à &lt;code&gt;forEach&lt;/code&gt; ou à &lt;code&gt;setTimeout&lt;/code&gt; à l'intérieur d'une méthode garde le &lt;code&gt;this&lt;/code&gt; de cette méthode. Le cas se produit surtout avec les gestionnaires d'événements et les fonctions de rappel (&lt;a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Operators/this" rel="noopener noreferrer"&gt;this sur MDN&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Mal lire l'ordre du code asynchrone
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A. début du script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;D. minuteur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;C. promesse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;B. fin du script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie est A, B, C, D, même avec un délai de 0&amp;nbsp;ms. Le code synchrone passe d'abord, puis les promesses, puis les minuteurs. Retenez qu'une valeur obtenue avec &lt;code&gt;await&lt;/code&gt; n'existe pas avant la ligne qui l'attend.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quand passer à TypeScript et à un framework
&lt;/h2&gt;

&lt;p&gt;Passez à TypeScript quand vos projets dépassent quelques fichiers et que vous perdez du temps sur des erreurs de type. TypeScript ajoute des annotations de type à JavaScript, vérifiées avant l'exécution. Sa &lt;a href="https://www.typescriptlang.org/fr/docs/" rel="noopener noreferrer"&gt;documentation en français&lt;/a&gt; propose un tutoriel de cinq minutes pour qui connaît déjà JavaScript.&lt;/p&gt;

&lt;p&gt;Node.js facilite la transition. Depuis la version 22.18.0, il exécute un fichier &lt;code&gt;.ts&lt;/code&gt; sans option, à condition que le code n'utilise que la syntaxe TypeScript effaçable, comme les annotations et les interfaces. Il ne vérifie pas les types&amp;nbsp;: la documentation recommande de lancer &lt;code&gt;npx tsc --noEmit&lt;/code&gt; à part (&lt;a href="https://nodejs.org/learn/typescript/run-natively" rel="noopener noreferrer"&gt;TypeScript dans Node.js&lt;/a&gt;, consulté le 30 octobre 2026).&lt;/p&gt;

&lt;p&gt;Pour un framework (React, Vue, Svelte), notre critère est simple&amp;nbsp;: vous devez savoir écrire la liste de tâches de la semaine 5 avec le DOM, sans aide. Sinon, les erreurs du framework resteront opaques.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ce que nous n'avons pas pu vérifier
&lt;/h2&gt;

&lt;p&gt;Points incertains ou non testés&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les exemples sous Node.js 26.&lt;/strong&gt; Ils ont été exécutés avec Node.js 22.15.0, la version installée sur notre poste, et non avec la 26.13.0 recommandée. Ils n'utilisent que des fonctions standard présentes dans les deux versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le statut gratuit de chaque vidéo de Grafikart.&lt;/strong&gt; La page Premium mentionne des vidéos réservées sans dire lesquelles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La durée d'apprentissage.&lt;/strong&gt; Le plan de 10&amp;nbsp;semaines est une proposition éditoriale. Nous n'avons trouvé aucune étude fiable sur le temps nécessaire pour apprendre JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La représentativité de l'enquête Stack Overflow.&lt;/strong&gt; Ses 30&amp;nbsp;903 réponses viennent de personnes qui fréquentent Stack Overflow&amp;nbsp;; elles ne décrivent pas tout le marché, ni la France en particulier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le nombre d'années consécutives en tête.&lt;/strong&gt; La page 2026 de Stack Overflow parle d'une «&amp;nbsp;12e année consécutive&amp;nbsp;», mot pour mot comme l'édition 2025, alors que l'édition 2023 parlait déjà de la onzième. Nous n'avons pas retenu ce décompte.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La complétude des traductions françaises&lt;/strong&gt; de MDN et de javascript.info&amp;nbsp;: certaines pages peuvent être moins à jour que leur version anglaise.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Questions fréquentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Peut-on apprendre JavaScript gratuitement ?
&lt;/h3&gt;

&lt;p&gt;Oui. Le parcours &lt;a href="https://developer.mozilla.org/fr/docs/Learn_web_development/Core/Scripting" rel="noopener noreferrer"&gt;Premiers pas en JavaScript de MDN&lt;/a&gt;, le cours d'OpenClassrooms en libre accès et le tutoriel javascript.info sont gratuits et en français. Les outils le sont aussi&amp;nbsp;: VS Code, Node.js et le navigateur.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combien de temps faut-il pour apprendre JavaScript ?
&lt;/h3&gt;

&lt;p&gt;Aucune étude fiable ne donne de durée. Notre plan propose 10&amp;nbsp;semaines, à raison d'une heure par jour, jusqu'à une première application complète.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faut-il apprendre HTML et CSS avant JavaScript ?
&lt;/h3&gt;

&lt;p&gt;Pour le web, oui, au moins les bases&amp;nbsp;: le parcours de MDN les suppose acquises. Pour du JavaScript côté serveur avec Node.js, ce n'est pas nécessaire, et vous pouvez commencer par la console.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quelle version de Node.js installer en 2026 ?
&lt;/h3&gt;

&lt;p&gt;La version LTS proposée par &lt;a href="https://nodejs.org/fr/download" rel="noopener noreferrer"&gt;nodejs.org&lt;/a&gt;, soit la 26.13.0 au 30 octobre 2026. Les branches 22 et 24 restent maintenues jusqu'en avril 2027 et avril 2028.&lt;/p&gt;




&lt;h2&gt;
  
  
  À propos de l'auteur
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Abdelilah Ourti&lt;/strong&gt; est ingénieur IA et architecte logiciel. Il conçoit et déploie des applications web et des systèmes d'IA, et publie sur NeurauTech des guides et des comparatifs destinés aux développeurs francophones. Ses autres articles sont réunis sur &lt;a href="https://neurautech.com/author/neurautech/" rel="noopener noreferrer"&gt;sa page auteur&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Méthode.&lt;/strong&gt; Synthèse fondée sur des sources publiques&amp;nbsp;: enquête Stack Overflow 2026, rapport Octoverse de GitHub, site et calendrier officiels de Node.js, documentation de MDN, de VS Code et de TypeScript, pages des cours cités. Les exemples de code ont été exécutés avec Node.js 22.15.0 et l'exemple de page web dans le navigateur Chromium 120&amp;nbsp;; les sorties affichées sont celles obtenues. Aucun cours n'a été suivi en entier, aucun lien n'est affilié et aucune relation commerciale ne lie l'auteur aux plateformes mentionnées.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Une erreur ou un lien périmé&amp;nbsp;?&lt;/strong&gt; Écrivez à &lt;a href="mailto:contact@neurautech.com"&gt;contact@neurautech.com&lt;/a&gt;&amp;nbsp;: l'article sera corrigé et la date de mise à jour modifiée.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dernière mise à jour&amp;nbsp;: 30 octobre 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Images&amp;nbsp;: Gabriel Heinzer et Markus Spiske (Unsplash License).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>debutant</category>
      <category>node</category>
      <category>developpementweb</category>
    </item>
  </channel>
</rss>
