[{"data":1,"prerenderedAt":10248},["ShallowReactive",2],{"blog-count-fr":3,"blog-list-fr-1":4},12,[5,1286,1903,4902,6675,6938],{"id":6,"title":7,"body":8,"date":1273,"description":1274,"extension":1275,"img":1276,"meta":1277,"navigation":1096,"path":1278,"seo":1279,"slug":1280,"stem":1281,"tags":1282,"topics":1276,"__hash__":1285},"content\u002F3.security\u002F1.introducing-obscura.fr.md","Comment un entretien m'a poussé à créer Obscura — un générateur de mots de passe avec une vraie entropie",{"type":9,"value":10,"toc":1258},"minimark",[11,16,20,28,32,35,51,54,58,61,64,74,77,88,91,97,100,112,115,123,126,130,133,150,155,166,303,307,310,565,568,572,575,960,964,967,981,984,988,994,1190,1193,1197,1200,1203,1220,1224,1227,1230,1233,1237,1254],[12,13,15],"h2",{"id":14},"introduction","Introduction",[17,18,19],"p",{},"Lors d'un entretien récent pour un poste d'ingénieur logiciel sécurité, j'ai reçu un défi :\ncréer un générateur de mots de passe sécurisé qui prenne en compte la véritable entropie.\nL'interviewer voulait voir comment j'allais aborder le problème, et j'étais ravi de me lancer.",[17,21,22,23,27],{},"Au départ, je pensais que ce serait simple. J'avais déjà utilisé des générateurs de mots de passe\n(à base de ",[24,25,26],"code",{},"Math.random"," et compagnie), et je supposais qu'il serait facile d'en créer un sûr.\nMais la conversation s'est rapidement éloignée des implémentations superficielles pour aller vers\nun sujet plus profond et souvent négligé : l'entropie — et la façon dont la plupart des\nprétendus générateurs ne la traitent pas correctement.",[12,29,31],{"id":30},"le-défi","Le défi",[17,33,34],{},"L'interviewer insistait sur le fait que l'entropie devait être mesurable, ajustable, et prévisible\nselon les choix de l'utilisateur. Il ne cherchait pas un simple outil qui balance des caractères\naléatoires ; il voulait un générateur capable de :",[36,37,38,42,45,48],"ul",{},[39,40,41],"li",{},"quantifier l'entropie de l'information du mot de passe généré,",[39,43,44],{},"s'adapter aux contraintes du pool de caractères (exclure les doublons ou les caractères spéciaux),",[39,46,47],{},"éviter les motifs et séquences communs (par ex. « 123 », « qwerty » ou « abc »),",[39,49,50],{},"et tout en restant simple d'usage et robuste face aux attaques par force brute.",[17,52,53],{},"Ça m'a fait réfléchir au fait que la majorité des générateurs de mots de passe ne sont, au fond,\nque des générateurs de caractères aléatoires un peu glorifiés.",[12,55,57],{"id":56},"mon-raisonnement-comprendre-lentropie","Mon raisonnement : comprendre l'entropie",[17,59,60],{},"J'ai commencé par lui demander la définition de l'entropie dans le contexte de la génération de\nmots de passe. Il m'a expliqué que l'entropie est une mesure de l'incertitude ou du caractère\naléatoire d'un système. Pour les mots de passe, elle désigne le degré d'imprévisibilité du mot\nde passe lui-même. Plus un mot de passe est imprévisible, plus son entropie est élevée, et plus\nil est résistant aux attaques par force brute. (Je me suis dit : allez mec, t'étais censé me faire\ncomprendre le concept, pas le compliquer 🙁 — parce que je ne savais ni le mesurer, ni le calculer.)",[17,62,63],{},"Après quelques minutes de discussion, il m'a donné la formule de l'entropie, simple mais puissante :",[65,66,71],"pre",{"className":67,"code":69,"language":70},[68],"language-text","E = log2(P^L)\n","text",[24,72,69],{"__ignoreMap":73},"",[17,75,76],{},"Où :",[36,78,79,82,85],{},[39,80,81],{},"E est l'entropie en bits",[39,83,84],{},"P est la taille du pool de caractères (par ex. 26 pour les minuscules, 10 pour les chiffres, etc.)",[39,86,87],{},"L est la longueur du mot de passe",[17,89,90],{},"Et voici l'exemple de calcul qu'il m'a donné. Pour un mot de passe utilisant 26 lettres\nminuscules et une longueur de 8 :",[65,92,95],{"className":93,"code":94,"language":70},[68],"E = log2(26^8) = 8 * log2(26) ≈ 37.6 bits\n",[24,96,94],{"__ignoreMap":73},[17,98,99],{},"Un mot de passe fort doit avoir au moins 80 bits d'entropie, donc je devais m'assurer que mon\ngénérateur augmente l'entropie en utilisant :",[101,102,103,106,109],"ol",{},[39,104,105],{},"Un pool de caractères plus large (majuscules, minuscules, chiffres, caractères spéciaux)",[39,107,108],{},"Une longueur de mot de passe plus importante",[39,110,111],{},"Un vrai aléa pour la sélection des caractères (pas du pseudo-aléa)",[17,113,114],{},"Après tous ces détails, j'ai compris deux choses :",[36,116,117,120],{},[39,118,119],{},"Plus le pool est grand et plus le mot de passe est long, plus il est solide.",[39,121,122],{},"L'entropie se mesure en bits, et plus de bits signifie plus de combinaisons possibles.",[17,124,125],{},"Mais si on exclut les caractères similaires, qu'on impose l'unicité ou qu'on commence par un\ncaractère spécifique, le pool rétrécit — et l'entropie diminue. Il faut donc calculer l'entropie\ndynamiquement en fonction des choix actuels de l'utilisateur.",[12,127,129],{"id":128},"la-solution-construire-un-vrai-générateur-de-mots-de-passe","La solution : construire un vrai générateur de mots de passe",[17,131,132],{},"J'ai écrit la première version du générateur en TypeScript, en me concentrant uniquement sur la\nlogique. Il propose des options comme :",[36,134,135,138,141,144,147],{},[39,136,137],{},"inclure\u002Fexclure majuscules, minuscules, chiffres, caractères spéciaux",[39,139,140],{},"exclure les caractères similaires (par ex. « l » et « 1 », « O » et « 0 »)",[39,142,143],{},"éviter les doublons et les motifs séquentiels",[39,145,146],{},"générer plusieurs mots de passe à la fois",[39,148,149],{},"et surtout : le calcul d'entropie en temps réel",[151,152,154],"h3",{"id":153},"approche-de-conception","Approche de conception",[17,156,157,158,161,162,165],{},"Mon implémentation suit une approche modulaire autour de la fonction centrale ",[24,159,160],{},"generatePassword",".\nJ'ai défini une interface ",[24,163,164],{},"PasswordOptions"," claire pour rendre le code plus lisible et fortement\ntypé.",[65,167,171],{"className":168,"code":169,"language":170,"meta":73,"style":73},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export interface PasswordOptions {\n  length: number\n  includeLowercase: boolean\n  includeUppercase: boolean\n  includeDigits: boolean\n  includeSymbols: boolean\n  excludeSimilarCharacters: boolean\n  noDuplicateCharacters: boolean\n  noSequentialCharacters: boolean\n  beginWithLetter: boolean\n  quantity: number\n}\n","typescript",[24,172,173,194,207,218,228,238,248,258,268,278,288,298],{"__ignoreMap":73},[174,175,178,182,186,190],"span",{"class":176,"line":177},"line",1,[174,179,181],{"class":180},"s7zQu","export",[174,183,185],{"class":184},"spNyl"," interface",[174,187,189],{"class":188},"sBMFI"," PasswordOptions",[174,191,193],{"class":192},"sMK4o"," {\n",[174,195,197,201,204],{"class":176,"line":196},2,[174,198,200],{"class":199},"swJcz","  length",[174,202,203],{"class":192},":",[174,205,206],{"class":188}," number\n",[174,208,210,213,215],{"class":176,"line":209},3,[174,211,212],{"class":199},"  includeLowercase",[174,214,203],{"class":192},[174,216,217],{"class":188}," boolean\n",[174,219,221,224,226],{"class":176,"line":220},4,[174,222,223],{"class":199},"  includeUppercase",[174,225,203],{"class":192},[174,227,217],{"class":188},[174,229,231,234,236],{"class":176,"line":230},5,[174,232,233],{"class":199},"  includeDigits",[174,235,203],{"class":192},[174,237,217],{"class":188},[174,239,241,244,246],{"class":176,"line":240},6,[174,242,243],{"class":199},"  includeSymbols",[174,245,203],{"class":192},[174,247,217],{"class":188},[174,249,251,254,256],{"class":176,"line":250},7,[174,252,253],{"class":199},"  excludeSimilarCharacters",[174,255,203],{"class":192},[174,257,217],{"class":188},[174,259,261,264,266],{"class":176,"line":260},8,[174,262,263],{"class":199},"  noDuplicateCharacters",[174,265,203],{"class":192},[174,267,217],{"class":188},[174,269,271,274,276],{"class":176,"line":270},9,[174,272,273],{"class":199},"  noSequentialCharacters",[174,275,203],{"class":192},[174,277,217],{"class":188},[174,279,281,284,286],{"class":176,"line":280},10,[174,282,283],{"class":199},"  beginWithLetter",[174,285,203],{"class":192},[174,287,217],{"class":188},[174,289,291,294,296],{"class":176,"line":290},11,[174,292,293],{"class":199},"  quantity",[174,295,203],{"class":192},[174,297,206],{"class":188},[174,299,300],{"class":176,"line":3},[174,301,302],{"class":192},"}\n",[151,304,306],{"id":305},"construction-du-pool-de-caractères","Construction du pool de caractères",[17,308,309],{},"Le socle du code est la sélection du pool de caractères. J'ai défini des constantes par jeu de\ncaractères et écrit une fonction utilitaire pour construire un pool personnalisé selon les\npréférences de l'utilisateur :",[65,311,313],{"className":168,"code":312,"language":170,"meta":73,"style":73},"function getCharacterPool(options: PasswordOptions): string {\n  let pool = ''\n  if (options.includeLowercase) pool += LOWERCASE\n  if (options.includeUppercase) pool += UPPERCASE\n  if (options.includeDigits) pool += DIGITS\n  if (options.includeSymbols) pool += SYMBOLS\n  if (options.excludeSimilarCharacters) {\n    pool = pool\n      .split('')\n      .filter((c) => !SIMILAR.includes(c))\n      .join('')\n  }\n  return pool\n}\n",[24,314,315,343,358,386,408,430,452,470,480,496,534,547,552,560],{"__ignoreMap":73},[174,316,317,320,324,327,331,333,335,338,341],{"class":176,"line":177},[174,318,319],{"class":184},"function",[174,321,323],{"class":322},"s2Zo4"," getCharacterPool",[174,325,326],{"class":192},"(",[174,328,330],{"class":329},"sHdIc","options",[174,332,203],{"class":192},[174,334,189],{"class":188},[174,336,337],{"class":192},"):",[174,339,340],{"class":188}," string",[174,342,193],{"class":192},[174,344,345,348,352,355],{"class":176,"line":196},[174,346,347],{"class":184},"  let",[174,349,351],{"class":350},"sTEyZ"," pool",[174,353,354],{"class":192}," =",[174,356,357],{"class":192}," ''\n",[174,359,360,363,366,368,371,374,377,380,383],{"class":176,"line":209},[174,361,362],{"class":180},"  if",[174,364,365],{"class":199}," (",[174,367,330],{"class":350},[174,369,370],{"class":192},".",[174,372,373],{"class":350},"includeLowercase",[174,375,376],{"class":199},") ",[174,378,379],{"class":350},"pool",[174,381,382],{"class":192}," +=",[174,384,385],{"class":350}," LOWERCASE\n",[174,387,388,390,392,394,396,399,401,403,405],{"class":176,"line":220},[174,389,362],{"class":180},[174,391,365],{"class":199},[174,393,330],{"class":350},[174,395,370],{"class":192},[174,397,398],{"class":350},"includeUppercase",[174,400,376],{"class":199},[174,402,379],{"class":350},[174,404,382],{"class":192},[174,406,407],{"class":350}," UPPERCASE\n",[174,409,410,412,414,416,418,421,423,425,427],{"class":176,"line":230},[174,411,362],{"class":180},[174,413,365],{"class":199},[174,415,330],{"class":350},[174,417,370],{"class":192},[174,419,420],{"class":350},"includeDigits",[174,422,376],{"class":199},[174,424,379],{"class":350},[174,426,382],{"class":192},[174,428,429],{"class":350}," DIGITS\n",[174,431,432,434,436,438,440,443,445,447,449],{"class":176,"line":240},[174,433,362],{"class":180},[174,435,365],{"class":199},[174,437,330],{"class":350},[174,439,370],{"class":192},[174,441,442],{"class":350},"includeSymbols",[174,444,376],{"class":199},[174,446,379],{"class":350},[174,448,382],{"class":192},[174,450,451],{"class":350}," SYMBOLS\n",[174,453,454,456,458,460,462,465,467],{"class":176,"line":250},[174,455,362],{"class":180},[174,457,365],{"class":199},[174,459,330],{"class":350},[174,461,370],{"class":192},[174,463,464],{"class":350},"excludeSimilarCharacters",[174,466,376],{"class":199},[174,468,469],{"class":192},"{\n",[174,471,472,475,477],{"class":176,"line":260},[174,473,474],{"class":350},"    pool",[174,476,354],{"class":192},[174,478,479],{"class":350}," pool\n",[174,481,482,485,488,490,493],{"class":176,"line":270},[174,483,484],{"class":192},"      .",[174,486,487],{"class":322},"split",[174,489,326],{"class":199},[174,491,492],{"class":192},"''",[174,494,495],{"class":199},")\n",[174,497,498,500,503,505,507,510,513,516,519,522,524,527,529,531],{"class":176,"line":280},[174,499,484],{"class":192},[174,501,502],{"class":322},"filter",[174,504,326],{"class":199},[174,506,326],{"class":192},[174,508,509],{"class":329},"c",[174,511,512],{"class":192},")",[174,514,515],{"class":184}," =>",[174,517,518],{"class":192}," !",[174,520,521],{"class":350},"SIMILAR",[174,523,370],{"class":192},[174,525,526],{"class":322},"includes",[174,528,326],{"class":199},[174,530,509],{"class":350},[174,532,533],{"class":199},"))\n",[174,535,536,538,541,543,545],{"class":176,"line":290},[174,537,484],{"class":192},[174,539,540],{"class":322},"join",[174,542,326],{"class":199},[174,544,492],{"class":192},[174,546,495],{"class":199},[174,548,549],{"class":176,"line":3},[174,550,551],{"class":192},"  }\n",[174,553,555,558],{"class":176,"line":554},13,[174,556,557],{"class":180},"  return",[174,559,479],{"class":350},[174,561,563],{"class":176,"line":562},14,[174,564,302],{"class":192},[17,566,567],{},"Cette approche offre un contrôle fin sur le jeu de caractères, ce qui impacte directement le\ncalcul de l'entropie.",[151,569,571],{"id":570},"considérations-de-sécurité","Considérations de sécurité",[17,573,574],{},"J'ai implémenté plusieurs mécanismes pour garantir des mots de passe robustes :",[101,576,577,694,954],{},[39,578,579,583,584,587,588,591,592],{},[580,581,582],"strong",{},"Aléa cryptographiquement sûr"," : au lieu de ",[24,585,586],{},"Math.random()",", j'ai utilisé l'API Web Crypto\n",[24,589,590],{},"crypto.getRandomValues()"," pour un vrai aléa :\n",[65,593,595],{"className":168,"code":594,"language":170,"meta":73,"style":73},"    function getRandomChar(pool: string): string {\n         const randomIndex = crypto.getRandomValues(new Uint32Array(1))[0] % pool.length\n         return pool.charAt(randomIndex)\n     }\n",[24,596,597,619,670,689],{"__ignoreMap":73},[174,598,599,602,605,607,609,611,613,615,617],{"class":176,"line":177},[174,600,601],{"class":184},"    function",[174,603,604],{"class":322}," getRandomChar",[174,606,326],{"class":192},[174,608,379],{"class":329},[174,610,203],{"class":192},[174,612,340],{"class":188},[174,614,337],{"class":192},[174,616,340],{"class":188},[174,618,193],{"class":192},[174,620,621,624,627,629,632,634,637,639,642,645,647,651,654,657,660,663,665,667],{"class":176,"line":196},[174,622,623],{"class":184},"         const",[174,625,626],{"class":350}," randomIndex",[174,628,354],{"class":192},[174,630,631],{"class":350}," crypto",[174,633,370],{"class":192},[174,635,636],{"class":322},"getRandomValues",[174,638,326],{"class":199},[174,640,641],{"class":192},"new",[174,643,644],{"class":322}," Uint32Array",[174,646,326],{"class":199},[174,648,650],{"class":649},"sbssI","1",[174,652,653],{"class":199},"))[",[174,655,656],{"class":649},"0",[174,658,659],{"class":199},"] ",[174,661,662],{"class":192},"%",[174,664,351],{"class":350},[174,666,370],{"class":192},[174,668,669],{"class":350},"length\n",[174,671,672,675,677,679,682,684,687],{"class":176,"line":209},[174,673,674],{"class":180},"         return",[174,676,351],{"class":350},[174,678,370],{"class":192},[174,680,681],{"class":322},"charAt",[174,683,326],{"class":199},[174,685,686],{"class":350},"randomIndex",[174,688,495],{"class":199},[174,690,691],{"class":176,"line":220},[174,692,693],{"class":192},"     }\n",[39,695,696,699,700],{},[580,697,698],{},"Détection des motifs séquentiels"," : pour éviter les motifs faibles comme « abc » ou « 123 »,\nj'ai ajouté une fonction qui détecte les séquences :\n",[65,701,703],{"className":168,"code":702,"language":170,"meta":73,"style":73}," function hasSequentialCharacters(password: string): boolean {\n   for (let i = 0; i \u003C password.length - 2; i++) {\n     const a = password.charCodeAt(i)\n     const b = password.charCodeAt(i + 1)\n     const c = password.charCodeAt(i + 2)\n     if ((b === a + 1 && c === b + 1) || (b === a - 1 && c === b - 1)) {\n       return true\n     }\n   }\n   return false\n }\n",[24,704,705,729,780,804,831,856,923,932,936,941,949],{"__ignoreMap":73},[174,706,707,710,713,715,718,720,722,724,727],{"class":176,"line":177},[174,708,709],{"class":184}," function",[174,711,712],{"class":322}," hasSequentialCharacters",[174,714,326],{"class":192},[174,716,717],{"class":329},"password",[174,719,203],{"class":192},[174,721,340],{"class":188},[174,723,337],{"class":192},[174,725,726],{"class":188}," boolean",[174,728,193],{"class":192},[174,730,731,734,736,739,742,744,747,750,752,755,758,760,763,766,769,771,773,776,778],{"class":176,"line":196},[174,732,733],{"class":180},"   for",[174,735,365],{"class":199},[174,737,738],{"class":184},"let",[174,740,741],{"class":350}," i",[174,743,354],{"class":192},[174,745,746],{"class":649}," 0",[174,748,749],{"class":192},";",[174,751,741],{"class":350},[174,753,754],{"class":192}," \u003C",[174,756,757],{"class":350}," password",[174,759,370],{"class":192},[174,761,762],{"class":350},"length",[174,764,765],{"class":192}," -",[174,767,768],{"class":649}," 2",[174,770,749],{"class":192},[174,772,741],{"class":350},[174,774,775],{"class":192},"++",[174,777,376],{"class":199},[174,779,469],{"class":192},[174,781,782,785,788,790,792,794,797,799,802],{"class":176,"line":209},[174,783,784],{"class":184},"     const",[174,786,787],{"class":350}," a",[174,789,354],{"class":192},[174,791,757],{"class":350},[174,793,370],{"class":192},[174,795,796],{"class":322},"charCodeAt",[174,798,326],{"class":199},[174,800,801],{"class":350},"i",[174,803,495],{"class":199},[174,805,806,808,811,813,815,817,819,821,823,826,829],{"class":176,"line":220},[174,807,784],{"class":184},[174,809,810],{"class":350}," b",[174,812,354],{"class":192},[174,814,757],{"class":350},[174,816,370],{"class":192},[174,818,796],{"class":322},[174,820,326],{"class":199},[174,822,801],{"class":350},[174,824,825],{"class":192}," +",[174,827,828],{"class":649}," 1",[174,830,495],{"class":199},[174,832,833,835,838,840,842,844,846,848,850,852,854],{"class":176,"line":230},[174,834,784],{"class":184},[174,836,837],{"class":350}," c",[174,839,354],{"class":192},[174,841,757],{"class":350},[174,843,370],{"class":192},[174,845,796],{"class":322},[174,847,326],{"class":199},[174,849,801],{"class":350},[174,851,825],{"class":192},[174,853,768],{"class":649},[174,855,495],{"class":199},[174,857,858,861,864,867,870,872,874,876,879,881,883,885,887,889,891,894,896,898,900,902,904,906,908,910,912,914,916,918,921],{"class":176,"line":240},[174,859,860],{"class":180},"     if",[174,862,863],{"class":199}," ((",[174,865,866],{"class":350},"b",[174,868,869],{"class":192}," ===",[174,871,787],{"class":350},[174,873,825],{"class":192},[174,875,828],{"class":649},[174,877,878],{"class":192}," &&",[174,880,837],{"class":350},[174,882,869],{"class":192},[174,884,810],{"class":350},[174,886,825],{"class":192},[174,888,828],{"class":649},[174,890,376],{"class":199},[174,892,893],{"class":192},"||",[174,895,365],{"class":199},[174,897,866],{"class":350},[174,899,869],{"class":192},[174,901,787],{"class":350},[174,903,765],{"class":192},[174,905,828],{"class":649},[174,907,878],{"class":192},[174,909,837],{"class":350},[174,911,869],{"class":192},[174,913,810],{"class":350},[174,915,765],{"class":192},[174,917,828],{"class":649},[174,919,920],{"class":199},")) ",[174,922,469],{"class":192},[174,924,925,928],{"class":176,"line":250},[174,926,927],{"class":180},"       return",[174,929,931],{"class":930},"sfNiH"," true\n",[174,933,934],{"class":176,"line":260},[174,935,693],{"class":192},[174,937,938],{"class":176,"line":270},[174,939,940],{"class":192},"   }\n",[174,942,943,946],{"class":176,"line":280},[174,944,945],{"class":180},"   return",[174,947,948],{"class":930}," false\n",[174,950,951],{"class":176,"line":290},[174,952,953],{"class":192}," }\n",[39,955,956,959],{},[580,957,958],{},"Validation des entrées"," : le code interdit les combinaisons impossibles, comme exiger\nl'unicité des caractères pour un mot de passe plus long que le pool disponible.",[151,961,963],{"id":962},"logique-de-génération","Logique de génération",[17,965,966],{},"L'algorithme principal utilise un mécanisme de réessai pour garantir le respect de toutes les\ncontraintes :",[101,968,969,972,975,978],{},[39,970,971],{},"générer le premier caractère (éventuellement une lettre)",[39,973,974],{},"construire le reste du mot de passe caractère par caractère",[39,976,977],{},"valider chaque contrainte (unicité, motifs séquentiels, etc.)",[39,979,980],{},"si une contrainte échoue, recommencer toute la génération",[17,982,983],{},"Cette approche assure que chaque mot de passe généré satisfait toutes les exigences de sécurité.",[151,985,987],{"id":986},"sortie-finale","Sortie finale",[17,989,990,991,993],{},"La fonction ",[24,992,160],{}," orchestre l'ensemble, en imposant des limites raisonnables\n(8 à 64 caractères) et en générant la quantité de mots de passe demandée :",[65,995,997],{"className":168,"code":996,"language":170,"meta":73,"style":73},"export function generatePasswords(options: PasswordOptions): string[] {\n  const safeLength = Math.max(8, Math.min(options.length, 64))\n  const passwords: string[] = []\n\n  for (let i = 0; i \u003C options.quantity; i++) {\n    passwords.push(generatePassword({ ...options, length: safeLength }))\n  }\n  return passwords\n}\n",[24,998,999,1025,1073,1092,1098,1137,1175,1179,1186],{"__ignoreMap":73},[174,1000,1001,1003,1005,1008,1010,1012,1014,1016,1018,1020,1023],{"class":176,"line":177},[174,1002,181],{"class":180},[174,1004,709],{"class":184},[174,1006,1007],{"class":322}," generatePasswords",[174,1009,326],{"class":192},[174,1011,330],{"class":329},[174,1013,203],{"class":192},[174,1015,189],{"class":188},[174,1017,337],{"class":192},[174,1019,340],{"class":188},[174,1021,1022],{"class":350},"[] ",[174,1024,469],{"class":192},[174,1026,1027,1030,1033,1035,1038,1040,1043,1045,1048,1051,1053,1055,1058,1060,1062,1064,1066,1068,1071],{"class":176,"line":196},[174,1028,1029],{"class":184},"  const",[174,1031,1032],{"class":350}," safeLength",[174,1034,354],{"class":192},[174,1036,1037],{"class":350}," Math",[174,1039,370],{"class":192},[174,1041,1042],{"class":322},"max",[174,1044,326],{"class":199},[174,1046,1047],{"class":649},"8",[174,1049,1050],{"class":192},",",[174,1052,1037],{"class":350},[174,1054,370],{"class":192},[174,1056,1057],{"class":322},"min",[174,1059,326],{"class":199},[174,1061,330],{"class":350},[174,1063,370],{"class":192},[174,1065,762],{"class":350},[174,1067,1050],{"class":192},[174,1069,1070],{"class":649}," 64",[174,1072,533],{"class":199},[174,1074,1075,1077,1080,1082,1084,1086,1089],{"class":176,"line":209},[174,1076,1029],{"class":184},[174,1078,1079],{"class":350}," passwords",[174,1081,203],{"class":192},[174,1083,340],{"class":188},[174,1085,1022],{"class":199},[174,1087,1088],{"class":192},"=",[174,1090,1091],{"class":199}," []\n",[174,1093,1094],{"class":176,"line":220},[174,1095,1097],{"emptyLinePlaceholder":1096},true,"\n",[174,1099,1100,1103,1105,1107,1109,1111,1113,1115,1117,1119,1122,1124,1127,1129,1131,1133,1135],{"class":176,"line":230},[174,1101,1102],{"class":180},"  for",[174,1104,365],{"class":199},[174,1106,738],{"class":184},[174,1108,741],{"class":350},[174,1110,354],{"class":192},[174,1112,746],{"class":649},[174,1114,749],{"class":192},[174,1116,741],{"class":350},[174,1118,754],{"class":192},[174,1120,1121],{"class":350}," options",[174,1123,370],{"class":192},[174,1125,1126],{"class":350},"quantity",[174,1128,749],{"class":192},[174,1130,741],{"class":350},[174,1132,775],{"class":192},[174,1134,376],{"class":199},[174,1136,469],{"class":192},[174,1138,1139,1142,1144,1147,1149,1151,1153,1156,1159,1161,1163,1166,1168,1170,1173],{"class":176,"line":240},[174,1140,1141],{"class":350},"    passwords",[174,1143,370],{"class":192},[174,1145,1146],{"class":322},"push",[174,1148,326],{"class":199},[174,1150,160],{"class":322},[174,1152,326],{"class":199},[174,1154,1155],{"class":192},"{",[174,1157,1158],{"class":192}," ...",[174,1160,330],{"class":350},[174,1162,1050],{"class":192},[174,1164,1165],{"class":199}," length",[174,1167,203],{"class":192},[174,1169,1032],{"class":350},[174,1171,1172],{"class":192}," }",[174,1174,533],{"class":199},[174,1176,1177],{"class":176,"line":250},[174,1178,551],{"class":192},[174,1180,1181,1183],{"class":176,"line":260},[174,1182,557],{"class":180},[174,1184,1185],{"class":350}," passwords\n",[174,1187,1188],{"class":176,"line":270},[174,1189,302],{"class":192},[17,1191,1192],{},"Cette implémentation équilibre sécurité, ergonomie et performance, ce qui en fait un\ngénérateur de mots de passe robuste pour des cas réels.",[12,1194,1196],{"id":1195},"pourquoi-cest-important","Pourquoi c'est important",[17,1198,1199],{},"J'ai beaucoup appris de ce défi d'entretien, et j'ai réalisé que la plupart des générateurs de\nmots de passe sont juste des générateurs de caractères aléatoires un peu glorifiés. Ils ne\nprennent pas en compte l'entropie réelle. Une bonne hygiène des mots de passe est cruciale — et\nnous avons tous réutilisé un mot de passe au moins une fois. Des outils comme Obscura donnent à\nl'utilisateur confiance et contrôle sans compromis sur la vie privée.",[17,1201,1202],{},"Quelques conseils pour une meilleure sécurité :",[101,1204,1205,1208,1211,1214,1217],{},[39,1206,1207],{},"utilisez toujours un mot de passe unique pour chaque compte",[39,1209,1210],{},"privilégiez les mots de passe longs (16+ caractères)",[39,1212,1213],{},"évitez les motifs prévisibles ou les informations personnelles (anniversaires, prénoms, etc.)",[39,1215,1216],{},"utilisez un gestionnaire de mots de passe (Obscura est parfait pour générer des mots de passe\nforts à y stocker)",[39,1218,1219],{},"ne partagez jamais, au grand jamais, vos mots de passe",[12,1221,1223],{"id":1222},"mon-expérience-du-process","Mon expérience du process",[17,1225,1226],{},"Malheureusement, mon parcours avec l'entreprise ne s'est pas étendu au-delà de l'entretien\ntechnique. D'après leurs retours, ils cherchent quelqu'un avec une expérience plus poussée en\ningénierie logicielle et des certifications sécurité formelles comme CISSP, CLSSP,\nCOMPTIA Security+, etc.",[17,1228,1229],{},"Mon profil est principalement orienté ingénierie logicielle dans les systèmes distribués, avec un\nfocus sur Kubernetes et les pratiques DevSecOps. J'ai de l'expérience sur les aspects sécurité\ncomme l'analyse SAST\u002FDAST, le scan de vulnérabilités des dépendances, la sécurité des conteneurs,\nles revues de sécurité IaC et la gestion IAM \u002F des secrets — mais ils cherchaient un profil plus\nspécialisé en sécurité.",[17,1231,1232],{},"Malgré le résultat, j'ai vraiment apprécié le process d'entretien et j'ai beaucoup appris du\nchallenge. J'ai décidé d'en tirer quelque chose d'utile, et c'est comme ça qu'Obscura est né.",[12,1234,1236],{"id":1235},"essaie-le-forke-le-partage-le-contribue","Essaie-le, forke-le, partage-le, contribue",[17,1238,1239,1240,1247,1248,1253],{},"Tu peux essayer Obscura sur ",[1241,1242,1246],"a",{"href":1243,"rel":1244},"https:\u002F\u002Fobscura.denisakp.me",[1245],"nofollow","obscura.denisakp.me"," et consulter le\ncode sur ",[1241,1249,1252],{"href":1250,"rel":1251},"https:\u002F\u002Fgithub.com\u002Fdenisakp\u002Fobscura",[1245],"GitHub",". Tes retours, suggestions ou contributions\nsont les bienvenus. Le projet est open source : toute amélioration ou nouvelle fonctionnalité que\ntu voudrais ajouter est appréciée.",[1255,1256,1257],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":73,"searchDepth":196,"depth":196,"links":1259},[1260,1261,1262,1263,1270,1271,1272],{"id":14,"depth":196,"text":15},{"id":30,"depth":196,"text":31},{"id":56,"depth":196,"text":57},{"id":128,"depth":196,"text":129,"children":1264},[1265,1266,1267,1268,1269],{"id":153,"depth":209,"text":154},{"id":305,"depth":209,"text":306},{"id":570,"depth":209,"text":571},{"id":962,"depth":209,"text":963},{"id":986,"depth":209,"text":987},{"id":1195,"depth":196,"text":1196},{"id":1222,"depth":196,"text":1223},{"id":1235,"depth":196,"text":1236},"2025-04-13","D'un défi d'entretien à un outil open source : construire un générateur de mots de passe sécurisé en pensant à l'entropie réelle.","md",null,{},"\u002Fsecurity\u002Fintroducing-obscura.fr",{"title":7,"description":1274},"how-a-job-interview-led-me-to-create-obscura","3.security\u002F1.introducing-obscura.fr",[1283,1284],"Security","Open Source","SDNzr1yNQuCcuy9eHGbGBqiDzM_tZiIhqsLOTG2cKFw",{"id":1287,"title":1288,"body":1289,"date":1886,"description":1887,"extension":1275,"img":1276,"meta":1888,"navigation":1096,"path":1889,"seo":1890,"slug":1891,"stem":1892,"tags":1893,"topics":1899,"__hash__":1902},"content\u002Fdevops\u002F3.backup-and-restore-mongodb-in docker-environment.fr.md","Sauvegarder et restaurer MongoDB dans un environnement Docker",{"type":9,"value":1290,"toc":1866},[1291,1293,1306,1310,1313,1317,1324,1331,1338,1345,1352,1356,1359,1365,1371,1377,1380,1384,1388,1391,1395,1398,1470,1474,1481,1484,1516,1531,1553,1556,1561,1565,1571,1574,1592,1602,1605,1609,1612,1615,1619,1629,1632,1636,1639,1667,1671,1674,1691,1699,1703,1709,1736,1745,1748,1752,1756,1762,1768,1779,1785,1791,1797,1801,1808,1815,1819,1863],[12,1292,15],{"id":14},[17,1294,1295,1296,1301,1302,1305],{},"Dans notre précédent article, on a vu comment faire tourner une base MongoDB dans un conteneur\nDocker, et on a appris à créer un replica set à 3 nœuds avec Docker Compose. Si tu l'as raté,\ntu peux le consulter ",[1241,1297,1300],{"href":1298,"rel":1299},"https:\u002F\u002Fdenisakp.me\u002Fblog\u002Fdevops\u002Fset-up-mongodb-replica-with-docker",[1245],"ici",".\nDans cet article, on couvre un sujet essentiel : ",[580,1303,1304],{},"sauvegarder et restaurer MongoDB dans un\nenvironnement Docker",". Je vais te guider à travers le process de création d'une sauvegarde\ncomplète d'une base MongoDB qui tourne dans un conteneur Docker, puis sa restauration dans un\nnouveau conteneur MongoDB.",[12,1307,1309],{"id":1308},"limportance-des-sauvegardes-dans-un-environnement-conteneurisé","L'importance des sauvegardes dans un environnement conteneurisé",[17,1311,1312],{},"Les conteneurs sont conçus pour être éphémères, c'est-à-dire qu'ils peuvent être créés,\ndétruits et recréés à tout moment. Si ça offre de la flexibilité, ça signifie aussi que toute\ndonnée stockée dans un conteneur risque d'être perdue quand le conteneur est supprimé, ou\nperdue à cause de corruption ou d'autres soucis. Il est donc crucial d'avoir une stratégie de\nsauvegarde et de restauration robuste pour protéger tes données de la perte ou la corruption\net garantir disponibilité et continuité d'activité.",[12,1314,1316],{"id":1315},"pourquoi-les-sauvegardes-sont-importantes","Pourquoi les sauvegardes sont importantes",[17,1318,1319,1320,1323],{},"1- ",[580,1321,1322],{},"Prévention de la perte de données"," : les sauvegardes sont essentielles pour prévenir la\nperte de données due à une suppression accidentelle, corruption, panne matérielle ou autre\névénement inattendu. Avoir une sauvegarde garantit de pouvoir récupérer tes données en cas\nd'événement de ce type.",[17,1325,1326,1327,1330],{},"2- ",[580,1328,1329],{},"Continuité d'activité"," : les sauvegardes sont cruciales pour maintenir la continuité\nd'activité en cas de perte de données. Une stratégie de sauvegarde fiable garantit que tu\npeux rapidement récupérer tes données et reprendre les opérations sans temps d'arrêt\nsignificatif, particulièrement dans les environnements conteneurisés.",[17,1332,1333,1334,1337],{},"3- ",[580,1335,1336],{},"Reprise après sinistre"," : les sauvegardes sont une partie essentielle de tout plan de\nreprise après sinistre. Une stratégie de sauvegarde fiable garantit que tu peux récupérer\nrapidement tes données et reprendre les opérations en cas de sinistre.",[17,1339,1340,1341,1344],{},"4- ",[580,1342,1343],{},"Conformité et exigences légales"," : de nombreuses industries ont des exigences strictes\nde conformité et légales concernant les données.",[17,1346,1347,1348,1351],{},"5- ",[580,1349,1350],{},"Tranquillité d'esprit"," : avoir une stratégie de sauvegarde fiable en place te donne la\ntranquillité d'esprit en sachant que tes données sont protégées et peuvent être récupérées\nen cas d'événement.",[12,1353,1355],{"id":1354},"stratégies-de-sauvegarde-mongodb","Stratégies de sauvegarde MongoDB",[17,1357,1358],{},"MongoDB propose plusieurs options pour sauvegarder tes données : sauvegardes incrémentales,\ncomplètes et différentielles.",[17,1360,1319,1361,1364],{},[580,1362,1363],{},"Sauvegardes incrémentales"," : capturent uniquement les changements faits depuis la\ndernière sauvegarde, ce qui réduit la quantité de données à sauvegarder et accélère le\nprocess. En revanche, elles peuvent être complexes à gérer et restaurer.",[17,1366,1326,1367,1370],{},[580,1368,1369],{},"Sauvegardes complètes"," : capturent toute la base, données et index inclus. Bien que les\nsauvegardes complètes soient plus longues et plus gourmandes en ressources que les\nincrémentales, elles fournissent un snapshot complet de la base qui peut être utilisé pour\nla restaurer à un point précis dans le temps.",[17,1372,1333,1373,1376],{},[580,1374,1375],{},"Sauvegardes différentielles"," : capturent uniquement les changements faits depuis la\ndernière sauvegarde complète, ce qui réduit la quantité de données à sauvegarder par\nrapport à une complète. Ça peut être un bon compromis entre complète et incrémentale, mais\nça peut aussi être complexe.",[17,1378,1379],{},"Dans cet article, on se concentre sur la stratégie de sauvegarde complète puisque c'est la\nméthode la plus simple et fiable pour sauvegarder ta base.",[12,1381,1383],{"id":1382},"réaliser-une-sauvegarde-complète-de-mongodb","Réaliser une sauvegarde complète de MongoDB",[151,1385,1387],{"id":1386},"prérequis","Prérequis",[17,1389,1390],{},"1- Docker et Docker Compose installés sur ton système.\n2- Connaissance basique des concepts Docker comme les volumes.\n3- Temps pour compléter le tutoriel : 6 à 10 minutes.",[151,1392,1394],{"id":1393},"étape-1-démarrer-un-conteneur-mongodb","Étape 1 : démarrer un conteneur MongoDB",[17,1396,1397],{},"D'abord, on tire l'image MongoDB depuis Docker Hub et on démarre un conteneur MongoDB.",[65,1399,1403],{"className":1400,"code":1401,"language":1402,"meta":73,"style":73},"language-shell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","docker pull mongo:7.0-jammy # Pull the MongoDB image\ndocker volume create mongo-data # Create a volume to persist the MongoDB data\ndocker run -d --name mongodb -v mongo-data:\u002Fdata\u002Fdb -p 27017:27017 mongo:7.0-jammy # Start a MongoDB container\n","shell",[24,1404,1405,1421,1437],{"__ignoreMap":73},[174,1406,1407,1410,1414,1417],{"class":176,"line":177},[174,1408,1409],{"class":188},"docker",[174,1411,1413],{"class":1412},"sfazB"," pull",[174,1415,1416],{"class":1412}," mongo:7.0-jammy",[174,1418,1420],{"class":1419},"sHwdD"," # Pull the MongoDB image\n",[174,1422,1423,1425,1428,1431,1434],{"class":176,"line":196},[174,1424,1409],{"class":188},[174,1426,1427],{"class":1412}," volume",[174,1429,1430],{"class":1412}," create",[174,1432,1433],{"class":1412}," mongo-data",[174,1435,1436],{"class":1419}," # Create a volume to persist the MongoDB data\n",[174,1438,1439,1441,1444,1447,1450,1453,1456,1459,1462,1465,1467],{"class":176,"line":209},[174,1440,1409],{"class":188},[174,1442,1443],{"class":1412}," run",[174,1445,1446],{"class":1412}," -d",[174,1448,1449],{"class":1412}," --name",[174,1451,1452],{"class":1412}," mongodb",[174,1454,1455],{"class":1412}," -v",[174,1457,1458],{"class":1412}," mongo-data:\u002Fdata\u002Fdb",[174,1460,1461],{"class":1412}," -p",[174,1463,1464],{"class":1412}," 27017:27017",[174,1466,1416],{"class":1412},[174,1468,1469],{"class":1419}," # Start a MongoDB container\n",[151,1471,1473],{"id":1472},"étape-2-exécuter-la-commande-mongodump","Étape 2 : exécuter la commande mongodump",[17,1475,1476,1477,1480],{},"Pour créer une sauvegarde complète de ta base MongoDB, tu vas utiliser la commande\n",[24,1478,1479],{},"mongodump",", un utilitaire fourni par MongoDB. Cette commande crée un export binaire de ta\nbase qui pourra servir à restaurer les données plus tard.",[17,1482,1483],{},"Lance la commande suivante depuis ta machine hôte pour créer une sauvegarde complète de ta\nbase MongoDB :",[65,1485,1487],{"className":1400,"code":1486,"language":1402,"meta":73,"style":73},"docker exec -it mongodb mongodump --uri mongodb:\u002F\u002Flocalhost:27017\u002Ftest --gzip --archive=test.archive\n",[24,1488,1489],{"__ignoreMap":73},[174,1490,1491,1493,1496,1499,1501,1504,1507,1510,1513],{"class":176,"line":177},[174,1492,1409],{"class":188},[174,1494,1495],{"class":1412}," exec",[174,1497,1498],{"class":1412}," -it",[174,1500,1452],{"class":1412},[174,1502,1503],{"class":1412}," mongodump",[174,1505,1506],{"class":1412}," --uri",[174,1508,1509],{"class":1412}," mongodb:\u002F\u002Flocalhost:27017\u002Ftest",[174,1511,1512],{"class":1412}," --gzip",[174,1514,1515],{"class":1412}," --archive=test.archive\n",[17,1517,1518,1519,1522,1523,1526,1527,1530],{},"Dans mon exemple, mon conteneur s'appelle ",[24,1520,1521],{},"mongo",", et ma base s'appelle ",[24,1524,1525],{},"test"," — tu dois la\nremplacer par le nom de ta base. Note aussi le flag ",[24,1528,1529],{},"--gzip",", qui compresse le fichier de\nsauvegarde pour économiser de l'espace disque. Ma base de test pèse environ 18 Mo, et le\nfichier de sauvegarde environ 627 Ko. Réduction significative.",[17,1532,1533,1534,1537,1538,1541,1542,1545,1546,1549,1550,370],{},"Autre chose importante : le flag ",[24,1535,1536],{},"--uri",", qui spécifie la chaîne de connexion à la base\nMongoDB. Ici, on se connecte à la base MongoDB qui tourne sur ",[24,1539,1540],{},"localhost"," au port ",[24,1543,1544],{},"27017",". Si\ntu as suivi l'article précédent, tu as peut-être un replica set qui tourne sur\n",[24,1547,1548],{},"localhost:27017,localhost:27018,localhost:27019",". Dans ce cas, remplace la chaîne de\nconnexion par la version appropriée :\n",[24,1551,1552],{},"mongodb:\u002F\u002Fusername:passwor@localhost:27017,username:passwor@locahost:27018,username:passwor@localhost:27019\u002F?replicaSet=rs0",[17,1554,1555],{},"Si tout se passe bien, tu devrais voir un message indiquant que la sauvegarde a réussi.\nQuelque chose comme :",[1557,1558],"post-image",{"alt":1559,"source":1560},"Sauvegarde MongoDB réussie","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fhow-to\u002Fsuccess-backup-mongo",[151,1562,1564],{"id":1563},"étape-3-copier-le-fichier-de-sauvegarde-sur-ta-machine-hôte","Étape 3 : copier le fichier de sauvegarde sur ta machine hôte",[17,1566,1567,1568,1570],{},"La commande ",[24,1569,1479],{}," crée un fichier de sauvegarde à l'intérieur du conteneur MongoDB. On\ndoit le copier sur notre machine hôte parce que le conteneur est éphémère et peut être détruit\nà tout moment.",[17,1572,1573],{},"Lance la commande suivante pour copier le fichier de sauvegarde sur ta machine hôte :",[65,1575,1577],{"className":1400,"code":1576,"language":1402,"meta":73,"style":73},"docker cp mongodb:\u002Ftest.archive \u002Fpath\u002Fto\u002Fbackup\u002Fyourdatabase.archive\n",[24,1578,1579],{"__ignoreMap":73},[174,1580,1581,1583,1586,1589],{"class":176,"line":177},[174,1582,1409],{"class":188},[174,1584,1585],{"class":1412}," cp",[174,1587,1588],{"class":1412}," mongodb:\u002Ftest.archive",[174,1590,1591],{"class":1412}," \u002Fpath\u002Fto\u002Fbackup\u002Fyourdatabase.archive\n",[1593,1594,1595],"blockquote",{},[17,1596,1597,1598,1601],{},"Remplace ",[24,1599,1600],{},"\u002Fpath\u002Fto\u002Fbackup\u002Fyourdatabase.archive"," par le chemin où tu veux stocker le fichier\nde sauvegarde sur ta machine hôte.",[17,1603,1604],{},"Si tout se passe bien, tu devrais voir un message indiquant que le fichier a été copié avec\nsuccès. Quelque chose comme :",[1557,1606],{"alt":1607,"source":1608},"Archive copiée avec succès","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fhow-to\u002Fsuccess-copy-archive",[17,1610,1611],{},"Bravo ! Tu as créé avec succès une sauvegarde complète de ta base MongoDB. Tu peux maintenant\nstocker ce fichier dans un endroit sûr et l'utiliser pour restaurer tes données si besoin.\nQuand je dis « endroit sûr », je veux dire un endroit différent de ta machine locale ou de\nton serveur, parce que, si ta machine ou ton serveur tombe, tu perds à la fois la base et la\nsauvegarde.",[17,1613,1614],{},"Une autre option est d'utiliser un service de stockage cloud comme AWS S3, Google Cloud\nStorage, Azure Blob Storage ou MinIO pour stocker tes sauvegardes. Comme ça, tes sauvegardes\nsont en sécurité même si ta machine ou ton serveur tombe. Tu peux aussi prendre soin de la\nsécurité de tes sauvegardes en les chiffrant avant de les stocker dans le cloud. Ainsi, tu\ngarantis l'intégrité et la confidentialité de la base.",[12,1616,1618],{"id":1617},"réaliser-une-restauration-mongodb","Réaliser une restauration MongoDB",[17,1620,1621,1622,1625,1626,1628],{},"Aussi importante que la création de sauvegardes, la capacité à les restaurer quand nécessaire.\nLa commande ",[24,1623,1624],{},"mongorestore"," est utilisée pour restaurer une sauvegarde MongoDB créée avec\n",[24,1627,1479],{},". Dans cette section, on parcourt le process de restauration d'une sauvegarde\nMongoDB dans un environnement conteneurisé.",[17,1630,1631],{},"Pour ce tutoriel, on suppose que notre conteneur MongoDB a été supprimé sans le vouloir 😏,\net que le volume attaché est parti avec. On utilise le fichier de sauvegarde qu'on a créé\nplus tôt pour restaurer les données.",[151,1633,1635],{"id":1634},"étape-1-démarrer-un-nouveau-conteneur-mongodb","Étape 1 : démarrer un nouveau conteneur MongoDB",[17,1637,1638],{},"D'abord, démarre un nouveau conteneur MongoDB avec la même image et le même volume utilisés\npour créer la sauvegarde.",[65,1640,1642],{"className":1400,"code":1641,"language":1402,"meta":73,"style":73},"docker run -d --name mongodb -v mongo-data:\u002Fdata\u002Fdb -p 27017:27017 mongo:7.0-jammy\n",[24,1643,1644],{"__ignoreMap":73},[174,1645,1646,1648,1650,1652,1654,1656,1658,1660,1662,1664],{"class":176,"line":177},[174,1647,1409],{"class":188},[174,1649,1443],{"class":1412},[174,1651,1446],{"class":1412},[174,1653,1449],{"class":1412},[174,1655,1452],{"class":1412},[174,1657,1455],{"class":1412},[174,1659,1458],{"class":1412},[174,1661,1461],{"class":1412},[174,1663,1464],{"class":1412},[174,1665,1666],{"class":1412}," mongo:7.0-jammy\n",[151,1668,1670],{"id":1669},"étape-2-copier-le-fichier-de-sauvegarde-dans-le-nouveau-conteneur","Étape 2 : copier le fichier de sauvegarde dans le nouveau conteneur",[17,1672,1673],{},"Ensuite, copie le fichier de sauvegarde depuis ta machine hôte ou récupère-le depuis le\nstockage cloud vers le nouveau conteneur MongoDB. Pour cet exemple, on utilise le fichier\nqu'on a créé plus tôt.",[65,1675,1677],{"className":1400,"code":1676,"language":1402,"meta":73,"style":73},"docker cp \u002Fpath\u002Fto\u002Fbackup\u002Ftest.archive mongodb:\u002Ftest.archive\n",[24,1678,1679],{"__ignoreMap":73},[174,1680,1681,1683,1685,1688],{"class":176,"line":177},[174,1682,1409],{"class":188},[174,1684,1585],{"class":1412},[174,1686,1687],{"class":1412}," \u002Fpath\u002Fto\u002Fbackup\u002Ftest.archive",[174,1689,1690],{"class":1412}," mongodb:\u002Ftest.archive\n",[1593,1692,1693],{},[17,1694,1597,1695,1698],{},[24,1696,1697],{},"\u002Fpath\u002Fto\u002Fbackup\u002Ftest.archive"," par le chemin vers le fichier de sauvegarde sur ta\nmachine hôte.",[151,1700,1702],{"id":1701},"étape-3-exécuter-la-commande-mongorestore","Étape 3 : exécuter la commande mongorestore",[17,1704,1705,1706,1708],{},"Enfin, utilise la commande ",[24,1707,1624],{}," pour restaurer le fichier de sauvegarde dans le\nnouveau conteneur MongoDB.",[65,1710,1712],{"className":1400,"code":1711,"language":1402,"meta":73,"style":73},"docker exec -it mongodb mongorestore --uri mongodb:\u002F\u002Flocalhost:27017 --gzip --archive=test.archive\n",[24,1713,1714],{"__ignoreMap":73},[174,1715,1716,1718,1720,1722,1724,1727,1729,1732,1734],{"class":176,"line":177},[174,1717,1409],{"class":188},[174,1719,1495],{"class":1412},[174,1721,1498],{"class":1412},[174,1723,1452],{"class":1412},[174,1725,1726],{"class":1412}," mongorestore",[174,1728,1506],{"class":1412},[174,1730,1731],{"class":1412}," mongodb:\u002F\u002Flocalhost:27017",[174,1733,1512],{"class":1412},[174,1735,1515],{"class":1412},[17,1737,1738,1739,1741,1742,1744],{},"Cette commande restaure les données du fichier de sauvegarde vers la base MongoDB qui tourne\ndans le nouveau conteneur. Note qu'on n'a pas besoin de spécifier le nom de la base parce\nqu'il est inclus dans le fichier de sauvegarde. Le flag ",[24,1740,1529],{}," sert à indiquer à\n",[24,1743,1624],{}," que le fichier de sauvegarde est compressé.",[17,1746,1747],{},"Si tout se passe bien, tu devrais voir un message indiquant que la restauration a réussi.\nQuelque chose comme :",[1557,1749],{"alt":1750,"source":1751},"Sauvegarde restaurée avec succès","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fhow-to\u002Fsuccess-restore",[12,1753,1755],{"id":1754},"bonnes-pratiques-pour-les-sauvegardes-mongodb-en-environnement-conteneurisé","Bonnes pratiques pour les sauvegardes MongoDB en environnement conteneurisé",[17,1757,1319,1758,1761],{},[580,1759,1760],{},"Utiliser les volumes"," : stocke tes données MongoDB sur un volume pour les persister même\nsi le conteneur est détruit. Ça garantit que tes données sont en sécurité et peuvent être\nsauvegardées et restaurées facilement.",[17,1763,1326,1764,1767],{},[580,1765,1766],{},"Sauvegardes régulières"," : planifie des sauvegardes régulières de ta base MongoDB pour\ngarantir que tes données sont toujours à jour et peuvent être rapidement restaurées en cas\nd'événement. Selon tes données, tu peux avoir besoin de sauvegardes :",[36,1769,1770,1773,1776],{},[39,1771,1772],{},"Quotidiennes : idéal pour les bases avec des changements fréquents.",[39,1774,1775],{},"Hebdomadaires : adapté pour les bases modérément actives, avec des changements moins\nfréquents.",[39,1777,1778],{},"Mensuelles : pour les bases avec des changements peu fréquents.",[17,1780,1333,1781,1784],{},[580,1782,1783],{},"Sauvegardes hors site"," : stocke tes sauvegardes dans un emplacement hors site, comme un\nservice de stockage cloud, pour garantir que tes données sont en sécurité même si ta machine\nou ton serveur tombe. Souviens-toi de traiter les sauvegardes de ta base comme des données\nsensibles : tu devrais les chiffrer avant de les stocker dans le cloud, et en restreindre\nl'accès au personnel autorisé seulement.",[17,1786,1333,1787,1790],{},[580,1788,1789],{},"Automatiser les sauvegardes"," : mets en place des jobs de sauvegarde automatisés qui\ntournent à intervalles réguliers pour garantir que tes données sont sauvegardées de manière\nrégulière et cohérente.",[17,1792,1340,1793,1796],{},[580,1794,1795],{},"Tester les restaurations"," : teste régulièrement ton process de sauvegarde et de\nrestauration pour garantir que tes sauvegardes sont valides et peuvent être restaurées avec\nsuccès. Ça t'aidera à identifier les soucis de ta stratégie de sauvegarde et à faire les\najustements nécessaires. Après une sauvegarde, tu devrais tester le process de restauration\ndans un environnement sandbox pour garantir que la sauvegarde est valide et peut être\nrestaurée avec succès. Une chose importante : tu devrais documenter le process de\nrestauration, pour garantir que tout personnel autorisé peut restaurer la base en cas\nd'événement.",[12,1798,1800],{"id":1799},"conclusion","Conclusion",[17,1802,1803,1804,1807],{},"Dans cet article, on a couvert l'importance des sauvegardes dans un environnement\nconteneurisé et on t'a guidé à travers le process de création d'une sauvegarde complète d'une\nbase MongoDB qui tourne dans un conteneur Docker. Je t'ai également montré comment restaurer\nla sauvegarde dans un nouveau conteneur MongoDB. En suivant ces étapes et bonnes pratiques,\ntu peux garantir que tes données MongoDB sont sûres, sécurisées et toujours disponibles, même\nen cas de perte ou corruption. ",[580,1805,1806],{},"Souviens-toi : avoir une sauvegarde est essentiel, mais\npouvoir la restaurer est encore plus important",". Donc, assure-toi de tester tes process de\nsauvegarde et de restauration régulièrement pour garantir que tes données sont protégées et\npeuvent être récupérées quand nécessaire.",[17,1809,1810,1811,1814],{},"Dans un futur article, on couvrira un sujet intéressant : ",[580,1812,1813],{},"automatiser les sauvegardes\nMongoDB dans un environnement conteneurisé",". Je suis sûr que tu vas adorer, parce que ça va\nt'économiser du temps et des efforts. Reste à l'écoute !",[12,1816,1818],{"id":1817},"références","Références",[36,1820,1821,1828,1835,1842,1849,1856],{},[39,1822,1823],{},[1241,1824,1827],{"href":1825,"rel":1826},"https:\u002F\u002Fdocs.mongodb.com\u002Fmanual\u002Fcore\u002Fbackups\u002F",[1245],"Sauvegarde MongoDB",[39,1829,1830],{},[1241,1831,1834],{"href":1832,"rel":1833},"https:\u002F\u002Fdocs.mongodb.com\u002Fdatabase-tools\u002Fmongorestore\u002F",[1245],"Restauration MongoDB",[39,1836,1837],{},[1241,1838,1841],{"href":1839,"rel":1840},"https:\u002F\u002Fdocs.docker.com\u002Fstorage\u002Fvolumes\u002F",[1245],"Volumes Docker",[39,1843,1844],{},[1241,1845,1848],{"href":1846,"rel":1847},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Freference\u002Fcommandline\u002Fcp\u002F",[1245],"Commande Docker Copy",[39,1850,1851],{},[1241,1852,1855],{"href":1853,"rel":1854},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Freference\u002Fcommandline\u002Fexec\u002F",[1245],"Commande Docker Exec",[39,1857,1858],{},[1241,1859,1862],{"href":1860,"rel":1861},"https:\u002F\u002Fhub.docker.com\u002F_\u002Fmongo",[1245],"Image Docker MongoDB",[1255,1864,1865],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":73,"searchDepth":196,"depth":196,"links":1867},[1868,1869,1870,1871,1872,1878,1883,1884,1885],{"id":14,"depth":196,"text":15},{"id":1308,"depth":196,"text":1309},{"id":1315,"depth":196,"text":1316},{"id":1354,"depth":196,"text":1355},{"id":1382,"depth":196,"text":1383,"children":1873},[1874,1875,1876,1877],{"id":1386,"depth":209,"text":1387},{"id":1393,"depth":209,"text":1394},{"id":1472,"depth":209,"text":1473},{"id":1563,"depth":209,"text":1564},{"id":1617,"depth":196,"text":1618,"children":1879},[1880,1881,1882],{"id":1634,"depth":209,"text":1635},{"id":1669,"depth":209,"text":1670},{"id":1701,"depth":209,"text":1702},{"id":1754,"depth":196,"text":1755},{"id":1799,"depth":196,"text":1800},{"id":1817,"depth":196,"text":1818},"2024-07-18","Comment créer une sauvegarde complète d'une base MongoDB qui tourne dans un conteneur Docker et restaurer la sauvegarde dans un nouveau conteneur MongoDB.",{},"\u002Fdevops\u002Fbackup-and-restore-mongodb-in-docker-environment.fr",{"title":1288,"description":1887},"backup-restore-mongodb-docker-environment","devops\u002F3.backup-and-restore-mongodb-in docker-environment.fr",[1894,1895,1896,1897,1898],"MongoDB","Docker","Docker-compose","Backup","Restore",[1900,1409,1901],"devops","mongodb","y2upkEz4qakxdgMK0BMXEZ3QEYE5SD0FZKpDQt6bzUA",{"id":1904,"title":1905,"body":1906,"date":4890,"description":4891,"extension":1275,"img":4892,"meta":4893,"navigation":1096,"path":4894,"seo":4895,"slug":4896,"stem":4897,"tags":4898,"topics":4900,"__hash__":4901},"content\u002Fdevops\u002F2.set-up-mongodb-replica-with-docker.fr.md","Mettre en place un cluster MongoDB Replica Set à 3 nœuds avec Docker Compose",{"type":9,"value":1907,"toc":4873},[1908,1910,1913,1917,1925,1936,1939,1943,1949,1955,1961,1967,1975,1978,1980,1988,1996,2000,2007,2041,2044,2894,2900,2998,3002,3005,3008,3046,3066,3071,3075,3080,3091,3568,3572,3602,3611,3615,3927,3931,3934,3950,3959,3963,3969,4001,4015,4018,4031,4042,4557,4562,4566,4576,4736,4749,4759,4765,4785,4788,4793,4796,4798,4804,4811,4814,4816,4870],[12,1909,15],{"id":14},[17,1911,1912],{},"Dans cet article, je te guide à travers la mise en place d'un cluster MongoDB replica set à\n3 nœuds avec Docker Compose. Ce guide suppose que tu es familier avec les concepts Docker de\nbase — Docker Compose, volumes, réseaux, healthchecks — ainsi qu'avec les concepts MongoDB\ncomme les replica sets. Si tu es prêt, on plonge dedans !",[12,1914,1916],{"id":1915},"cest-quoi-un-replica-set-mongodb","C'est quoi un replica set MongoDB ?",[1557,1918],{"alt":1919,"className":1920,"source":1924},"Restauration de backup réussie",[1921,1922,1923],"h-auto","max-w-xl","mx-auto","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fhow-to\u002Fnye35wpkfcy8fivg5qlv",[17,1926,1927,1928,1931,1932,1935],{},"Un replica set MongoDB est un groupe d'instances MongoDB qui hébergent le même jeu de données.\nDans un replica set, un nœud est le nœud ",[580,1929,1930],{},"primary"," qui reçoit toutes les opérations\nd'écriture. Tous les autres nœuds, appelés ",[580,1933,1934],{},"secondary",", appliquent les opérations du primary\npour avoir le même jeu de données.",[17,1937,1938],{},"Les replica sets MongoDB offrent redondance et haute disponibilité, en gardant la base\nopérationnelle même si un ou plusieurs nœuds tombent. Ils répliquent les données sur plusieurs\nnœuds pour garantir intégrité et disponibilité.",[151,1940,1942],{"id":1941},"pourquoi-utiliser-un-replica-set-mongodb","Pourquoi utiliser un replica set MongoDB ?",[17,1944,1319,1945,1948],{},[580,1946,1947],{},"Haute disponibilité"," : si le nœud primary tombe, un secondary peut être élu comme nouveau\nprimary, ce qui garantit que la base reste disponible.",[17,1950,1326,1951,1954],{},[580,1952,1953],{},"Redondance des données"," : les données sont répliquées sur plusieurs nœuds, garantissant\nleur intégrité et leur disponibilité.",[17,1956,1333,1957,1960],{},[580,1958,1959],{},"Scalabilité en lecture"," : les nœuds secondary peuvent servir les opérations de lecture,\ndistribuant la charge de lecture sur plusieurs nœuds.",[17,1962,1340,1963,1966],{},[580,1964,1965],{},"Failover automatique"," : si le nœud primary tombe, un secondary est automatiquement élu\ncomme nouveau primary.",[17,1968,1969,1970,370],{},"Pour aller plus loin sur la réplication MongoDB, consulte la doc officielle sur les\n",[1241,1971,1974],{"href":1972,"rel":1973},"https:\u002F\u002Fdocs.mongodb.com\u002Fmanual\u002Freplication\u002F",[1245],"replica sets",[12,1976,1905],{"id":1977},"mettre-en-place-un-cluster-mongodb-replica-set-à-3-nœuds-avec-docker-compose",[151,1979,1387],{"id":1386},[17,1981,1982,1983,370],{},"Dans ce guide, on utilise Docker Compose pour mettre en place un cluster MongoDB replica set à\n3 nœuds. Avant de commencer, assure-toi d'avoir Docker et Docker Compose installés sur ta\nmachine. Si ce n'est pas le cas, télécharge-les depuis le\n",[1241,1984,1987],{"href":1985,"rel":1986},"https:\u002F\u002Fdocs.docker.com\u002Fget-docker\u002F",[1245],"site officiel Docker",[17,1989,1990,1991,370],{},"Tu auras peut-être aussi besoin d'une compréhension de base de MongoDB et des commandes\nmongo shell. Si tu débutes avec MongoDB, consulte la doc officielle sur les\n",[1241,1992,1995],{"href":1993,"rel":1994},"https:\u002F\u002Fdocs.mongodb.com\u002Fmanual\u002Fcrud\u002F",[1245],"opérations CRUD MongoDB",[151,1997,1999],{"id":1998},"étape-1-créer-un-fichier-docker-compose","Étape 1 : créer un fichier Docker Compose",[17,2001,2002,2003,2006],{},"Commence par créer un fichier ",[24,2004,2005],{},"docker-compose.yml"," dans un nouveau dossier. Ce fichier\ndéfinira les services pour les conteneurs MongoDB. On va créer :",[36,2008,2009,2023,2029],{},[39,2010,2011,2012,2015,2016,2019,2020],{},"trois services : ",[24,2013,2014],{},"mongo1",", ",[24,2017,2018],{},"mongo2"," et ",[24,2021,2022],{},"mongo3",[39,2024,2025,2026],{},"un réseau appelé ",[24,2027,2028],{},"mongo-cluster",[39,2030,2031,2032,2015,2035,2019,2038],{},"trois volumes : ",[24,2033,2034],{},"mongo1-data",[24,2036,2037],{},"mongo2-data",[24,2039,2040],{},"mongo3-data",[17,2042,2043],{},"C'est parti :",[65,2045,2049],{"className":2046,"code":2047,"filename":2005,"language":2048,"meta":73,"style":73},"language-yml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","networks:\n  mongo-cluster:\n    driver: bridge\nvolumes:\n  mongo1_data:\n  mongo1_config:\n  mongo2_data:\n  mongo2_config:\n  mongo3_data:\n  mongo3_config:\nservices:\n  mongo1:\n    container_name: mongo1\n    image: 'mongo:8.2-noble'\n    hostname: mongo1\n    command: [\"--config\", \"\u002Fetc\u002Fmongod.conf\"]\n    volumes:\n      - mongo1_data:\u002Fdata\u002Fdb\n      - mongo1_config:\u002Fdata\u002Fconfigdb\n      - '$PWD\u002Fscripts\u002Fmongo\u002Frs_keyfile:\u002Fetc\u002Fmongodb\u002Fkeyfile:ro'\n      - '$PWD\u002Fscripts\u002Fmongo\u002Fmongod.conf:\u002Fetc\u002Fmongod.conf:ro'\n    networks:\n      - mongo-cluster\n    environment:\n      MONGO_INITDB_ROOT_USERNAME: '${MONGO_ADMIN_USER:-admin}'\n      MONGO_INITDB_ROOT_PASSWORD: '${MONGO_ADMIN_PASSWD:-veryStringPassword}'\n      MONGO_INITDB_DATABASE: '${DB_NAME:-test}'\n    healthcheck:\n      test: [\"CMD\", \"mongosh\", \"--quiet\", \"--eval\", \"try { rs.status().ok } catch(e) { rs.initiate().ok }\"]\n      timeout: 5m\n      interval: 10s\n      retries: 5\n      start_period: 12s\n  mongo2:\n    container_name: mongo2\n    image: 'mongo:8.2-noble'\n    hostname: mongo2\n    volumes:\n      - mongo2_data:\u002Fdata\u002Fdb\n      - mongo2_config:\u002Fdata\u002Fconfigdb\n      - '$PWD\u002Fscripts\u002Fmongo\u002Frs_keyfile:\u002Fetc\u002Fmongodb\u002Fkeyfile:ro'\n      - '$PWD\u002Fscripts\u002Fmongo\u002Fmongod.conf:\u002Fetc\u002Fmongod.conf:ro'\n    networks:\n      - mongo-cluster\n    environment:\n      MONGO_INITDB_ROOT_USERNAME: '${MONGO_ADMIN_USER:-admin}'\n      MONGO_INITDB_ROOT_PASSWORD: '${MONGO_ADMIN_PASSWD:-veryStringPassword}'\n  mongo3:\n    container_name: mongo3\n    image: 'mongo:8.2-noble'\n    hostname: mongo3\n    volumes:\n      - mongo3_data:\u002Fdata\u002Fdb\n      - mongo3_config:\u002Fdata\u002Fconfigdb\n      - '$PWD\u002Fscripts\u002Fmongo\u002Frs_keyfile:\u002Fetc\u002Fmongodb\u002Fkeyfile:ro'\n      - '$PWD\u002Fscripts\u002Fmongo\u002Fmongod.conf:\u002Fetc\u002Fmongod.conf:ro'\n    networks:\n      - mongo-cluster\n    environment:\n      MONGO_INITDB_ROOT_USERNAME: '${MONGO_ADMIN_USER:-admin}'\n      MONGO_INITDB_ROOT_PASSWORD: '${MONGO_ADMIN_PASSWD:-veryStringPassword}'\n  mongo-init:\n    container_name: mongo-init\n    image: docker.io\u002Fmongo:8.2-noble\n    restart: no\n    entrypoint: [\"\u002Fbin\u002Fbash\", \"\u002Finit.sh\"]\n    networks:\n      - mongo-cluster\n    depends_on:\n      - mongo1\n      - mongo2\n      - mongo3\n    volumes:\n      - $PWD\u002Fscripts\u002Fmongo\u002Finit.js:\u002Finit.js\n      - $PWD\u002Fscripts\u002Fmongo\u002Finit.sh:\u002Finit.sh\n    environment:\n      MONGO_ROOT_USERNAME: '${MONGO_ADMIN_USER:-admin}'\n      MONGO_ROOT_PASSWORD: '${MONGO_ADMIN_PASSWD:-veryStringPassword}'\n      DB_USERNAME: '${DB_USERNAME:-myuser}'\n      DB_PASSWORD: '${DB_PASSWORD:-mypassword}'\n","yml",[24,2050,2051,2059,2066,2076,2083,2090,2097,2104,2111,2118,2125,2132,2139,2149,2165,2175,2207,2215,2224,2232,2244,2256,2264,2272,2280,2295,2310,2325,2333,2388,2399,2410,2421,2432,2440,2450,2463,2472,2479,2487,2495,2506,2517,2524,2531,2538,2551,2564,2572,2582,2595,2604,2611,2619,2627,2638,2649,2656,2663,2670,2683,2696,2704,2714,2724,2735,2763,2770,2777,2785,2792,2799,2806,2813,2821,2829,2836,2850,2864,2879],{"__ignoreMap":73},[174,2052,2053,2056],{"class":176,"line":177},[174,2054,2055],{"class":199},"networks",[174,2057,2058],{"class":192},":\n",[174,2060,2061,2064],{"class":176,"line":196},[174,2062,2063],{"class":199},"  mongo-cluster",[174,2065,2058],{"class":192},[174,2067,2068,2071,2073],{"class":176,"line":209},[174,2069,2070],{"class":199},"    driver",[174,2072,203],{"class":192},[174,2074,2075],{"class":1412}," bridge\n",[174,2077,2078,2081],{"class":176,"line":220},[174,2079,2080],{"class":199},"volumes",[174,2082,2058],{"class":192},[174,2084,2085,2088],{"class":176,"line":230},[174,2086,2087],{"class":199},"  mongo1_data",[174,2089,2058],{"class":192},[174,2091,2092,2095],{"class":176,"line":240},[174,2093,2094],{"class":199},"  mongo1_config",[174,2096,2058],{"class":192},[174,2098,2099,2102],{"class":176,"line":250},[174,2100,2101],{"class":199},"  mongo2_data",[174,2103,2058],{"class":192},[174,2105,2106,2109],{"class":176,"line":260},[174,2107,2108],{"class":199},"  mongo2_config",[174,2110,2058],{"class":192},[174,2112,2113,2116],{"class":176,"line":270},[174,2114,2115],{"class":199},"  mongo3_data",[174,2117,2058],{"class":192},[174,2119,2120,2123],{"class":176,"line":280},[174,2121,2122],{"class":199},"  mongo3_config",[174,2124,2058],{"class":192},[174,2126,2127,2130],{"class":176,"line":290},[174,2128,2129],{"class":199},"services",[174,2131,2058],{"class":192},[174,2133,2134,2137],{"class":176,"line":3},[174,2135,2136],{"class":199},"  mongo1",[174,2138,2058],{"class":192},[174,2140,2141,2144,2146],{"class":176,"line":554},[174,2142,2143],{"class":199},"    container_name",[174,2145,203],{"class":192},[174,2147,2148],{"class":1412}," mongo1\n",[174,2150,2151,2154,2156,2159,2162],{"class":176,"line":562},[174,2152,2153],{"class":199},"    image",[174,2155,203],{"class":192},[174,2157,2158],{"class":192}," '",[174,2160,2161],{"class":1412},"mongo:8.2-noble",[174,2163,2164],{"class":192},"'\n",[174,2166,2168,2171,2173],{"class":176,"line":2167},15,[174,2169,2170],{"class":199},"    hostname",[174,2172,203],{"class":192},[174,2174,2148],{"class":1412},[174,2176,2178,2181,2183,2186,2189,2192,2194,2196,2199,2202,2204],{"class":176,"line":2177},16,[174,2179,2180],{"class":199},"    command",[174,2182,203],{"class":192},[174,2184,2185],{"class":192}," [",[174,2187,2188],{"class":192},"\"",[174,2190,2191],{"class":1412},"--config",[174,2193,2188],{"class":192},[174,2195,1050],{"class":192},[174,2197,2198],{"class":192}," \"",[174,2200,2201],{"class":1412},"\u002Fetc\u002Fmongod.conf",[174,2203,2188],{"class":192},[174,2205,2206],{"class":192},"]\n",[174,2208,2210,2213],{"class":176,"line":2209},17,[174,2211,2212],{"class":199},"    volumes",[174,2214,2058],{"class":192},[174,2216,2218,2221],{"class":176,"line":2217},18,[174,2219,2220],{"class":192},"      -",[174,2222,2223],{"class":1412}," mongo1_data:\u002Fdata\u002Fdb\n",[174,2225,2227,2229],{"class":176,"line":2226},19,[174,2228,2220],{"class":192},[174,2230,2231],{"class":1412}," mongo1_config:\u002Fdata\u002Fconfigdb\n",[174,2233,2235,2237,2239,2242],{"class":176,"line":2234},20,[174,2236,2220],{"class":192},[174,2238,2158],{"class":192},[174,2240,2241],{"class":1412},"$PWD\u002Fscripts\u002Fmongo\u002Frs_keyfile:\u002Fetc\u002Fmongodb\u002Fkeyfile:ro",[174,2243,2164],{"class":192},[174,2245,2247,2249,2251,2254],{"class":176,"line":2246},21,[174,2248,2220],{"class":192},[174,2250,2158],{"class":192},[174,2252,2253],{"class":1412},"$PWD\u002Fscripts\u002Fmongo\u002Fmongod.conf:\u002Fetc\u002Fmongod.conf:ro",[174,2255,2164],{"class":192},[174,2257,2259,2262],{"class":176,"line":2258},22,[174,2260,2261],{"class":199},"    networks",[174,2263,2058],{"class":192},[174,2265,2267,2269],{"class":176,"line":2266},23,[174,2268,2220],{"class":192},[174,2270,2271],{"class":1412}," mongo-cluster\n",[174,2273,2275,2278],{"class":176,"line":2274},24,[174,2276,2277],{"class":199},"    environment",[174,2279,2058],{"class":192},[174,2281,2283,2286,2288,2290,2293],{"class":176,"line":2282},25,[174,2284,2285],{"class":199},"      MONGO_INITDB_ROOT_USERNAME",[174,2287,203],{"class":192},[174,2289,2158],{"class":192},[174,2291,2292],{"class":1412},"${MONGO_ADMIN_USER:-admin}",[174,2294,2164],{"class":192},[174,2296,2298,2301,2303,2305,2308],{"class":176,"line":2297},26,[174,2299,2300],{"class":199},"      MONGO_INITDB_ROOT_PASSWORD",[174,2302,203],{"class":192},[174,2304,2158],{"class":192},[174,2306,2307],{"class":1412},"${MONGO_ADMIN_PASSWD:-veryStringPassword}",[174,2309,2164],{"class":192},[174,2311,2313,2316,2318,2320,2323],{"class":176,"line":2312},27,[174,2314,2315],{"class":199},"      MONGO_INITDB_DATABASE",[174,2317,203],{"class":192},[174,2319,2158],{"class":192},[174,2321,2322],{"class":1412},"${DB_NAME:-test}",[174,2324,2164],{"class":192},[174,2326,2328,2331],{"class":176,"line":2327},28,[174,2329,2330],{"class":199},"    healthcheck",[174,2332,2058],{"class":192},[174,2334,2336,2339,2341,2343,2345,2348,2350,2352,2354,2357,2359,2361,2363,2366,2368,2370,2372,2375,2377,2379,2381,2384,2386],{"class":176,"line":2335},29,[174,2337,2338],{"class":199},"      test",[174,2340,203],{"class":192},[174,2342,2185],{"class":192},[174,2344,2188],{"class":192},[174,2346,2347],{"class":1412},"CMD",[174,2349,2188],{"class":192},[174,2351,1050],{"class":192},[174,2353,2198],{"class":192},[174,2355,2356],{"class":1412},"mongosh",[174,2358,2188],{"class":192},[174,2360,1050],{"class":192},[174,2362,2198],{"class":192},[174,2364,2365],{"class":1412},"--quiet",[174,2367,2188],{"class":192},[174,2369,1050],{"class":192},[174,2371,2198],{"class":192},[174,2373,2374],{"class":1412},"--eval",[174,2376,2188],{"class":192},[174,2378,1050],{"class":192},[174,2380,2198],{"class":192},[174,2382,2383],{"class":1412},"try { rs.status().ok } catch(e) { rs.initiate().ok }",[174,2385,2188],{"class":192},[174,2387,2206],{"class":192},[174,2389,2391,2394,2396],{"class":176,"line":2390},30,[174,2392,2393],{"class":199},"      timeout",[174,2395,203],{"class":192},[174,2397,2398],{"class":1412}," 5m\n",[174,2400,2402,2405,2407],{"class":176,"line":2401},31,[174,2403,2404],{"class":199},"      interval",[174,2406,203],{"class":192},[174,2408,2409],{"class":1412}," 10s\n",[174,2411,2413,2416,2418],{"class":176,"line":2412},32,[174,2414,2415],{"class":199},"      retries",[174,2417,203],{"class":192},[174,2419,2420],{"class":649}," 5\n",[174,2422,2424,2427,2429],{"class":176,"line":2423},33,[174,2425,2426],{"class":199},"      start_period",[174,2428,203],{"class":192},[174,2430,2431],{"class":1412}," 12s\n",[174,2433,2435,2438],{"class":176,"line":2434},34,[174,2436,2437],{"class":199},"  mongo2",[174,2439,2058],{"class":192},[174,2441,2443,2445,2447],{"class":176,"line":2442},35,[174,2444,2143],{"class":199},[174,2446,203],{"class":192},[174,2448,2449],{"class":1412}," mongo2\n",[174,2451,2453,2455,2457,2459,2461],{"class":176,"line":2452},36,[174,2454,2153],{"class":199},[174,2456,203],{"class":192},[174,2458,2158],{"class":192},[174,2460,2161],{"class":1412},[174,2462,2164],{"class":192},[174,2464,2466,2468,2470],{"class":176,"line":2465},37,[174,2467,2170],{"class":199},[174,2469,203],{"class":192},[174,2471,2449],{"class":1412},[174,2473,2475,2477],{"class":176,"line":2474},38,[174,2476,2212],{"class":199},[174,2478,2058],{"class":192},[174,2480,2482,2484],{"class":176,"line":2481},39,[174,2483,2220],{"class":192},[174,2485,2486],{"class":1412}," mongo2_data:\u002Fdata\u002Fdb\n",[174,2488,2490,2492],{"class":176,"line":2489},40,[174,2491,2220],{"class":192},[174,2493,2494],{"class":1412}," mongo2_config:\u002Fdata\u002Fconfigdb\n",[174,2496,2498,2500,2502,2504],{"class":176,"line":2497},41,[174,2499,2220],{"class":192},[174,2501,2158],{"class":192},[174,2503,2241],{"class":1412},[174,2505,2164],{"class":192},[174,2507,2509,2511,2513,2515],{"class":176,"line":2508},42,[174,2510,2220],{"class":192},[174,2512,2158],{"class":192},[174,2514,2253],{"class":1412},[174,2516,2164],{"class":192},[174,2518,2520,2522],{"class":176,"line":2519},43,[174,2521,2261],{"class":199},[174,2523,2058],{"class":192},[174,2525,2527,2529],{"class":176,"line":2526},44,[174,2528,2220],{"class":192},[174,2530,2271],{"class":1412},[174,2532,2534,2536],{"class":176,"line":2533},45,[174,2535,2277],{"class":199},[174,2537,2058],{"class":192},[174,2539,2541,2543,2545,2547,2549],{"class":176,"line":2540},46,[174,2542,2285],{"class":199},[174,2544,203],{"class":192},[174,2546,2158],{"class":192},[174,2548,2292],{"class":1412},[174,2550,2164],{"class":192},[174,2552,2554,2556,2558,2560,2562],{"class":176,"line":2553},47,[174,2555,2300],{"class":199},[174,2557,203],{"class":192},[174,2559,2158],{"class":192},[174,2561,2307],{"class":1412},[174,2563,2164],{"class":192},[174,2565,2567,2570],{"class":176,"line":2566},48,[174,2568,2569],{"class":199},"  mongo3",[174,2571,2058],{"class":192},[174,2573,2575,2577,2579],{"class":176,"line":2574},49,[174,2576,2143],{"class":199},[174,2578,203],{"class":192},[174,2580,2581],{"class":1412}," mongo3\n",[174,2583,2585,2587,2589,2591,2593],{"class":176,"line":2584},50,[174,2586,2153],{"class":199},[174,2588,203],{"class":192},[174,2590,2158],{"class":192},[174,2592,2161],{"class":1412},[174,2594,2164],{"class":192},[174,2596,2598,2600,2602],{"class":176,"line":2597},51,[174,2599,2170],{"class":199},[174,2601,203],{"class":192},[174,2603,2581],{"class":1412},[174,2605,2607,2609],{"class":176,"line":2606},52,[174,2608,2212],{"class":199},[174,2610,2058],{"class":192},[174,2612,2614,2616],{"class":176,"line":2613},53,[174,2615,2220],{"class":192},[174,2617,2618],{"class":1412}," mongo3_data:\u002Fdata\u002Fdb\n",[174,2620,2622,2624],{"class":176,"line":2621},54,[174,2623,2220],{"class":192},[174,2625,2626],{"class":1412}," mongo3_config:\u002Fdata\u002Fconfigdb\n",[174,2628,2630,2632,2634,2636],{"class":176,"line":2629},55,[174,2631,2220],{"class":192},[174,2633,2158],{"class":192},[174,2635,2241],{"class":1412},[174,2637,2164],{"class":192},[174,2639,2641,2643,2645,2647],{"class":176,"line":2640},56,[174,2642,2220],{"class":192},[174,2644,2158],{"class":192},[174,2646,2253],{"class":1412},[174,2648,2164],{"class":192},[174,2650,2652,2654],{"class":176,"line":2651},57,[174,2653,2261],{"class":199},[174,2655,2058],{"class":192},[174,2657,2659,2661],{"class":176,"line":2658},58,[174,2660,2220],{"class":192},[174,2662,2271],{"class":1412},[174,2664,2666,2668],{"class":176,"line":2665},59,[174,2667,2277],{"class":199},[174,2669,2058],{"class":192},[174,2671,2673,2675,2677,2679,2681],{"class":176,"line":2672},60,[174,2674,2285],{"class":199},[174,2676,203],{"class":192},[174,2678,2158],{"class":192},[174,2680,2292],{"class":1412},[174,2682,2164],{"class":192},[174,2684,2686,2688,2690,2692,2694],{"class":176,"line":2685},61,[174,2687,2300],{"class":199},[174,2689,203],{"class":192},[174,2691,2158],{"class":192},[174,2693,2307],{"class":1412},[174,2695,2164],{"class":192},[174,2697,2699,2702],{"class":176,"line":2698},62,[174,2700,2701],{"class":199},"  mongo-init",[174,2703,2058],{"class":192},[174,2705,2707,2709,2711],{"class":176,"line":2706},63,[174,2708,2143],{"class":199},[174,2710,203],{"class":192},[174,2712,2713],{"class":1412}," mongo-init\n",[174,2715,2717,2719,2721],{"class":176,"line":2716},64,[174,2718,2153],{"class":199},[174,2720,203],{"class":192},[174,2722,2723],{"class":1412}," docker.io\u002Fmongo:8.2-noble\n",[174,2725,2727,2730,2732],{"class":176,"line":2726},65,[174,2728,2729],{"class":199},"    restart",[174,2731,203],{"class":192},[174,2733,2734],{"class":930}," no\n",[174,2736,2738,2741,2743,2745,2747,2750,2752,2754,2756,2759,2761],{"class":176,"line":2737},66,[174,2739,2740],{"class":199},"    entrypoint",[174,2742,203],{"class":192},[174,2744,2185],{"class":192},[174,2746,2188],{"class":192},[174,2748,2749],{"class":1412},"\u002Fbin\u002Fbash",[174,2751,2188],{"class":192},[174,2753,1050],{"class":192},[174,2755,2198],{"class":192},[174,2757,2758],{"class":1412},"\u002Finit.sh",[174,2760,2188],{"class":192},[174,2762,2206],{"class":192},[174,2764,2766,2768],{"class":176,"line":2765},67,[174,2767,2261],{"class":199},[174,2769,2058],{"class":192},[174,2771,2773,2775],{"class":176,"line":2772},68,[174,2774,2220],{"class":192},[174,2776,2271],{"class":1412},[174,2778,2780,2783],{"class":176,"line":2779},69,[174,2781,2782],{"class":199},"    depends_on",[174,2784,2058],{"class":192},[174,2786,2788,2790],{"class":176,"line":2787},70,[174,2789,2220],{"class":192},[174,2791,2148],{"class":1412},[174,2793,2795,2797],{"class":176,"line":2794},71,[174,2796,2220],{"class":192},[174,2798,2449],{"class":1412},[174,2800,2802,2804],{"class":176,"line":2801},72,[174,2803,2220],{"class":192},[174,2805,2581],{"class":1412},[174,2807,2809,2811],{"class":176,"line":2808},73,[174,2810,2212],{"class":199},[174,2812,2058],{"class":192},[174,2814,2816,2818],{"class":176,"line":2815},74,[174,2817,2220],{"class":192},[174,2819,2820],{"class":1412}," $PWD\u002Fscripts\u002Fmongo\u002Finit.js:\u002Finit.js\n",[174,2822,2824,2826],{"class":176,"line":2823},75,[174,2825,2220],{"class":192},[174,2827,2828],{"class":1412}," $PWD\u002Fscripts\u002Fmongo\u002Finit.sh:\u002Finit.sh\n",[174,2830,2832,2834],{"class":176,"line":2831},76,[174,2833,2277],{"class":199},[174,2835,2058],{"class":192},[174,2837,2839,2842,2844,2846,2848],{"class":176,"line":2838},77,[174,2840,2841],{"class":199},"      MONGO_ROOT_USERNAME",[174,2843,203],{"class":192},[174,2845,2158],{"class":192},[174,2847,2292],{"class":1412},[174,2849,2164],{"class":192},[174,2851,2853,2856,2858,2860,2862],{"class":176,"line":2852},78,[174,2854,2855],{"class":199},"      MONGO_ROOT_PASSWORD",[174,2857,203],{"class":192},[174,2859,2158],{"class":192},[174,2861,2307],{"class":1412},[174,2863,2164],{"class":192},[174,2865,2867,2870,2872,2874,2877],{"class":176,"line":2866},79,[174,2868,2869],{"class":199},"      DB_USERNAME",[174,2871,203],{"class":192},[174,2873,2158],{"class":192},[174,2875,2876],{"class":1412},"${DB_USERNAME:-myuser}",[174,2878,2164],{"class":192},[174,2880,2882,2885,2887,2889,2892],{"class":176,"line":2881},80,[174,2883,2884],{"class":199},"      DB_PASSWORD",[174,2886,203],{"class":192},[174,2888,2158],{"class":192},[174,2890,2891],{"class":1412},"${DB_PASSWORD:-mypassword}",[174,2893,2164],{"class":192},[17,2895,2896,2897,2899],{},"Décortiquons le fichier ",[24,2898,2005],{}," :",[36,2901,2902,2915,2928,2979],{},[39,2903,2904,2905,2907,2908,2910,2911,2019,2913,370],{},"On définit un réseau ",[24,2906,2028],{}," qui sera utilisé par tous les conteneurs MongoDB. Cela\ngarantit que les conteneurs peuvent communiquer entre eux via les hostnames ",[24,2909,2014],{},",\n",[24,2912,2018],{},[24,2914,2022],{},[39,2916,2917,2918,2015,2921,2019,2924,2927],{},"On définit trois volumes : ",[24,2919,2920],{},"mongo1_data",[24,2922,2923],{},"mongo2_data",[24,2925,2926],{},"mongo3_data"," pour persister les\ndonnées de chaque conteneur MongoDB.",[39,2929,2930,2931,2015,2933,2019,2935,2937,2938,2940,2941,2944,2945,2948,2949,2952,2953,2015,2955,2019,2957,2959,2960,2910,2962,2019,2965,2968,2969,2971,2972,2974,2975,2019,2977,370],{},"On définit trois services : ",[24,2932,2014],{},[24,2934,2018],{},[24,2936,2022],{},". Chaque service lance un\nconteneur MongoDB avec l'image ",[24,2939,2161],{},". On spécifie l'option ",[24,2942,2943],{},"--replSet"," pour\ndéfinir le nom du replica set à ",[24,2946,2947],{},"rs0",". On spécifie également l'option ",[24,2950,2951],{},"--bind_ip"," pour lier\nle conteneur aux hostnames ",[24,2954,2014],{},[24,2956,2018],{},[24,2958,2022],{},". On monte les volumes pour\npersister les données et le keyfile pour l'authentification. On expose les ports ",[24,2961,1544],{},[24,2963,2964],{},"27018",[24,2966,2967],{},"27019"," pour les conteneurs MongoDB. On définit un healthcheck pour le service\n",[24,2970,2014],{}," pour vérifier le statut du replica set et l'initialiser s'il ne l'est pas encore.\nAprès réussite du healthcheck, le replica set est initialisé avec le nœud primary ",[24,2973,2014],{},"\net les secondary ",[24,2976,2018],{},[24,2978,2022],{},[39,2980,2981,2982,2910,2985,2015,2988,2015,2991,2019,2994,2997],{},"On définit les variables d'environnement ",[24,2983,2984],{},"MONGO_INITDB_ROOT_USERNAME",[24,2986,2987],{},"MONGO_INITDB_ROOT_PASSWORD",[24,2989,2990],{},"MONGO_INITDB_DATABASE",[24,2992,2993],{},"DB_USERNAME",[24,2995,2996],{},"DB_PASSWORD"," pour\nchaque service pour configurer l'authentification MongoDB. On utilise l'interpolation pour\ngarantir l'usage des valeurs par défaut si les variables d'environnement ne sont pas\ndéfinies.",[151,2999,3001],{"id":3000},"étape-2-créer-un-keyfile-pour-lauthentification","Étape 2 : créer un keyfile pour l'authentification",[17,3003,3004],{},"MongoDB utilise des keyfiles pour l'authentification interne entre les membres du replica set.\nDans certains scénarios où aucun credentials n'est défini, on n'en a pas besoin. Mais pour les\nbesoins de ce guide, il faut créer un keyfile et le monter dans les conteneurs MongoDB,\npuisqu'on utilise l'authentification.",[17,3006,3007],{},"Pour créer un keyfile, lance la commande suivante :",[65,3009,3013],{"className":3010,"code":3011,"language":3012,"meta":73,"style":73},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","openssl rand -base64 756 > scripts\u002Fmongo\u002Frs_keyfile && chmod 400 scripts\u002Fmongo\u002Frs_keyfile\n","bash",[24,3014,3015],{"__ignoreMap":73},[174,3016,3017,3020,3023,3026,3029,3032,3035,3037,3040,3043],{"class":176,"line":177},[174,3018,3019],{"class":188},"openssl",[174,3021,3022],{"class":1412}," rand",[174,3024,3025],{"class":1412}," -base64",[174,3027,3028],{"class":649}," 756",[174,3030,3031],{"class":192}," >",[174,3033,3034],{"class":1412}," scripts\u002Fmongo\u002Frs_keyfile",[174,3036,878],{"class":192},[174,3038,3039],{"class":188}," chmod",[174,3041,3042],{"class":649}," 400",[174,3044,3045],{"class":1412}," scripts\u002Fmongo\u002Frs_keyfile\n",[17,3047,3048,3049,3052,3053,3056,3057,3060,3061,370],{},"Cette commande génère une clé aléatoire de 756 octets et la sauvegarde dans le fichier\n",[24,3050,3051],{},"scripts\u002Fmongo\u002Frs_keyfile",". On définit ensuite les permissions du fichier à ",[24,3054,3055],{},"400"," pour\ns'assurer que seul le propriétaire ",[24,3058,3059],{},"mongodb:mongodb"," peut lire et écrire le fichier. Tu peux\nen apprendre plus sur les keyfiles dans la\n",[1241,3062,3065],{"href":3063,"rel":3064},"https:\u002F\u002Fwww.mongodb.com\u002Fdocs\u002Fmanual\u002Ftutorial\u002Fdeploy-replica-set-with-keyfile-access-control\u002F",[1245],"doc MongoDB",[1593,3067,3068],{},[17,3069,3070],{},"Note : le keyfile sert à l'authentification interne entre les membres du replica set. Il est\nimportant de garder le keyfile sécurisé et de ne pas l'exposer à des utilisateurs non\nautorisés.",[151,3072,3074],{"id":3073},"étape-3-scripts-dinitialisation","Étape 3 : scripts d'initialisation",[3076,3077,3079],"h4",{"id":3078},"initialisation-mongodb","Initialisation MongoDB",[17,3081,3082,3083,3086,3087,3090],{},"Comme mentionné plus tôt, on va configurer une base de données initiale, un nom d'utilisateur\net un mot de passe pour le replica set MongoDB. Pour ce faire, on utilise un script\nd'initialisation qui sera exécuté au démarrage du conteneur MongoDB. Crée maintenant un\nfichier ",[24,3084,3085],{},"init.js"," dans le dossier ",[24,3088,3089],{},"scripts\u002Fmongo"," avec le contenu suivant :",[65,3092,3097],{"className":3093,"code":3094,"filename":3095,"language":3096,"meta":73,"style":73},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","db = db.getSiblingDB('admin');\n\ntry {\n  var rsConf = {\n    _id: 'okydookReplSet',\n    members: [\n      { _id: 0, host: 'mongo1:27017', priority: 2 },\n      { _id: 1, host: 'mongo2:27017' },\n      { _id: 2, host: 'mongo3:27017' }\n    ]\n  };\n  \n  \u002F\u002F Check if replica set is already initialized\n  var isInitialized = false;\n  try {\n    var status = rs.status();\n    if (status.ok === 1) {\n      isInitialized = true;\n      print(\"ReplicaSet already initiated\");\n    }\n  } catch(e) {\n    \u002F\u002F rs.status() throws error if not initialized yet\n    print(\"ReplicaSet not yet initiated, proceeding with initiation...\");\n  }\n  \n  if (!isInitialized) {\n    rs.initiate(rsConf);\n    print(\"ReplicaSet initiated successfully\");\n    \n    \u002F\u002F Wait for replica set to be ready\n    sleep(20000);\n  }\n} catch(e) {\n  print(\"Failed to initiate replica set: \" + e.message);\n  throw e;\n}\n","scripts\u002Fmongo\u002Finit.js","js",[24,3098,3099,3129,3133,3140,3152,3168,3178,3216,3241,3266,3271,3276,3281,3286,3300,3307,3330,3352,3364,3382,3387,3404,3409,3427,3431,3435,3451,3470,3487,3492,3497,3511,3515,3527,3555,3564],{"__ignoreMap":73},[174,3100,3101,3104,3106,3109,3111,3114,3116,3119,3122,3124,3126],{"class":176,"line":177},[174,3102,3103],{"class":350},"db ",[174,3105,1088],{"class":192},[174,3107,3108],{"class":350}," db",[174,3110,370],{"class":192},[174,3112,3113],{"class":322},"getSiblingDB",[174,3115,326],{"class":350},[174,3117,3118],{"class":192},"'",[174,3120,3121],{"class":1412},"admin",[174,3123,3118],{"class":192},[174,3125,512],{"class":350},[174,3127,3128],{"class":192},";\n",[174,3130,3131],{"class":176,"line":196},[174,3132,1097],{"emptyLinePlaceholder":1096},[174,3134,3135,3138],{"class":176,"line":209},[174,3136,3137],{"class":180},"try",[174,3139,193],{"class":192},[174,3141,3142,3145,3148,3150],{"class":176,"line":220},[174,3143,3144],{"class":184},"  var",[174,3146,3147],{"class":350}," rsConf",[174,3149,354],{"class":192},[174,3151,193],{"class":192},[174,3153,3154,3157,3159,3161,3164,3166],{"class":176,"line":230},[174,3155,3156],{"class":199},"    _id",[174,3158,203],{"class":192},[174,3160,2158],{"class":192},[174,3162,3163],{"class":1412},"okydookReplSet",[174,3165,3118],{"class":192},[174,3167,2910],{"class":192},[174,3169,3170,3173,3175],{"class":176,"line":240},[174,3171,3172],{"class":199},"    members",[174,3174,203],{"class":192},[174,3176,3177],{"class":199}," [\n",[174,3179,3180,3183,3186,3188,3190,3192,3195,3197,3199,3202,3204,3206,3209,3211,3213],{"class":176,"line":250},[174,3181,3182],{"class":192},"      {",[174,3184,3185],{"class":199}," _id",[174,3187,203],{"class":192},[174,3189,746],{"class":649},[174,3191,1050],{"class":192},[174,3193,3194],{"class":199}," host",[174,3196,203],{"class":192},[174,3198,2158],{"class":192},[174,3200,3201],{"class":1412},"mongo1:27017",[174,3203,3118],{"class":192},[174,3205,1050],{"class":192},[174,3207,3208],{"class":199}," priority",[174,3210,203],{"class":192},[174,3212,768],{"class":649},[174,3214,3215],{"class":192}," },\n",[174,3217,3218,3220,3222,3224,3226,3228,3230,3232,3234,3237,3239],{"class":176,"line":260},[174,3219,3182],{"class":192},[174,3221,3185],{"class":199},[174,3223,203],{"class":192},[174,3225,828],{"class":649},[174,3227,1050],{"class":192},[174,3229,3194],{"class":199},[174,3231,203],{"class":192},[174,3233,2158],{"class":192},[174,3235,3236],{"class":1412},"mongo2:27017",[174,3238,3118],{"class":192},[174,3240,3215],{"class":192},[174,3242,3243,3245,3247,3249,3251,3253,3255,3257,3259,3262,3264],{"class":176,"line":270},[174,3244,3182],{"class":192},[174,3246,3185],{"class":199},[174,3248,203],{"class":192},[174,3250,768],{"class":649},[174,3252,1050],{"class":192},[174,3254,3194],{"class":199},[174,3256,203],{"class":192},[174,3258,2158],{"class":192},[174,3260,3261],{"class":1412},"mongo3:27017",[174,3263,3118],{"class":192},[174,3265,953],{"class":192},[174,3267,3268],{"class":176,"line":280},[174,3269,3270],{"class":199},"    ]\n",[174,3272,3273],{"class":176,"line":290},[174,3274,3275],{"class":192},"  };\n",[174,3277,3278],{"class":176,"line":3},[174,3279,3280],{"class":199},"  \n",[174,3282,3283],{"class":176,"line":554},[174,3284,3285],{"class":1419},"  \u002F\u002F Check if replica set is already initialized\n",[174,3287,3288,3290,3293,3295,3298],{"class":176,"line":562},[174,3289,3144],{"class":184},[174,3291,3292],{"class":350}," isInitialized",[174,3294,354],{"class":192},[174,3296,3297],{"class":930}," false",[174,3299,3128],{"class":192},[174,3301,3302,3305],{"class":176,"line":2167},[174,3303,3304],{"class":180},"  try",[174,3306,193],{"class":192},[174,3308,3309,3312,3315,3317,3320,3322,3325,3328],{"class":176,"line":2177},[174,3310,3311],{"class":184},"    var",[174,3313,3314],{"class":350}," status",[174,3316,354],{"class":192},[174,3318,3319],{"class":350}," rs",[174,3321,370],{"class":192},[174,3323,3324],{"class":322},"status",[174,3326,3327],{"class":199},"()",[174,3329,3128],{"class":192},[174,3331,3332,3335,3337,3339,3341,3344,3346,3348,3350],{"class":176,"line":2209},[174,3333,3334],{"class":180},"    if",[174,3336,365],{"class":199},[174,3338,3324],{"class":350},[174,3340,370],{"class":192},[174,3342,3343],{"class":350},"ok",[174,3345,869],{"class":192},[174,3347,828],{"class":649},[174,3349,376],{"class":199},[174,3351,469],{"class":192},[174,3353,3354,3357,3359,3362],{"class":176,"line":2217},[174,3355,3356],{"class":350},"      isInitialized",[174,3358,354],{"class":192},[174,3360,3361],{"class":930}," true",[174,3363,3128],{"class":192},[174,3365,3366,3369,3371,3373,3376,3378,3380],{"class":176,"line":2226},[174,3367,3368],{"class":322},"      print",[174,3370,326],{"class":199},[174,3372,2188],{"class":192},[174,3374,3375],{"class":1412},"ReplicaSet already initiated",[174,3377,2188],{"class":192},[174,3379,512],{"class":199},[174,3381,3128],{"class":192},[174,3383,3384],{"class":176,"line":2234},[174,3385,3386],{"class":192},"    }\n",[174,3388,3389,3392,3395,3397,3400,3402],{"class":176,"line":2246},[174,3390,3391],{"class":192},"  }",[174,3393,3394],{"class":180}," catch",[174,3396,326],{"class":199},[174,3398,3399],{"class":350},"e",[174,3401,376],{"class":199},[174,3403,469],{"class":192},[174,3405,3406],{"class":176,"line":2258},[174,3407,3408],{"class":1419},"    \u002F\u002F rs.status() throws error if not initialized yet\n",[174,3410,3411,3414,3416,3418,3421,3423,3425],{"class":176,"line":2266},[174,3412,3413],{"class":322},"    print",[174,3415,326],{"class":199},[174,3417,2188],{"class":192},[174,3419,3420],{"class":1412},"ReplicaSet not yet initiated, proceeding with initiation...",[174,3422,2188],{"class":192},[174,3424,512],{"class":199},[174,3426,3128],{"class":192},[174,3428,3429],{"class":176,"line":2274},[174,3430,551],{"class":192},[174,3432,3433],{"class":176,"line":2282},[174,3434,3280],{"class":199},[174,3436,3437,3439,3441,3444,3447,3449],{"class":176,"line":2297},[174,3438,362],{"class":180},[174,3440,365],{"class":199},[174,3442,3443],{"class":192},"!",[174,3445,3446],{"class":350},"isInitialized",[174,3448,376],{"class":199},[174,3450,469],{"class":192},[174,3452,3453,3456,3458,3461,3463,3466,3468],{"class":176,"line":2312},[174,3454,3455],{"class":350},"    rs",[174,3457,370],{"class":192},[174,3459,3460],{"class":322},"initiate",[174,3462,326],{"class":199},[174,3464,3465],{"class":350},"rsConf",[174,3467,512],{"class":199},[174,3469,3128],{"class":192},[174,3471,3472,3474,3476,3478,3481,3483,3485],{"class":176,"line":2327},[174,3473,3413],{"class":322},[174,3475,326],{"class":199},[174,3477,2188],{"class":192},[174,3479,3480],{"class":1412},"ReplicaSet initiated successfully",[174,3482,2188],{"class":192},[174,3484,512],{"class":199},[174,3486,3128],{"class":192},[174,3488,3489],{"class":176,"line":2335},[174,3490,3491],{"class":199},"    \n",[174,3493,3494],{"class":176,"line":2390},[174,3495,3496],{"class":1419},"    \u002F\u002F Wait for replica set to be ready\n",[174,3498,3499,3502,3504,3507,3509],{"class":176,"line":2401},[174,3500,3501],{"class":322},"    sleep",[174,3503,326],{"class":199},[174,3505,3506],{"class":649},"20000",[174,3508,512],{"class":199},[174,3510,3128],{"class":192},[174,3512,3513],{"class":176,"line":2412},[174,3514,551],{"class":192},[174,3516,3517,3520,3522,3525],{"class":176,"line":2423},[174,3518,3519],{"class":192},"}",[174,3521,3394],{"class":180},[174,3523,3524],{"class":350},"(e) ",[174,3526,469],{"class":192},[174,3528,3529,3532,3534,3536,3539,3541,3543,3546,3548,3551,3553],{"class":176,"line":2434},[174,3530,3531],{"class":322},"  print",[174,3533,326],{"class":199},[174,3535,2188],{"class":192},[174,3537,3538],{"class":1412},"Failed to initiate replica set: ",[174,3540,2188],{"class":192},[174,3542,825],{"class":192},[174,3544,3545],{"class":350}," e",[174,3547,370],{"class":192},[174,3549,3550],{"class":350},"message",[174,3552,512],{"class":199},[174,3554,3128],{"class":192},[174,3556,3557,3560,3562],{"class":176,"line":2442},[174,3558,3559],{"class":180},"  throw",[174,3561,3545],{"class":350},[174,3563,3128],{"class":192},[174,3565,3566],{"class":176,"line":2452},[174,3567,302],{"class":192},[17,3569,2896,3570,2899],{},[24,3571,3085],{},[36,3573,3574,3579,3591],{},[39,3575,3576,3577,370],{},"On se connecte à la base spécifiée par la variable ",[24,3578,2990],{},[39,3580,3581,3582,2019,3584,3586,3587,3590],{},"On crée un utilisateur spécifié par les variables ",[24,3583,2993],{},[24,3585,2996],{}," avec le\nrôle ",[24,3588,3589],{},"readWrite"," sur la base.",[39,3592,3593,3594,3597,3598,3601],{},"On définit le write concern à ",[24,3595,3596],{},"majority"," avec un timeout de ",[24,3599,3600],{},"5000"," millisecondes. Cela\ngarantit que l'opération d'écriture est acquittée par la majorité des membres du replica set.",[17,3603,3604,3605,3610],{},"Ce script s'exécute au démarrage du conteneur MongoDB, créant la base et l'utilisateur\ninitiaux pour le replica set. Note que la partie scriptée n'est pas obligatoire et que tu\npourrais utiliser des valeurs en dur. Cependant, selon la\n",[1241,3606,3609],{"href":3607,"rel":3608},"https:\u002F\u002F12factor.net\u002Fconfig",[1245],"section III du 12-factor app",", il vaut mieux utiliser les\nvariables d'environnement pour stocker les secrets ou tout ce qui peut varier entre les\ndéploiements.",[3076,3612,3614],{"id":3613},"process-de-connexion-au-replicaset","Process de connexion au replicaset",[65,3616,3618],{"className":3010,"code":3617,"language":3012,"meta":73,"style":73},"#!\u002Fbin\u002Fbash\n\necho \"Waiting for MongoDB to be ready...\"\nsleep 15\n\n# Test connection\nuntil mongosh --host mongo1:27017 -u \"${MONGO_ROOT_USERNAME}\" -p \"${MONGO_ROOT_PASSWORD}\" --authenticationDatabase admin --eval \"db.adminCommand('ping')\" > \u002Fdev\u002Fnull 2>&1; do\n    echo \"Waiting for MongoDB connection...\"\n    sleep 5\ndone\n\necho \"MongoDB is ready, initializing replica set...\"\n\nmongosh --host mongo1:27017 \\\n    -u \"${MONGO_ROOT_USERNAME}\" \\\n    -p \"${MONGO_ROOT_PASSWORD}\" \\\n    --authenticationDatabase admin \\\n    --eval \"\n        var MONGO_DB_USER='${MONGO_DB_USER}';\n        var MONGO_DB_PASSWORD='${MONGO_DB_PASSWORD}';\n    \" \\\n    --file \u002Finit.js\n\nif [ $? -eq 0 ]; then\n    echo \"Initialization completed successfully\"\nelse\n    echo \"Initialization failed\"\n    exit 1\nfi\n",[24,3619,3620,3625,3629,3642,3650,3654,3659,3721,3733,3739,3744,3748,3759,3763,3774,3787,3800,3809,3817,3833,3847,3854,3862,3866,3887,3898,3903,3914,3922],{"__ignoreMap":73},[174,3621,3622],{"class":176,"line":177},[174,3623,3624],{"class":1419},"#!\u002Fbin\u002Fbash\n",[174,3626,3627],{"class":176,"line":196},[174,3628,1097],{"emptyLinePlaceholder":1096},[174,3630,3631,3634,3636,3639],{"class":176,"line":209},[174,3632,3633],{"class":322},"echo",[174,3635,2198],{"class":192},[174,3637,3638],{"class":1412},"Waiting for MongoDB to be ready...",[174,3640,3641],{"class":192},"\"\n",[174,3643,3644,3647],{"class":176,"line":220},[174,3645,3646],{"class":188},"sleep",[174,3648,3649],{"class":649}," 15\n",[174,3651,3652],{"class":176,"line":230},[174,3653,1097],{"emptyLinePlaceholder":1096},[174,3655,3656],{"class":176,"line":240},[174,3657,3658],{"class":1419},"# Test connection\n",[174,3660,3661,3664,3667,3670,3673,3676,3679,3682,3685,3687,3689,3692,3694,3697,3700,3703,3705,3708,3710,3712,3715,3718],{"class":176,"line":250},[174,3662,3663],{"class":180},"until",[174,3665,3666],{"class":188}," mongosh",[174,3668,3669],{"class":1412}," --host",[174,3671,3672],{"class":1412}," mongo1:27017",[174,3674,3675],{"class":1412}," -u",[174,3677,3678],{"class":192}," \"${",[174,3680,3681],{"class":350},"MONGO_ROOT_USERNAME",[174,3683,3684],{"class":192},"}\"",[174,3686,1461],{"class":1412},[174,3688,3678],{"class":192},[174,3690,3691],{"class":350},"MONGO_ROOT_PASSWORD",[174,3693,3684],{"class":192},[174,3695,3696],{"class":1412}," --authenticationDatabase",[174,3698,3699],{"class":1412}," admin",[174,3701,3702],{"class":1412}," --eval",[174,3704,2198],{"class":192},[174,3706,3707],{"class":1412},"db.adminCommand('ping')",[174,3709,2188],{"class":192},[174,3711,3031],{"class":192},[174,3713,3714],{"class":1412}," \u002Fdev\u002Fnull",[174,3716,3717],{"class":192}," 2>&1;",[174,3719,3720],{"class":180}," do\n",[174,3722,3723,3726,3728,3731],{"class":176,"line":260},[174,3724,3725],{"class":322},"    echo",[174,3727,2198],{"class":192},[174,3729,3730],{"class":1412},"Waiting for MongoDB connection...",[174,3732,3641],{"class":192},[174,3734,3735,3737],{"class":176,"line":270},[174,3736,3501],{"class":188},[174,3738,2420],{"class":649},[174,3740,3741],{"class":176,"line":280},[174,3742,3743],{"class":180},"done\n",[174,3745,3746],{"class":176,"line":290},[174,3747,1097],{"emptyLinePlaceholder":1096},[174,3749,3750,3752,3754,3757],{"class":176,"line":3},[174,3751,3633],{"class":322},[174,3753,2198],{"class":192},[174,3755,3756],{"class":1412},"MongoDB is ready, initializing replica set...",[174,3758,3641],{"class":192},[174,3760,3761],{"class":176,"line":554},[174,3762,1097],{"emptyLinePlaceholder":1096},[174,3764,3765,3767,3769,3771],{"class":176,"line":562},[174,3766,2356],{"class":188},[174,3768,3669],{"class":1412},[174,3770,3672],{"class":1412},[174,3772,3773],{"class":350}," \\\n",[174,3775,3776,3779,3781,3783,3785],{"class":176,"line":2167},[174,3777,3778],{"class":1412},"    -u",[174,3780,3678],{"class":192},[174,3782,3681],{"class":350},[174,3784,3684],{"class":192},[174,3786,3773],{"class":350},[174,3788,3789,3792,3794,3796,3798],{"class":176,"line":2177},[174,3790,3791],{"class":1412},"    -p",[174,3793,3678],{"class":192},[174,3795,3691],{"class":350},[174,3797,3684],{"class":192},[174,3799,3773],{"class":350},[174,3801,3802,3805,3807],{"class":176,"line":2209},[174,3803,3804],{"class":1412},"    --authenticationDatabase",[174,3806,3699],{"class":1412},[174,3808,3773],{"class":350},[174,3810,3811,3814],{"class":176,"line":2217},[174,3812,3813],{"class":1412},"    --eval",[174,3815,3816],{"class":192}," \"\n",[174,3818,3819,3822,3825,3828,3830],{"class":176,"line":2226},[174,3820,3821],{"class":1412},"        var MONGO_DB_USER='",[174,3823,3824],{"class":192},"${",[174,3826,3827],{"class":350},"MONGO_DB_USER",[174,3829,3519],{"class":192},[174,3831,3832],{"class":1412},"';\n",[174,3834,3835,3838,3840,3843,3845],{"class":176,"line":2234},[174,3836,3837],{"class":1412},"        var MONGO_DB_PASSWORD='",[174,3839,3824],{"class":192},[174,3841,3842],{"class":350},"MONGO_DB_PASSWORD",[174,3844,3519],{"class":192},[174,3846,3832],{"class":1412},[174,3848,3849,3852],{"class":176,"line":2246},[174,3850,3851],{"class":192},"    \"",[174,3853,3773],{"class":350},[174,3855,3856,3859],{"class":176,"line":2258},[174,3857,3858],{"class":1412},"    --file",[174,3860,3861],{"class":1412}," \u002Finit.js\n",[174,3863,3864],{"class":176,"line":2266},[174,3865,1097],{"emptyLinePlaceholder":1096},[174,3867,3868,3871,3873,3876,3879,3881,3884],{"class":176,"line":2274},[174,3869,3870],{"class":180},"if",[174,3872,2185],{"class":192},[174,3874,3875],{"class":350}," $? ",[174,3877,3878],{"class":192},"-eq",[174,3880,746],{"class":649},[174,3882,3883],{"class":192}," ];",[174,3885,3886],{"class":180}," then\n",[174,3888,3889,3891,3893,3896],{"class":176,"line":2282},[174,3890,3725],{"class":322},[174,3892,2198],{"class":192},[174,3894,3895],{"class":1412},"Initialization completed successfully",[174,3897,3641],{"class":192},[174,3899,3900],{"class":176,"line":2297},[174,3901,3902],{"class":180},"else\n",[174,3904,3905,3907,3909,3912],{"class":176,"line":2312},[174,3906,3725],{"class":322},[174,3908,2198],{"class":192},[174,3910,3911],{"class":1412},"Initialization failed",[174,3913,3641],{"class":192},[174,3915,3916,3919],{"class":176,"line":2327},[174,3917,3918],{"class":322},"    exit",[174,3920,3921],{"class":649}," 1\n",[174,3923,3924],{"class":176,"line":2335},[174,3925,3926],{"class":180},"fi\n",[151,3928,3930],{"id":3929},"étape-4-démarrer-le-cluster-mongodb-replica-set","Étape 4 : démarrer le cluster MongoDB Replica Set",[17,3932,3933],{},"Maintenant que tout est en place, on peut démarrer le cluster MongoDB replica set avec Docker\nCompose en exécutant la commande suivante :",[65,3935,3937],{"className":3010,"code":3936,"language":3012,"meta":73,"style":73},"docker-compose up -d\n",[24,3938,3939],{"__ignoreMap":73},[174,3940,3941,3944,3947],{"class":176,"line":177},[174,3942,3943],{"class":188},"docker-compose",[174,3945,3946],{"class":1412}," up",[174,3948,3949],{"class":1412}," -d\n",[1593,3951,3952],{},[17,3953,3954,3955,3958],{},"Note : le flag ",[24,3956,3957],{},"-d"," lance les conteneurs en mode détaché, ce qui signifie qu'ils tournent en\narrière-plan.",[151,3960,3962],{"id":3961},"étape-5-vérifier-le-replica-set-mongodb","Étape 5 : vérifier le replica set MongoDB",[17,3964,3965,3966,3968],{},"Pour vérifier que le replica set MongoDB tourne correctement, tu peux utiliser le shell\n",[24,3967,1521],{}," pour te connecter au nœud primary et vérifier le statut du replica set. Lance la\ncommande suivante pour te connecter au primary :",[65,3970,3972],{"className":3010,"code":3971,"language":3012,"meta":73,"style":73},"docker exec -it mongo_rs0 mongosh -u admin -p veryStringPassword --authenticationDatabase admin\n",[24,3973,3974],{"__ignoreMap":73},[174,3975,3976,3978,3980,3982,3985,3987,3989,3991,3993,3996,3998],{"class":176,"line":177},[174,3977,1409],{"class":188},[174,3979,1495],{"class":1412},[174,3981,1498],{"class":1412},[174,3983,3984],{"class":1412}," mongo_rs0",[174,3986,3666],{"class":1412},[174,3988,3675],{"class":1412},[174,3990,3699],{"class":1412},[174,3992,1461],{"class":1412},[174,3994,3995],{"class":1412}," veryStringPassword",[174,3997,3696],{"class":1412},[174,3999,4000],{"class":1412}," admin\n",[1593,4002,4003],{},[17,4004,4005,4006,4009,4010,4012,4013,370],{},"Note : remplace ",[24,4007,4008],{},"veryStringPassword"," par le mot de passe que tu as défini pour la variable\n",[24,4011,2987],{},". Si tu n'en as pas défini, utilise ",[24,4014,4008],{},[17,4016,4017],{},"Une fois connecté, lance la commande suivante pour vérifier le statut du replica set :",[65,4019,4021],{"className":3010,"code":4020,"language":3012,"meta":73,"style":73},"rs.status()\n",[24,4022,4023],{"__ignoreMap":73},[174,4024,4025,4028],{"class":176,"line":177},[174,4026,4027],{"class":322},"rs.status",[174,4029,4030],{"class":192},"()\n",[17,4032,4033,4034,4036,4037,2019,4039,4041],{},"Tu devrais voir le statut du replica set avec le nœud primary ",[24,4035,2014],{}," et les secondary\n",[24,4038,2018],{},[24,4040,2022],{},". La sortie devrait ressembler à :",[65,4043,4047],{"className":4044,"code":4045,"language":4046,"meta":73,"style":73},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","members: [\n    {\n      _id: 0,\n      name: 'mongo1:27017',\n      health: 1,\n      state: 1,\n      stateStr: 'PRIMARY',\n      uptime: 478,\n      electionTime: Timestamp({ t: 1721354075, i: 1 }),\n      electionDate: ISODate('2024-07-19T01:54:35.000Z'),\n        ...\n    },\n    {\n      _id: 1,\n      name: 'mongo2:27018',\n      health: 1,\n      state: 2,\n      stateStr: 'SECONDARY',\n      uptime: 471,\n      syncSourceHost: 'mongo1:27017',\n        ...\n    },\n    {\n      _id: 2,\n      name: 'mongo3:27019',\n      health: 1,\n      state: 2,\n      stateStr: 'SECONDARY',\n      uptime: 471,\n      syncSourceHost: 'mongo1:27017',\n        ...\n    }\n  ],\n  ok: 1,\n  '$clusterTime': {\n    clusterTime: Timestamp({ t: 1721354536, i: 1 }),\n    signature: {\n      hash: Binary.createFromBase64('8KKPt\u002FSmHkhGZDH299+yq6j5i04=', 0),\n      keyId: Long('7393159456961331205')\n    }\n  },\n","json",[24,4048,4049,4057,4062,4073,4093,4104,4115,4127,4139,4173,4207,4212,4217,4221,4231,4250,4260,4270,4281,4292,4311,4315,4319,4323,4333,4352,4362,4372,4382,4392,4410,4414,4418,4425,4434,4441,4473,4482,4531,4547,4551],{"__ignoreMap":73},[174,4050,4051,4054],{"class":176,"line":177},[174,4052,4053],{"class":350},"members: ",[174,4055,4056],{"class":192},"[\n",[174,4058,4059],{"class":176,"line":196},[174,4060,4061],{"class":192},"    {\n",[174,4063,4064,4067,4069,4071],{"class":176,"line":209},[174,4065,4066],{"class":350},"      _id",[174,4068,203],{"class":192},[174,4070,746],{"class":649},[174,4072,2910],{"class":192},[174,4074,4075,4078,4080,4083,4085,4087,4089,4091],{"class":176,"line":220},[174,4076,4077],{"class":350},"      name",[174,4079,203],{"class":192},[174,4081,4082],{"class":350}," 'mongo",[174,4084,650],{"class":649},[174,4086,203],{"class":350},[174,4088,1544],{"class":649},[174,4090,3118],{"class":350},[174,4092,2910],{"class":192},[174,4094,4095,4098,4100,4102],{"class":176,"line":230},[174,4096,4097],{"class":350},"      health",[174,4099,203],{"class":192},[174,4101,828],{"class":649},[174,4103,2910],{"class":192},[174,4105,4106,4109,4111,4113],{"class":176,"line":240},[174,4107,4108],{"class":350},"      state",[174,4110,203],{"class":192},[174,4112,828],{"class":649},[174,4114,2910],{"class":192},[174,4116,4117,4120,4122,4125],{"class":176,"line":250},[174,4118,4119],{"class":350},"      stateStr",[174,4121,203],{"class":192},[174,4123,4124],{"class":350}," 'PRIMARY'",[174,4126,2910],{"class":192},[174,4128,4129,4132,4134,4137],{"class":176,"line":260},[174,4130,4131],{"class":350},"      uptime",[174,4133,203],{"class":192},[174,4135,4136],{"class":649}," 478",[174,4138,2910],{"class":192},[174,4140,4141,4144,4146,4149,4151,4154,4156,4159,4161,4163,4165,4167,4169,4171],{"class":176,"line":270},[174,4142,4143],{"class":350},"      electionTime",[174,4145,203],{"class":192},[174,4147,4148],{"class":350}," Timestamp(",[174,4150,1155],{"class":192},[174,4152,4153],{"class":350}," t",[174,4155,203],{"class":192},[174,4157,4158],{"class":649}," 1721354075",[174,4160,1050],{"class":192},[174,4162,741],{"class":350},[174,4164,203],{"class":192},[174,4166,828],{"class":649},[174,4168,1172],{"class":192},[174,4170,512],{"class":350},[174,4172,2910],{"class":192},[174,4174,4175,4178,4180,4183,4186,4189,4192,4194,4197,4199,4202,4205],{"class":176,"line":280},[174,4176,4177],{"class":350},"      electionDate",[174,4179,203],{"class":192},[174,4181,4182],{"class":350}," ISODate('",[174,4184,4185],{"class":649},"2024-07-19",[174,4187,4188],{"class":350},"T",[174,4190,4191],{"class":649},"01",[174,4193,203],{"class":350},[174,4195,4196],{"class":649},"54",[174,4198,203],{"class":350},[174,4200,4201],{"class":649},"35.000",[174,4203,4204],{"class":350},"Z')",[174,4206,2910],{"class":192},[174,4208,4209],{"class":176,"line":290},[174,4210,4211],{"class":350},"        ...\n",[174,4213,4214],{"class":176,"line":3},[174,4215,4216],{"class":192},"    },\n",[174,4218,4219],{"class":176,"line":554},[174,4220,4061],{"class":192},[174,4222,4223,4225,4227,4229],{"class":176,"line":562},[174,4224,4066],{"class":350},[174,4226,203],{"class":192},[174,4228,828],{"class":649},[174,4230,2910],{"class":192},[174,4232,4233,4235,4237,4239,4242,4244,4246,4248],{"class":176,"line":2167},[174,4234,4077],{"class":350},[174,4236,203],{"class":192},[174,4238,4082],{"class":350},[174,4240,4241],{"class":649},"2",[174,4243,203],{"class":350},[174,4245,2964],{"class":649},[174,4247,3118],{"class":350},[174,4249,2910],{"class":192},[174,4251,4252,4254,4256,4258],{"class":176,"line":2177},[174,4253,4097],{"class":350},[174,4255,203],{"class":192},[174,4257,828],{"class":649},[174,4259,2910],{"class":192},[174,4261,4262,4264,4266,4268],{"class":176,"line":2209},[174,4263,4108],{"class":350},[174,4265,203],{"class":192},[174,4267,768],{"class":649},[174,4269,2910],{"class":192},[174,4271,4272,4274,4276,4279],{"class":176,"line":2217},[174,4273,4119],{"class":350},[174,4275,203],{"class":192},[174,4277,4278],{"class":350}," 'SECONDARY'",[174,4280,2910],{"class":192},[174,4282,4283,4285,4287,4290],{"class":176,"line":2226},[174,4284,4131],{"class":350},[174,4286,203],{"class":192},[174,4288,4289],{"class":649}," 471",[174,4291,2910],{"class":192},[174,4293,4294,4297,4299,4301,4303,4305,4307,4309],{"class":176,"line":2234},[174,4295,4296],{"class":350},"      syncSourceHost",[174,4298,203],{"class":192},[174,4300,4082],{"class":350},[174,4302,650],{"class":649},[174,4304,203],{"class":350},[174,4306,1544],{"class":649},[174,4308,3118],{"class":350},[174,4310,2910],{"class":192},[174,4312,4313],{"class":176,"line":2246},[174,4314,4211],{"class":350},[174,4316,4317],{"class":176,"line":2258},[174,4318,4216],{"class":192},[174,4320,4321],{"class":176,"line":2266},[174,4322,4061],{"class":192},[174,4324,4325,4327,4329,4331],{"class":176,"line":2274},[174,4326,4066],{"class":350},[174,4328,203],{"class":192},[174,4330,768],{"class":649},[174,4332,2910],{"class":192},[174,4334,4335,4337,4339,4341,4344,4346,4348,4350],{"class":176,"line":2282},[174,4336,4077],{"class":350},[174,4338,203],{"class":192},[174,4340,4082],{"class":350},[174,4342,4343],{"class":649},"3",[174,4345,203],{"class":350},[174,4347,2967],{"class":649},[174,4349,3118],{"class":350},[174,4351,2910],{"class":192},[174,4353,4354,4356,4358,4360],{"class":176,"line":2297},[174,4355,4097],{"class":350},[174,4357,203],{"class":192},[174,4359,828],{"class":649},[174,4361,2910],{"class":192},[174,4363,4364,4366,4368,4370],{"class":176,"line":2312},[174,4365,4108],{"class":350},[174,4367,203],{"class":192},[174,4369,768],{"class":649},[174,4371,2910],{"class":192},[174,4373,4374,4376,4378,4380],{"class":176,"line":2327},[174,4375,4119],{"class":350},[174,4377,203],{"class":192},[174,4379,4278],{"class":350},[174,4381,2910],{"class":192},[174,4383,4384,4386,4388,4390],{"class":176,"line":2335},[174,4385,4131],{"class":350},[174,4387,203],{"class":192},[174,4389,4289],{"class":649},[174,4391,2910],{"class":192},[174,4393,4394,4396,4398,4400,4402,4404,4406,4408],{"class":176,"line":2390},[174,4395,4296],{"class":350},[174,4397,203],{"class":192},[174,4399,4082],{"class":350},[174,4401,650],{"class":649},[174,4403,203],{"class":350},[174,4405,1544],{"class":649},[174,4407,3118],{"class":350},[174,4409,2910],{"class":192},[174,4411,4412],{"class":176,"line":2401},[174,4413,4211],{"class":350},[174,4415,4416],{"class":176,"line":2412},[174,4417,3386],{"class":192},[174,4419,4420,4423],{"class":176,"line":2423},[174,4421,4422],{"class":192},"  ]",[174,4424,2910],{"class":350},[174,4426,4427,4430,4432],{"class":176,"line":2434},[174,4428,4429],{"class":350},"  ok: ",[174,4431,650],{"class":649},[174,4433,2910],{"class":350},[174,4435,4436,4439],{"class":176,"line":2442},[174,4437,4438],{"class":350},"  '$clusterTime': ",[174,4440,469],{"class":192},[174,4442,4443,4446,4448,4450,4452,4454,4456,4459,4461,4463,4465,4467,4469,4471],{"class":176,"line":2452},[174,4444,4445],{"class":350},"    clusterTime",[174,4447,203],{"class":192},[174,4449,4148],{"class":350},[174,4451,1155],{"class":192},[174,4453,4153],{"class":350},[174,4455,203],{"class":192},[174,4457,4458],{"class":649}," 1721354536",[174,4460,1050],{"class":192},[174,4462,741],{"class":350},[174,4464,203],{"class":192},[174,4466,828],{"class":649},[174,4468,1172],{"class":192},[174,4470,512],{"class":350},[174,4472,2910],{"class":192},[174,4474,4475,4478,4480],{"class":176,"line":2465},[174,4476,4477],{"class":350},"    signature",[174,4479,203],{"class":192},[174,4481,193],{"class":192},[174,4483,4484,4487,4489,4492,4495,4498,4500,4503,4506,4509,4512,4515,4518,4520,4523,4526,4528],{"class":176,"line":2474},[174,4485,4486],{"class":350},"      hash",[174,4488,203],{"class":192},[174,4490,4491],{"class":350}," Binary.createFromBase",[174,4493,4494],{"class":649},"64",[174,4496,4497],{"class":350},"('",[174,4499,1047],{"class":649},[174,4501,4502],{"class":350},"KKPt\u002FSmHkhGZDH",[174,4504,4505],{"class":649},"299",[174,4507,4508],{"class":350},"+yq",[174,4510,4511],{"class":649},"6",[174,4513,4514],{"class":350},"j",[174,4516,4517],{"class":649},"5",[174,4519,801],{"class":350},[174,4521,4522],{"class":649},"04",[174,4524,4525],{"class":350},"='",[174,4527,1050],{"class":192},[174,4529,4530],{"class":350}," 0),\n",[174,4532,4533,4536,4538,4541,4544],{"class":176,"line":2481},[174,4534,4535],{"class":350},"      keyId",[174,4537,203],{"class":192},[174,4539,4540],{"class":350}," Long('",[174,4542,4543],{"class":649},"7393159456961331205",[174,4545,4546],{"class":350},"')\n",[174,4548,4549],{"class":176,"line":2489},[174,4550,3386],{"class":192},[174,4552,4553,4555],{"class":176,"line":2497},[174,4554,3391],{"class":192},[174,4556,2910],{"class":350},[1593,4558,4559],{},[17,4560,4561],{},"Note : c'est une sortie tronquée, et elle peut varier selon la version de MongoDB utilisée.",[12,4563,4565],{"id":4564},"bonus-ajouter-mongo-express-pour-une-administration-web","Bonus : ajouter Mongo-Express pour une administration web",[17,4567,4568,4569,4572,4573,4575],{},"Ajoutons un outil d'administration web pour MongoDB en utilisant ",[24,4570,4571],{},"mongo-express",". Pour ajouter\n",[24,4574,4571],{}," au fichier Docker Compose, ajoute la définition de service suivante :",[65,4577,4579],{"className":2046,"code":4578,"filename":2005,"language":2048,"meta":73,"style":73},"mongo-express:\n  container_name: mongo-express\n  image: 'mongo-express:latest'\n  ports:\n    - '8081:8081'\n  networks:\n    - mongo-cluster\n  environment:\n    ME_CONFIG_BASICAUTH: false\n    ME_CONFIG_MONGODB_ENABLE_ADMIN: false\n    ME_CONFIG_MONGODB_ADMINUSERNAME: '${MONGO_ADMIN_USER:-admin}'\n    ME_CONFIG_MONGODB_ADMINPASSWORD: '${MONGO_ADMIN_PASSWD:-veryStrongPassword}'\n    ME_CONFIG_MONGODB_URL: >-\n      mongodb:\u002F\u002F${DB_USERNAME:-myuser}:${DB_PASSWORD:-veryStringPassword}@mongo1:27017,mongo2:27018,mongo3:27019\u002F${DB_NAME}?replicaSet=rs0\n  depends_on:\n    mongo1:\n      condition: service_healthy\n\n",[24,4580,4581,4587,4597,4611,4618,4630,4637,4643,4650,4659,4668,4681,4695,4707,4712,4719,4726],{"__ignoreMap":73},[174,4582,4583,4585],{"class":176,"line":177},[174,4584,4571],{"class":199},[174,4586,2058],{"class":192},[174,4588,4589,4592,4594],{"class":176,"line":196},[174,4590,4591],{"class":199},"  container_name",[174,4593,203],{"class":192},[174,4595,4596],{"class":1412}," mongo-express\n",[174,4598,4599,4602,4604,4606,4609],{"class":176,"line":209},[174,4600,4601],{"class":199},"  image",[174,4603,203],{"class":192},[174,4605,2158],{"class":192},[174,4607,4608],{"class":1412},"mongo-express:latest",[174,4610,2164],{"class":192},[174,4612,4613,4616],{"class":176,"line":220},[174,4614,4615],{"class":199},"  ports",[174,4617,2058],{"class":192},[174,4619,4620,4623,4625,4628],{"class":176,"line":230},[174,4621,4622],{"class":192},"    -",[174,4624,2158],{"class":192},[174,4626,4627],{"class":1412},"8081:8081",[174,4629,2164],{"class":192},[174,4631,4632,4635],{"class":176,"line":240},[174,4633,4634],{"class":199},"  networks",[174,4636,2058],{"class":192},[174,4638,4639,4641],{"class":176,"line":250},[174,4640,4622],{"class":192},[174,4642,2271],{"class":1412},[174,4644,4645,4648],{"class":176,"line":260},[174,4646,4647],{"class":199},"  environment",[174,4649,2058],{"class":192},[174,4651,4652,4655,4657],{"class":176,"line":270},[174,4653,4654],{"class":199},"    ME_CONFIG_BASICAUTH",[174,4656,203],{"class":192},[174,4658,948],{"class":930},[174,4660,4661,4664,4666],{"class":176,"line":280},[174,4662,4663],{"class":199},"    ME_CONFIG_MONGODB_ENABLE_ADMIN",[174,4665,203],{"class":192},[174,4667,948],{"class":930},[174,4669,4670,4673,4675,4677,4679],{"class":176,"line":290},[174,4671,4672],{"class":199},"    ME_CONFIG_MONGODB_ADMINUSERNAME",[174,4674,203],{"class":192},[174,4676,2158],{"class":192},[174,4678,2292],{"class":1412},[174,4680,2164],{"class":192},[174,4682,4683,4686,4688,4690,4693],{"class":176,"line":3},[174,4684,4685],{"class":199},"    ME_CONFIG_MONGODB_ADMINPASSWORD",[174,4687,203],{"class":192},[174,4689,2158],{"class":192},[174,4691,4692],{"class":1412},"${MONGO_ADMIN_PASSWD:-veryStrongPassword}",[174,4694,2164],{"class":192},[174,4696,4697,4700,4702,4704],{"class":176,"line":554},[174,4698,4699],{"class":199},"    ME_CONFIG_MONGODB_URL",[174,4701,203],{"class":192},[174,4703,3031],{"class":180},[174,4705,4706],{"class":184},"-\n",[174,4708,4709],{"class":176,"line":562},[174,4710,4711],{"class":1412},"      mongodb:\u002F\u002F${DB_USERNAME:-myuser}:${DB_PASSWORD:-veryStringPassword}@mongo1:27017,mongo2:27018,mongo3:27019\u002F${DB_NAME}?replicaSet=rs0\n",[174,4713,4714,4717],{"class":176,"line":2167},[174,4715,4716],{"class":199},"  depends_on",[174,4718,2058],{"class":192},[174,4720,4721,4724],{"class":176,"line":2177},[174,4722,4723],{"class":199},"    mongo1",[174,4725,2058],{"class":192},[174,4727,4728,4731,4733],{"class":176,"line":2209},[174,4729,4730],{"class":199},"      condition",[174,4732,203],{"class":192},[174,4734,4735],{"class":1412}," service_healthy\n",[1593,4737,4738],{},[17,4739,4740,4741,4743,4744,4746,4747,370],{},"Note : le service ",[24,4742,4571],{}," dépend du service ",[24,4745,2014],{}," healthy. Cela garantit que le\nreplica set est en marche avant de démarrer le service ",[24,4748,4571],{},[17,4750,4751,4752,4754,4755,4758],{},"Tu peux accéder à ",[24,4753,4571],{}," à ",[24,4756,4757],{},"http:\u002F\u002Flocalhost:8081"," dans ton navigateur, et avoir\nquelque chose comme :",[1557,4760],{"alt":4761,"className":4762,"source":4764},"Connexion réussie à mongo-express",[1921,4763,1923],"max-w-full","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fhow-to\u002Fmongo-express",[1593,4766,4767],{},[17,4768,4769,4770,4773,4774,2015,4776,2019,4778,4781,4782,370],{},"Note : pour connecter ton application au replica set MongoDB, tu peux utiliser la chaîne de\nconnexion ",[24,4771,4772],{},"mongodb:\u002F\u002F${DB_USERNAME}:${DB_PASSWORD}@mongo1:27017,mongo2:27018,mongo3:27019\u002F${DB_NAME}?replicaSet=rs0",".\nPense aussi à remplacer ",[24,4775,2993],{},[24,4777,2996],{},[24,4779,4780],{},"DB_NAME"," par les credentials de ton\napplication en définissant les variables d'environnement dans le fichier ",[24,4783,4784],{},".env",[17,4786,4787],{},"Si tout est bien configuré, tu devrais avoir quelque chose comme ceci dans ton Docker\nDesktop :",[1557,4789],{"alt":4790,"className":4791,"source":4792},"Replicaset créé avec succès",[1921,4763,1923],"https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fhow-to\u002Fdocker-desktop",[17,4794,4795],{},"Et voilà, tu as un cluster MongoDB replica set avec un outil d'administration web !",[12,4797,1800],{"id":1799},[17,4799,4800,4801,4803],{},"Dans cet article, je t'ai guidé à travers la mise en place d'un cluster MongoDB replica set à\n3 nœuds avec Docker Compose. On a couvert les bases des replica sets MongoDB, les prérequis\npour mettre en place un replica set, et les étapes pour créer un fichier Docker Compose qui le\nconfigure. Je t'ai aussi montré comment vérifier le replica set et ajouter un outil\nd'administration web avec ",[24,4802,4571],{},". J'espère que ce guide t'aidera à démarrer avec les\nreplica sets MongoDB et Docker Compose.",[17,4805,4806,4807,4810],{},"Dans un futur article, je parlerai de quelque chose de plus important :\n",[580,4808,4809],{},"comment sauvegarder et restaurer une base MongoDB dans un environnement conteneurisé",".\nTu as peut-être entendu que les conteneurs Docker sont éphémères, donc il est important\nd'avoir une stratégie de backup en place pour éviter la perte de données. Reste à l'écoute !",[17,4812,4813],{},"Si tu as des questions ou des retours, n'hésite pas à me contacter. Bon code !",[12,4815,1818],{"id":1817},[36,4817,4818,4824,4831,4838,4844,4849,4856,4863],{},[39,4819,4820],{},[1241,4821,4823],{"href":1972,"rel":4822},[1245],"Documentation MongoDB Replica Set",[39,4825,4826],{},[1241,4827,4830],{"href":4828,"rel":4829},"https:\u002F\u002Fdocs.docker.com\u002F",[1245],"Documentation Docker",[39,4832,4833],{},[1241,4834,4837],{"href":4835,"rel":4836},"https:\u002F\u002Fdocs.docker.com\u002Fcompose\u002F",[1245],"Documentation Docker Compose",[39,4839,4840],{},[1241,4841,4843],{"href":1993,"rel":4842},[1245],"Opérations CRUD MongoDB",[39,4845,4846],{},[1241,4847,1841],{"href":1839,"rel":4848},[1245],[39,4850,4851],{},[1241,4852,4855],{"href":4853,"rel":4854},"https:\u002F\u002Fdocs.docker.com\u002Fnetwork\u002F",[1245],"Réseaux Docker",[39,4857,4858],{},[1241,4859,4862],{"href":4860,"rel":4861},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Freference\u002Fbuilder\u002F#healthcheck",[1245],"Healthchecks Docker",[39,4864,4865],{},[1241,4866,4869],{"href":4867,"rel":4868},"https:\u002F\u002Fgithub.com\u002Fmongo-express\u002Fmongo-express",[1245],"Mongo-Express",[1255,4871,4872],{},"html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":73,"searchDepth":196,"depth":196,"links":4874},[4875,4876,4879,4887,4888,4889],{"id":14,"depth":196,"text":15},{"id":1915,"depth":196,"text":1916,"children":4877},[4878],{"id":1941,"depth":209,"text":1942},{"id":1977,"depth":196,"text":1905,"children":4880},[4881,4882,4883,4884,4885,4886],{"id":1386,"depth":209,"text":1387},{"id":1998,"depth":209,"text":1999},{"id":3000,"depth":209,"text":3001},{"id":3073,"depth":209,"text":3074},{"id":3929,"depth":209,"text":3930},{"id":3961,"depth":209,"text":3962},{"id":4564,"depth":196,"text":4565},{"id":1799,"depth":196,"text":1800},{"id":1817,"depth":196,"text":1818},"2024-07-17","Comment mettre en place un cluster MongoDB replica set à 3 nœuds avec Docker Compose. Apprends ce qu'est un replica set MongoDB, les prérequis, et les étapes pour créer un fichier Docker Compose qui le configure.","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fmongodb-replicaset",{},"\u002Fdevops\u002Fset-up-mongodb-replica-with-docker.fr",{"title":1905,"description":4891},"set-up-mongodb-replica-set-with-docker","devops\u002F2.set-up-mongodb-replica-with-docker.fr",[1894,4899,1895,1896],"Replica-Set",[1900,1409,1901],"r1HzjCuyw8LU_2eGvvEEWx4X9rZ6d5Q-nBF25WRs3-I",{"id":4903,"title":4904,"body":4905,"date":6658,"description":6659,"extension":1275,"img":6660,"meta":6661,"navigation":1096,"path":6662,"seo":6663,"slug":6664,"stem":6665,"tags":6666,"topics":6672,"__hash__":6674},"content\u002Fdevops\u002F1.deploy-expressjs-api-using-docker.fr.md","Déployer une API Express.js avec Docker",{"type":9,"value":4906,"toc":6636},[4907,4909,4912,4921,4925,4932,4934,4940,4944,4952,4956,4976,4980,5411,5414,5434,5436,5442,5446,5457,5461,5464,5484,5490,5494,5598,5601,5702,5706,5709,5728,5736,5739,6105,6109,6115,6118,6121,6124,6128,6137,6265,6282,6285,6288,6293,6297,6614,6625,6627,6630,6633],[12,4908,15],{"id":14},[17,4910,4911],{},"Dans le paysage technologique d'aujourd'hui, livrer des applications rapidement et de manière\nfiable est critique. Docker, avec sa technologie de conteneurisation, propose un moyen simple de\ndéployer des applications Node.js. Cet article discute du déploiement d'une API Express.js avec\nMongoDB, en se concentrant sur les bonnes pratiques des conteneurs Docker et les méthodologies\nde test. On passe sur les bases de la construction d'une application Express.js et de la\nconnexion à MongoDB. On se concentre plutôt sur l'utilisation des capacités de Docker, le suivi\ndes bonnes pratiques MongoDB, la création de tests robustes avec Jest, et l'utilisation de Nginx\nen tant que reverse proxy.",[17,4913,4914,4915,4920],{},"L'ensemble du code, ",[1241,4916,4919],{"href":4917,"target":4918},"https:\u002F\u002Fgithub.com\u002Fdenisakp\u002Fexpressjs-mongo-docker","_blank","dépôt sur GitHub",",\nsert d'illustration pratique des concepts et pratiques couverts dans ce guide.",[12,4922,4924],{"id":4923},"expressjs","Express.js",[17,4926,4927,4928,370],{},"Express.js est un framework Node.js populaire qui accélère et facilite le développement\nd'applications web et d'APIs. Il fournit un ensemble puissant de fonctionnalités qui\nsimplifient le process de création de serveur, permettant aux développeurs de mettre en place\ndes routes complexes, des middlewares et des fonctionnalités côté serveur avec moins de code.\nGrâce à sa large popularité, Express.js est soutenu par une grosse communauté qui propose un\nensemble de middlewares étendant ses capacités, ce qui en fait un choix populaire pour\nconstruire des applications web performantes. Si Express.js ne t'es pas familier, consulte\nla ",[1241,4929,4931],{"href":4930,"target":4918},"https:\u002F\u002Fexpressjs.com\u002F","documentation",[12,4933,1894],{"id":1901},[17,4935,4936,4937,370],{},"MongoDB est une base NoSQL conçue pour un développement et une mise à l'échelle simples. Elle\nest connue comme une base de données orientée document et stocke les données dans des documents\nflexibles type JSON, ce qui signifie que les champs peuvent différer d'un document à l'autre et\nque les structures de données peuvent évoluer dans le temps. Cette architecture permet de\ndéfinir facilement des relations hiérarchiques, de stocker des tableaux et de créer d'autres\nstructures plus complexes. MongoDB est connue pour son agilité, sa haute disponibilité et son\norientation sécurité, ce qui en fait un choix populaire pour les applications web modernes qui\nexigent un accès rapide à de grandes quantités de données. Si tu débutes avec MongoDB, consulte\nla ",[1241,4938,4931],{"href":4939,"target":4918},"https:\u002F\u002Fwww.mongodb.com\u002Fdocs\u002F",[12,4941,4943],{"id":4942},"pattern-singleton","Pattern Singleton",[17,4945,4946,4947,4951],{},"Le pattern Singleton est un design pattern logiciel qui garantit qu'une classe n'a qu'une seule\ninstance tout en fournissant un point d'accès global à celle-ci. Selon\n",[1241,4948,4950],{"href":4949,"target":4918},"https:\u002F\u002Frefactoring.guru\u002Fdesign-patterns\u002Fsingleton","Refactoring Guru",",\nle pattern Singleton est fréquemment utilisé pour gérer les connexions à la base de données\npuisqu'il permet d'instancier une classe une seule fois. C'est particulièrement bénéfique\nquand une ressource partagée unique, comme une connexion DB, est nécessaire pour réaliser des\nopérations à travers plusieurs composants d'une application. En utilisant le pattern Singleton,\non évite de créer par erreur plusieurs instances d'une classe, ce qui économise des ressources\net garantit un comportement cohérent dans tout le programme.",[151,4953,4955],{"id":4954},"bénéfices-du-singleton-pour-le-client-mongodb","Bénéfices du Singleton pour le client MongoDB",[36,4957,4958,4964,4970],{},[39,4959,4960,4963],{},[580,4961,4962],{},"Connexion cohérente"," : maintenir une seule connexion MongoDB évite le surcoût d'ouvrir et\nfermer plusieurs connexions, ce qui améliore les performances.",[39,4965,4966,4969],{},[580,4967,4968],{},"Optimisation des ressources"," : le Singleton garantit un usage optimal des ressources, en\nprévenant les pièges des connexions redondantes.",[39,4971,4972,4975],{},[580,4973,4974],{},"Éviter les fuites de connexion"," : tu peux éviter les fuites de connexion potentielles\ncausées par des composants distincts d'une application qui géreraient mal des instances de\nconnexion individuelles. Une fuite de connexion peut épuiser les ressources du serveur de\nbase de données, et entraîner une dégradation des performances.",[151,4977,4979],{"id":4978},"implémenter-le-pattern-singleton-pour-le-client-mongodb","Implémenter le pattern Singleton pour le client MongoDB",[65,4981,4984],{"className":3093,"code":4982,"filename":4983,"language":3096,"meta":73,"style":73},"\u002F\u002F this snippet is based on official mongodb npm module, not mongoosejs module. \nclass MyDatabase {\n  static client;\n  static db;\n\n  \u002F**\n   * @static\n   * @return {Promise\u003CMongoClient>}\n   *\u002F\n  static async connect() {\n    if (!this.client) {\n      try {\n        this.client = new MongoClient(\"mongodb:\u002F\u002Flocalhost:27017\u002F\", { serverApi: ServerApiVersion.v1 });\n        await this.client.connect();\n        console.info('connected to database'); \u002F\u002F we may use proper logging system instead of console.log\n      } catch (error) {\n        console.error(\"failed to connect to MongoDB: \", error.message);\n        throw error\n      }\n    }\n    return this.client;\n  }\n\n  \u002F**\n   * Retrieves the MongoDB database instance.\n   * @static\n   * @returns {Promise\u003CDb>}\n   *\u002F\n  static async getDB() {\n    if (!this.db) {\n      const client = await this.connect();\n      this.db = client.db(\"express\"); \u002F\u002F express is our database name\n    }\n    return this.db\n  }\n}\n","database.config.mjs",[24,4985,4986,4991,5001,5011,5019,5023,5028,5040,5058,5063,5077,5093,5100,5147,5166,5192,5208,5238,5246,5251,5255,5266,5270,5274,5278,5283,5291,5307,5311,5324,5339,5359,5390,5394,5403,5407],{"__ignoreMap":73},[174,4987,4988],{"class":176,"line":177},[174,4989,4990],{"class":1419},"\u002F\u002F this snippet is based on official mongodb npm module, not mongoosejs module. \n",[174,4992,4993,4996,4999],{"class":176,"line":196},[174,4994,4995],{"class":184},"class",[174,4997,4998],{"class":188}," MyDatabase",[174,5000,193],{"class":192},[174,5002,5003,5006,5009],{"class":176,"line":209},[174,5004,5005],{"class":184},"  static",[174,5007,5008],{"class":199}," client",[174,5010,3128],{"class":192},[174,5012,5013,5015,5017],{"class":176,"line":220},[174,5014,5005],{"class":184},[174,5016,3108],{"class":199},[174,5018,3128],{"class":192},[174,5020,5021],{"class":176,"line":230},[174,5022,1097],{"emptyLinePlaceholder":1096},[174,5024,5025],{"class":176,"line":240},[174,5026,5027],{"class":1419},"  \u002F**\n",[174,5029,5030,5033,5036],{"class":176,"line":250},[174,5031,5032],{"class":1419},"   * ",[174,5034,5035],{"class":180},"@",[174,5037,5039],{"class":5038},"s6hCs","static\n",[174,5041,5042,5044,5046,5049,5052,5056],{"class":176,"line":260},[174,5043,5032],{"class":1419},[174,5045,5035],{"class":180},[174,5047,5048],{"class":5038},"return",[174,5050,5051],{"class":180}," {",[174,5053,5055],{"class":5054},"sFweD","Promise\u003CMongoClient>",[174,5057,302],{"class":180},[174,5059,5060],{"class":176,"line":270},[174,5061,5062],{"class":1419},"   *\u002F\n",[174,5064,5065,5067,5070,5073,5075],{"class":176,"line":280},[174,5066,5005],{"class":184},[174,5068,5069],{"class":184}," async",[174,5071,5072],{"class":199}," connect",[174,5074,3327],{"class":192},[174,5076,193],{"class":192},[174,5078,5079,5081,5083,5086,5089,5091],{"class":176,"line":290},[174,5080,3334],{"class":180},[174,5082,365],{"class":199},[174,5084,5085],{"class":192},"!this.",[174,5087,5088],{"class":350},"client",[174,5090,376],{"class":199},[174,5092,469],{"class":192},[174,5094,5095,5098],{"class":176,"line":3},[174,5096,5097],{"class":180},"      try",[174,5099,193],{"class":192},[174,5101,5102,5105,5107,5109,5112,5115,5117,5119,5122,5124,5126,5128,5131,5133,5136,5138,5141,5143,5145],{"class":176,"line":554},[174,5103,5104],{"class":192},"        this.",[174,5106,5088],{"class":350},[174,5108,354],{"class":192},[174,5110,5111],{"class":192}," new",[174,5113,5114],{"class":322}," MongoClient",[174,5116,326],{"class":199},[174,5118,2188],{"class":192},[174,5120,5121],{"class":1412},"mongodb:\u002F\u002Flocalhost:27017\u002F",[174,5123,2188],{"class":192},[174,5125,1050],{"class":192},[174,5127,5051],{"class":192},[174,5129,5130],{"class":199}," serverApi",[174,5132,203],{"class":192},[174,5134,5135],{"class":350}," ServerApiVersion",[174,5137,370],{"class":192},[174,5139,5140],{"class":350},"v1",[174,5142,1172],{"class":192},[174,5144,512],{"class":199},[174,5146,3128],{"class":192},[174,5148,5149,5152,5155,5157,5159,5162,5164],{"class":176,"line":562},[174,5150,5151],{"class":180},"        await",[174,5153,5154],{"class":192}," this.",[174,5156,5088],{"class":350},[174,5158,370],{"class":192},[174,5160,5161],{"class":322},"connect",[174,5163,3327],{"class":199},[174,5165,3128],{"class":192},[174,5167,5168,5171,5173,5176,5178,5180,5183,5185,5187,5189],{"class":176,"line":2167},[174,5169,5170],{"class":350},"        console",[174,5172,370],{"class":192},[174,5174,5175],{"class":322},"info",[174,5177,326],{"class":199},[174,5179,3118],{"class":192},[174,5181,5182],{"class":1412},"connected to database",[174,5184,3118],{"class":192},[174,5186,512],{"class":199},[174,5188,749],{"class":192},[174,5190,5191],{"class":1419}," \u002F\u002F we may use proper logging system instead of console.log\n",[174,5193,5194,5197,5199,5201,5204,5206],{"class":176,"line":2177},[174,5195,5196],{"class":192},"      }",[174,5198,3394],{"class":180},[174,5200,365],{"class":199},[174,5202,5203],{"class":350},"error",[174,5205,376],{"class":199},[174,5207,469],{"class":192},[174,5209,5210,5212,5214,5216,5218,5220,5223,5225,5227,5230,5232,5234,5236],{"class":176,"line":2209},[174,5211,5170],{"class":350},[174,5213,370],{"class":192},[174,5215,5203],{"class":322},[174,5217,326],{"class":199},[174,5219,2188],{"class":192},[174,5221,5222],{"class":1412},"failed to connect to MongoDB: ",[174,5224,2188],{"class":192},[174,5226,1050],{"class":192},[174,5228,5229],{"class":350}," error",[174,5231,370],{"class":192},[174,5233,3550],{"class":350},[174,5235,512],{"class":199},[174,5237,3128],{"class":192},[174,5239,5240,5243],{"class":176,"line":2217},[174,5241,5242],{"class":180},"        throw",[174,5244,5245],{"class":350}," error\n",[174,5247,5248],{"class":176,"line":2226},[174,5249,5250],{"class":192},"      }\n",[174,5252,5253],{"class":176,"line":2234},[174,5254,3386],{"class":192},[174,5256,5257,5260,5262,5264],{"class":176,"line":2246},[174,5258,5259],{"class":180},"    return",[174,5261,5154],{"class":192},[174,5263,5088],{"class":350},[174,5265,3128],{"class":192},[174,5267,5268],{"class":176,"line":2258},[174,5269,551],{"class":192},[174,5271,5272],{"class":176,"line":2266},[174,5273,1097],{"emptyLinePlaceholder":1096},[174,5275,5276],{"class":176,"line":2274},[174,5277,5027],{"class":1419},[174,5279,5280],{"class":176,"line":2282},[174,5281,5282],{"class":1419},"   * Retrieves the MongoDB database instance.\n",[174,5284,5285,5287,5289],{"class":176,"line":2297},[174,5286,5032],{"class":1419},[174,5288,5035],{"class":180},[174,5290,5039],{"class":5038},[174,5292,5293,5295,5297,5300,5302,5305],{"class":176,"line":2312},[174,5294,5032],{"class":1419},[174,5296,5035],{"class":180},[174,5298,5299],{"class":5038},"returns",[174,5301,5051],{"class":180},[174,5303,5304],{"class":5054},"Promise\u003CDb>",[174,5306,302],{"class":180},[174,5308,5309],{"class":176,"line":2327},[174,5310,5062],{"class":1419},[174,5312,5313,5315,5317,5320,5322],{"class":176,"line":2335},[174,5314,5005],{"class":184},[174,5316,5069],{"class":184},[174,5318,5319],{"class":199}," getDB",[174,5321,3327],{"class":192},[174,5323,193],{"class":192},[174,5325,5326,5328,5330,5332,5335,5337],{"class":176,"line":2390},[174,5327,3334],{"class":180},[174,5329,365],{"class":199},[174,5331,5085],{"class":192},[174,5333,5334],{"class":350},"db",[174,5336,376],{"class":199},[174,5338,469],{"class":192},[174,5340,5341,5344,5346,5348,5351,5353,5355,5357],{"class":176,"line":2401},[174,5342,5343],{"class":184},"      const",[174,5345,5008],{"class":350},[174,5347,354],{"class":192},[174,5349,5350],{"class":180}," await",[174,5352,5154],{"class":192},[174,5354,5161],{"class":322},[174,5356,3327],{"class":199},[174,5358,3128],{"class":192},[174,5360,5361,5364,5366,5368,5370,5372,5374,5376,5378,5381,5383,5385,5387],{"class":176,"line":2412},[174,5362,5363],{"class":192},"      this.",[174,5365,5334],{"class":350},[174,5367,354],{"class":192},[174,5369,5008],{"class":350},[174,5371,370],{"class":192},[174,5373,5334],{"class":322},[174,5375,326],{"class":199},[174,5377,2188],{"class":192},[174,5379,5380],{"class":1412},"express",[174,5382,2188],{"class":192},[174,5384,512],{"class":199},[174,5386,749],{"class":192},[174,5388,5389],{"class":1419}," \u002F\u002F express is our database name\n",[174,5391,5392],{"class":176,"line":2423},[174,5393,3386],{"class":192},[174,5395,5396,5398,5400],{"class":176,"line":2434},[174,5397,5259],{"class":180},[174,5399,5154],{"class":192},[174,5401,5402],{"class":350},"db\n",[174,5404,5405],{"class":176,"line":2442},[174,5406,551],{"class":192},[174,5408,5409],{"class":176,"line":2452},[174,5410,302],{"class":192},[17,5412,5413],{},"Ce code garantit que partout dans ton application, tu travailles avec la même instance du\nclient MongoDB quand tu en as besoin.",[36,5415,5416,5422,5428],{},[39,5417,5418,5421],{},[580,5419,5420],{},"Propriétés statiques"," : pour stocker les instances uniques de MongoClient et de la base\n(Db).",[39,5423,5424,5427],{},[580,5425,5426],{},"Méthode Connect"," : établit une connexion à MongoDB, en s'assurant qu'une seule instance\nMongoClient est active.",[39,5429,5430,5433],{},[580,5431,5432],{},"Méthode GetDB"," : pour récupérer la base tout en maintenant le pattern Singleton.",[12,5435,1895],{"id":1409},[17,5437,5438,5441],{},[1241,5439,1895],{"href":5440,"target":4918},"https:\u002F\u002Fdocs.docker.com\u002Fget-started\u002Foverview\u002F"," est une\nplateforme puissante qui facilite la création, le déploiement et l'exploitation d'applications\nvia des conteneurs. Les conteneurs permettent aux développeurs d'empaqueter un programme avec\ntous ses éléments — bibliothèques et autres dépendances — et de tout livrer comme un seul\npaquet.",[151,5443,5445],{"id":5444},"dockerfile","Dockerfile",[17,5447,5448,5449,5452,5453,370],{},"Un Dockerfile est un document texte qui contient toutes les commandes nécessaires qu'un\nutilisateur peut utiliser sur la ligne de commande pour construire une image. Il sert de\nmodèle pour créer des images Docker. Le Dockerfile inclut des directives comme ",[24,5450,5451],{},"FROM"," pour\ncréer une nouvelle étape de build à partir d'une image de base. On trouve de nombreux exemples\nsur la ",[1241,5454,5456],{"href":5455,"target":4918},"https:\u002F\u002Fdocs.docker.com\u002Freference\u002Fdockerfile\u002F","page de\ndocumentation Docker",[151,5458,5460],{"id":5459},"couches-multistage","Couches multistage",[17,5462,5463],{},"Le build multistage Docker est une fonctionnalité qui permet de construire une image en\nplusieurs étapes en utilisant un seul Dockerfile. Chaque étape peut utiliser une image de base\ndifférente et s'appuyer sur le travail des étapes précédentes, en ne sélectionnant que les\nartefacts nécessaires pour l'étape suivante. C'est très bénéfique pour optimiser les\nDockerfiles, les rendant plus efficaces et plus faciles à gérer. Voici quelques bénéfices de\nl'usage du multistage :",[36,5465,5466,5472,5478],{},[39,5467,5468,5471],{},[580,5469,5470],{},"Tailles d'image réduites"," : utiliser les builds multistage permet de réduire\nconsidérablement la taille de l'image finale. Les fichiers et dépendances inutiles des étapes\nprécédentes ne sont pas requis dans l'image finale, ce qui donne un artefact de déploiement\nplus petit.",[39,5473,5474,5477],{},[580,5475,5476],{},"Améliorations de sécurité"," : comme l'image finale ne contient que le nécessaire, elle\nréduit la surface d'attaque, ce qui améliore la sécurité. Moins de dépendances à l'exécution\nsignifie moins d'opportunités de vulnérabilités.",[39,5479,5480,5483],{},[580,5481,5482],{},"Temps de build plus rapides"," : en divisant les Dockerfiles en plusieurs étapes, tu peux\nmettre en cache et réutiliser les étapes précédentes sans reconstruire l'image complète. Ça\naccélère le process de build, particulièrement pendant le développement et les tests.",[17,5485,5486,5487,370],{},"Tu peux en apprendre plus sur le multistage Docker via la\n",[1241,5488,4931],{"href":5489,"target":4918},"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fmulti-stage\u002F",[151,5491,5493],{"id":5492},"notre-dockerfile-dapi-expressjs","Notre Dockerfile d'API Express.js",[65,5495,5499],{"className":5496,"code":5497,"filename":5498,"language":1409,"meta":73,"style":73},"language-docker shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","FROM node:lts-alpine AS base\nRUN apk add --no-cache libc6-compat dumb-init\nWORKDIR \u002Fapp\n\nFROM base AS dependencies\nCOPY package*.json .\nRUN npm install\n\nFROM dependenices AS prune\nRUN npm prune --omit=dev\n\nFROM base AS production\nCOPY --from=prune \u002Fapp\u002Fnode_modules .\nCOPY . .\nRUN addgroup -S vegeta && adduser -S vegeta -G vegeta\nUSER vegeta\nENV NODE_ENV production\nEXPOSE 3000\nENTRYPOINT [\"dumb-init\", \"--\"]\nCMD [\"node\", \"src\u002Fserver.mjs\"]\n","Dokerfile",[24,5500,5501,5506,5511,5516,5520,5525,5530,5535,5539,5544,5549,5553,5558,5563,5568,5573,5578,5583,5588,5593],{"__ignoreMap":73},[174,5502,5503],{"class":176,"line":177},[174,5504,5505],{},"FROM node:lts-alpine AS base\n",[174,5507,5508],{"class":176,"line":196},[174,5509,5510],{},"RUN apk add --no-cache libc6-compat dumb-init\n",[174,5512,5513],{"class":176,"line":209},[174,5514,5515],{},"WORKDIR \u002Fapp\n",[174,5517,5518],{"class":176,"line":220},[174,5519,1097],{"emptyLinePlaceholder":1096},[174,5521,5522],{"class":176,"line":230},[174,5523,5524],{},"FROM base AS dependencies\n",[174,5526,5527],{"class":176,"line":240},[174,5528,5529],{},"COPY package*.json .\n",[174,5531,5532],{"class":176,"line":250},[174,5533,5534],{},"RUN npm install\n",[174,5536,5537],{"class":176,"line":260},[174,5538,1097],{"emptyLinePlaceholder":1096},[174,5540,5541],{"class":176,"line":270},[174,5542,5543],{},"FROM dependenices AS prune\n",[174,5545,5546],{"class":176,"line":280},[174,5547,5548],{},"RUN npm prune --omit=dev\n",[174,5550,5551],{"class":176,"line":290},[174,5552,1097],{"emptyLinePlaceholder":1096},[174,5554,5555],{"class":176,"line":3},[174,5556,5557],{},"FROM base AS production\n",[174,5559,5560],{"class":176,"line":554},[174,5561,5562],{},"COPY --from=prune \u002Fapp\u002Fnode_modules .\n",[174,5564,5565],{"class":176,"line":562},[174,5566,5567],{},"COPY . .\n",[174,5569,5570],{"class":176,"line":2167},[174,5571,5572],{},"RUN addgroup -S vegeta && adduser -S vegeta -G vegeta\n",[174,5574,5575],{"class":176,"line":2177},[174,5576,5577],{},"USER vegeta\n",[174,5579,5580],{"class":176,"line":2209},[174,5581,5582],{},"ENV NODE_ENV production\n",[174,5584,5585],{"class":176,"line":2217},[174,5586,5587],{},"EXPOSE 3000\n",[174,5589,5590],{"class":176,"line":2226},[174,5591,5592],{},"ENTRYPOINT [\"dumb-init\", \"--\"]\n",[174,5594,5595],{"class":176,"line":2234},[174,5596,5597],{},"CMD [\"node\", \"src\u002Fserver.mjs\"]\n",[17,5599,5600],{},"Voici un découpage du Dockerfile fourni :",[101,5602,5603,5635,5654,5667],{},[39,5604,5605,5608],{},[580,5606,5607],{},"Étape base",[36,5609,5610,5617,5628],{},[39,5611,5612,5613,5616],{},"Image de base : ",[24,5614,5615],{},"node:lts-alpine"," pour un environnement Node.js léger.",[39,5618,5619,5620,5623,5624,5627],{},"Packages essentiels : installe ",[24,5621,5622],{},"libc6-compat"," pour la compatibilité et ",[24,5625,5626],{},"dumb-init"," pour\nla gestion de process.",[39,5629,5630,5631,5634],{},"Répertoire de travail : définit ",[24,5632,5633],{},"\u002Fapp"," comme répertoire de travail pour les instructions\nsuivantes.",[39,5636,5637,5640],{},[580,5638,5639],{},"Étape dependencies",[36,5641,5642,5651],{},[39,5643,5644,5645,2019,5648,370],{},"Copie ",[24,5646,5647],{},"package.json",[24,5649,5650],{},"package-lock.json",[39,5652,5653],{},"Installe uniquement les dépendances de production.",[39,5655,5656,5659],{},[580,5657,5658],{},"Étape prune",[36,5660,5661],{},[39,5662,5663,5664,370],{},"Supprime les fichiers superflus et les dépendances de développement pour réduire\ndavantage la taille de ",[24,5665,5666],{},"node_modules",[39,5668,5669,5672],{},[580,5670,5671],{},"Étape production",[36,5673,5674,5677,5683,5686,5696],{},[39,5675,5676],{},"Réutilisation de l'image de base : repart de l'étape base pour garantir un environnement\npropre.",[39,5678,5679,5680,5682],{},"Modules et code : ajoute les ",[24,5681,5666],{}," élagués et copie le code source de\nl'application dans le conteneur.",[39,5684,5685],{},"Sécurité : configure un utilisateur non-root pour renforcer la sécurité, en évitant de\nlancer le conteneur avec les privilèges root.",[39,5687,5688,5689,4754,5692,5695],{},"Configuration : définit ",[24,5690,5691],{},"NODE_ENV",[24,5693,5694],{},"production"," pour optimiser l'environnement Node.js\npour la production.",[39,5697,5698,5699,5701],{},"Setup runtime : configure ",[24,5700,5626],{}," comme point d'entrée pour gérer le process\nprincipal, garantissant un démarrage et un arrêt propres.",[12,5703,5705],{"id":5704},"tests-e2e-avec-jest","Tests E2E avec Jest",[17,5707,5708],{},"Les tests E2E (end-to-end) évaluent le workflow de l'application du début à la fin. Ce type de\ntest valide l'intégration du système avec d'autres interfaces, évalue ses dépendances à\nd'autres environnements, et garantit que toutes les pièces du système fonctionnent ensemble\ncomme prévu dans des situations changeantes.",[17,5710,5711,5712,2910,5716,2910,5720,2910,5724,370],{},"Les tests E2E peuvent être réalisés sur une API Express.js avec divers outils. Ces outils\npermettent de répliquer un usage réel en envoyant des requêtes HTTP à l'API et en vérifiant\nles réponses, garantissant que l'ensemble du système fonctionne correctement. Les outils\ncouramment utilisés pour les tests E2E d'applications Express.js incluent\n",[1241,5713,5715],{"href":5714,"target":4918},"https:\u002F\u002Flearning.postman.com\u002Fdocs\u002Fintroduction\u002Foverview\u002F","Postman",[1241,5717,5719],{"href":5718,"target":4918},"https:\u002F\u002Fjestjs.io\u002F","JestJS",[1241,5721,5723],{"href":5722,"target":4918},"https:\u002F\u002Fmochajs.org\u002F","MochaJS",[1241,5725,5727],{"href":5726,"target":4918},"https:\u002F\u002Fdocs.cypress.io\u002Fguides\u002Foverview\u002Fwhy-cypress","Cypress",[17,5729,5730,5731,5735],{},"Pour ce guide, on utilise JestJS, connu comme un framework de tests unitaires pour JavaScript.\nOn le combine avec ",[1241,5732,5734],{"href":5733,"target":4918},"https:\u002F\u002Fgithub.com\u002Fladjs\u002Fsupertest","SuperTest","\npour gérer nos tests E2E, puisqu'il inclut une syntaxe claire pour écrire des tests et un\nsupport correct de la gestion asynchrone.",[17,5737,5738],{},"Voici un cas de test qui s'assure que notre serveur est en marche et fonctionnel.",[65,5740,5743],{"className":3093,"code":5741,"filename":5742,"language":3096,"meta":73,"style":73},"import supertest from 'supertest';\nimport server from 'src\u002Fserver.mjs';\n\ndescribe('Healthcheck E2E tests', () => {\n  afterAll(async () => {\n    await server.close(); \u002F\u002F Ensure the server is closed after tests\n  });\n\n  it('\u002FGET \u002Fapi\u002Fhealthcheck', async () => {\n    const response = await supertest(server).get('\u002Fapi\u002Fhealthcheck');\n    expect(response.statusCode).toBe(200);\n    expect(response.body).toHaveProperty(\"uptime\");\n    expect(typeof response.body.uptime).toBe('number');\n    expect(response.body.uptime).toBeGreaterThan(0);\n    expect(response.body).toHaveProperty(\"mongo\", true);\n  });\n});\n","healthcheck.test.mjs",[24,5744,5745,5765,5783,5787,5810,5826,5846,5854,5858,5882,5922,5953,5986,6024,6055,6089,6097],{"__ignoreMap":73},[174,5746,5747,5750,5753,5756,5758,5761,5763],{"class":176,"line":177},[174,5748,5749],{"class":180},"import",[174,5751,5752],{"class":350}," supertest ",[174,5754,5755],{"class":180},"from",[174,5757,2158],{"class":192},[174,5759,5760],{"class":1412},"supertest",[174,5762,3118],{"class":192},[174,5764,3128],{"class":192},[174,5766,5767,5769,5772,5774,5776,5779,5781],{"class":176,"line":196},[174,5768,5749],{"class":180},[174,5770,5771],{"class":350}," server ",[174,5773,5755],{"class":180},[174,5775,2158],{"class":192},[174,5777,5778],{"class":1412},"src\u002Fserver.mjs",[174,5780,3118],{"class":192},[174,5782,3128],{"class":192},[174,5784,5785],{"class":176,"line":209},[174,5786,1097],{"emptyLinePlaceholder":1096},[174,5788,5789,5792,5794,5796,5799,5801,5803,5806,5808],{"class":176,"line":220},[174,5790,5791],{"class":322},"describe",[174,5793,326],{"class":350},[174,5795,3118],{"class":192},[174,5797,5798],{"class":1412},"Healthcheck E2E tests",[174,5800,3118],{"class":192},[174,5802,1050],{"class":192},[174,5804,5805],{"class":192}," ()",[174,5807,515],{"class":184},[174,5809,193],{"class":192},[174,5811,5812,5815,5817,5820,5822,5824],{"class":176,"line":230},[174,5813,5814],{"class":322},"  afterAll",[174,5816,326],{"class":199},[174,5818,5819],{"class":184},"async",[174,5821,5805],{"class":192},[174,5823,515],{"class":184},[174,5825,193],{"class":192},[174,5827,5828,5831,5834,5836,5839,5841,5843],{"class":176,"line":240},[174,5829,5830],{"class":180},"    await",[174,5832,5833],{"class":350}," server",[174,5835,370],{"class":192},[174,5837,5838],{"class":322},"close",[174,5840,3327],{"class":199},[174,5842,749],{"class":192},[174,5844,5845],{"class":1419}," \u002F\u002F Ensure the server is closed after tests\n",[174,5847,5848,5850,5852],{"class":176,"line":250},[174,5849,3391],{"class":192},[174,5851,512],{"class":199},[174,5853,3128],{"class":192},[174,5855,5856],{"class":176,"line":260},[174,5857,1097],{"emptyLinePlaceholder":1096},[174,5859,5860,5863,5865,5867,5870,5872,5874,5876,5878,5880],{"class":176,"line":270},[174,5861,5862],{"class":322},"  it",[174,5864,326],{"class":199},[174,5866,3118],{"class":192},[174,5868,5869],{"class":1412},"\u002FGET \u002Fapi\u002Fhealthcheck",[174,5871,3118],{"class":192},[174,5873,1050],{"class":192},[174,5875,5069],{"class":184},[174,5877,5805],{"class":192},[174,5879,515],{"class":184},[174,5881,193],{"class":192},[174,5883,5884,5887,5890,5892,5894,5897,5899,5902,5904,5906,5909,5911,5913,5916,5918,5920],{"class":176,"line":280},[174,5885,5886],{"class":184},"    const",[174,5888,5889],{"class":350}," response",[174,5891,354],{"class":192},[174,5893,5350],{"class":180},[174,5895,5896],{"class":322}," supertest",[174,5898,326],{"class":199},[174,5900,5901],{"class":350},"server",[174,5903,512],{"class":199},[174,5905,370],{"class":192},[174,5907,5908],{"class":322},"get",[174,5910,326],{"class":199},[174,5912,3118],{"class":192},[174,5914,5915],{"class":1412},"\u002Fapi\u002Fhealthcheck",[174,5917,3118],{"class":192},[174,5919,512],{"class":199},[174,5921,3128],{"class":192},[174,5923,5924,5927,5929,5932,5934,5937,5939,5941,5944,5946,5949,5951],{"class":176,"line":290},[174,5925,5926],{"class":322},"    expect",[174,5928,326],{"class":199},[174,5930,5931],{"class":350},"response",[174,5933,370],{"class":192},[174,5935,5936],{"class":350},"statusCode",[174,5938,512],{"class":199},[174,5940,370],{"class":192},[174,5942,5943],{"class":322},"toBe",[174,5945,326],{"class":199},[174,5947,5948],{"class":649},"200",[174,5950,512],{"class":199},[174,5952,3128],{"class":192},[174,5954,5955,5957,5959,5961,5963,5966,5968,5970,5973,5975,5977,5980,5982,5984],{"class":176,"line":3},[174,5956,5926],{"class":322},[174,5958,326],{"class":199},[174,5960,5931],{"class":350},[174,5962,370],{"class":192},[174,5964,5965],{"class":350},"body",[174,5967,512],{"class":199},[174,5969,370],{"class":192},[174,5971,5972],{"class":322},"toHaveProperty",[174,5974,326],{"class":199},[174,5976,2188],{"class":192},[174,5978,5979],{"class":1412},"uptime",[174,5981,2188],{"class":192},[174,5983,512],{"class":199},[174,5985,3128],{"class":192},[174,5987,5988,5990,5992,5995,5997,5999,6001,6003,6005,6007,6009,6011,6013,6015,6018,6020,6022],{"class":176,"line":554},[174,5989,5926],{"class":322},[174,5991,326],{"class":199},[174,5993,5994],{"class":192},"typeof",[174,5996,5889],{"class":350},[174,5998,370],{"class":192},[174,6000,5965],{"class":350},[174,6002,370],{"class":192},[174,6004,5979],{"class":350},[174,6006,512],{"class":199},[174,6008,370],{"class":192},[174,6010,5943],{"class":322},[174,6012,326],{"class":199},[174,6014,3118],{"class":192},[174,6016,6017],{"class":1412},"number",[174,6019,3118],{"class":192},[174,6021,512],{"class":199},[174,6023,3128],{"class":192},[174,6025,6026,6028,6030,6032,6034,6036,6038,6040,6042,6044,6047,6049,6051,6053],{"class":176,"line":562},[174,6027,5926],{"class":322},[174,6029,326],{"class":199},[174,6031,5931],{"class":350},[174,6033,370],{"class":192},[174,6035,5965],{"class":350},[174,6037,370],{"class":192},[174,6039,5979],{"class":350},[174,6041,512],{"class":199},[174,6043,370],{"class":192},[174,6045,6046],{"class":322},"toBeGreaterThan",[174,6048,326],{"class":199},[174,6050,656],{"class":649},[174,6052,512],{"class":199},[174,6054,3128],{"class":192},[174,6056,6057,6059,6061,6063,6065,6067,6069,6071,6073,6075,6077,6079,6081,6083,6085,6087],{"class":176,"line":2167},[174,6058,5926],{"class":322},[174,6060,326],{"class":199},[174,6062,5931],{"class":350},[174,6064,370],{"class":192},[174,6066,5965],{"class":350},[174,6068,512],{"class":199},[174,6070,370],{"class":192},[174,6072,5972],{"class":322},[174,6074,326],{"class":199},[174,6076,2188],{"class":192},[174,6078,1521],{"class":1412},[174,6080,2188],{"class":192},[174,6082,1050],{"class":192},[174,6084,3361],{"class":930},[174,6086,512],{"class":199},[174,6088,3128],{"class":192},[174,6090,6091,6093,6095],{"class":176,"line":2177},[174,6092,3391],{"class":192},[174,6094,512],{"class":199},[174,6096,3128],{"class":192},[174,6098,6099,6101,6103],{"class":176,"line":2209},[174,6100,3519],{"class":192},[174,6102,512],{"class":350},[174,6104,3128],{"class":192},[12,6106,6108],{"id":6107},"nginx","Nginx",[17,6110,6111,6114],{},[1241,6112,6108],{"href":6113,"target":4918},"https:\u002F\u002Fnginx.org\u002Fen\u002Fdocs\u002F"," est un logiciel puissant et\nhautement performant qui agit comme serveur web et reverse proxy.",[17,6116,6117],{},"En tant que serveur web, Nginx peut gérer les requêtes HTTP\u002FHTTPS et fournir du contenu\nstatique rapidement en envoyant les fichiers depuis le disque vers le réseau. Il est très\nefficace pour servir du contenu statique comme des images, des fichiers JavaScript et CSS.",[17,6119,6120],{},"Nginx sert de reverse proxy, en dirigeant le trafic vers différents services backend selon\nles URLs ou en-têtes. Il gère également le load balancing entre plusieurs serveurs. C'est\nparticulièrement utile dans les architectures microservices, où plusieurs services gèrent\ndifférentes portions d'une application web.",[17,6122,6123],{},"Ainsi, utiliser Nginx comme reverse proxy pour une API Express.js fournit un moyen robuste,\nsécurisé et efficace de gérer les connexions clients, la sécurité et la livraison de contenu\nstatique — autant d'éléments qui contribuent à une architecture applicative plus scalable et\nmaintenable.",[151,6125,6127],{"id":6126},"mise-en-place-du-fichier-de-configuration-nginx","Mise en place du fichier de configuration Nginx",[17,6129,6130,6131,6134,6135,370],{},"Le fichier ",[24,6132,6133],{},"nginx.conf"," est le fichier de configuration principal de Nginx. Il spécifie\ncomment le serveur répond aux requêtes HTTP entrantes, gère de nombreux serveurs virtuels,\ntraite les paramètres SSL\u002FTLS, dirige les requêtes vers les apps backend, et plus encore.\nVoici la définition du bloc serveur de notre fichier ",[24,6136,6133],{},[65,6138,6142],{"className":6139,"code":6140,"filename":6141,"language":6107,"meta":73,"style":73},"language-nginx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","    server {\n        listen 80 default_server;\n        listen [::]:80 default_server;\n        server_name _;\n\n        # Security Headers\n        add_header X-Frame-Options \"SAMEORIGIN\";\n        add_header X-Content-Type-Options \"nosniff\";\n\n        # Proxy Pass to Node.js App\n        location \u002F {\n            proxy_pass http:\u002F\u002Fapp:3000; # app is the name of our app service name in docker\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection 'upgrade';\n            proxy_set_header Host $host;\n            proxy_cache_bypass $http_upgrade;\n            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n            proxy_set_header X-Forwarded-Proto $scheme;\n        }\n\n        # Error Handling\n        error_log \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log;\n        access_log \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log;\n    }\n","default.conf",[24,6143,6144,6149,6154,6159,6164,6168,6173,6178,6183,6187,6192,6197,6202,6207,6212,6217,6222,6227,6232,6237,6242,6246,6251,6256,6261],{"__ignoreMap":73},[174,6145,6146],{"class":176,"line":177},[174,6147,6148],{},"    server {\n",[174,6150,6151],{"class":176,"line":196},[174,6152,6153],{},"        listen 80 default_server;\n",[174,6155,6156],{"class":176,"line":209},[174,6157,6158],{},"        listen [::]:80 default_server;\n",[174,6160,6161],{"class":176,"line":220},[174,6162,6163],{},"        server_name _;\n",[174,6165,6166],{"class":176,"line":230},[174,6167,1097],{"emptyLinePlaceholder":1096},[174,6169,6170],{"class":176,"line":240},[174,6171,6172],{},"        # Security Headers\n",[174,6174,6175],{"class":176,"line":250},[174,6176,6177],{},"        add_header X-Frame-Options \"SAMEORIGIN\";\n",[174,6179,6180],{"class":176,"line":260},[174,6181,6182],{},"        add_header X-Content-Type-Options \"nosniff\";\n",[174,6184,6185],{"class":176,"line":270},[174,6186,1097],{"emptyLinePlaceholder":1096},[174,6188,6189],{"class":176,"line":280},[174,6190,6191],{},"        # Proxy Pass to Node.js App\n",[174,6193,6194],{"class":176,"line":290},[174,6195,6196],{},"        location \u002F {\n",[174,6198,6199],{"class":176,"line":3},[174,6200,6201],{},"            proxy_pass http:\u002F\u002Fapp:3000; # app is the name of our app service name in docker\n",[174,6203,6204],{"class":176,"line":554},[174,6205,6206],{},"            proxy_http_version 1.1;\n",[174,6208,6209],{"class":176,"line":562},[174,6210,6211],{},"            proxy_set_header Upgrade $http_upgrade;\n",[174,6213,6214],{"class":176,"line":2167},[174,6215,6216],{},"            proxy_set_header Connection 'upgrade';\n",[174,6218,6219],{"class":176,"line":2177},[174,6220,6221],{},"            proxy_set_header Host $host;\n",[174,6223,6224],{"class":176,"line":2209},[174,6225,6226],{},"            proxy_cache_bypass $http_upgrade;\n",[174,6228,6229],{"class":176,"line":2217},[174,6230,6231],{},"            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n",[174,6233,6234],{"class":176,"line":2226},[174,6235,6236],{},"            proxy_set_header X-Forwarded-Proto $scheme;\n",[174,6238,6239],{"class":176,"line":2234},[174,6240,6241],{},"        }\n",[174,6243,6244],{"class":176,"line":2246},[174,6245,1097],{"emptyLinePlaceholder":1096},[174,6247,6248],{"class":176,"line":2258},[174,6249,6250],{},"        # Error Handling\n",[174,6252,6253],{"class":176,"line":2266},[174,6254,6255],{},"        error_log \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log;\n",[174,6257,6258],{"class":176,"line":2274},[174,6259,6260],{},"        access_log \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log;\n",[174,6262,6263],{"class":176,"line":2282},[174,6264,3386],{},[17,6266,6267,6268,4754,6271,6274,6275,4754,6278,6281],{},"Cette configuration Nginx écoute le port 80 pour le trafic HTTP et supporte à la fois IPv4 et\nIPv6. Elle est configurée comme serveur catch-all par défaut, répondant à toute requête HTTP,\net inclut des en-têtes de sécurité importants : ",[24,6269,6270],{},"X-Frame-Options",[24,6272,6273],{},"SAMEORIGIN"," pour prévenir\nles attaques par clickjacking, et ",[24,6276,6277],{},"X-Content-Type-Options",[24,6279,6280],{},"nosniff"," pour empêcher le\nnavigateur de faire du MIME-sniffing sur des réponses qui ne correspondent pas au type de\ncontenu déclaré.",[17,6283,6284],{},"D'autres options de configuration incluent le cache de fichiers statiques (dossiers\nd'uploads), la configuration SSL\u002FTLS, et — pourquoi pas — des rate limits. On les couvrira\npeut-être dans une autre itération de ce projet.",[12,6286,6287],{"id":3943},"Docker Compose",[17,6289,6290,6292],{},[1241,6291,6287],{"href":4835,"target":4918}," est un outil\npour créer et gérer des applications Docker multi-conteneurs. Il permet de configurer les\nservices, réseaux et volumes de ton application à l'aide d'un fichier YAML. Puis, avec une\nseule commande, tu peux exécuter tous les services listés dans ta configuration.",[151,6294,6296],{"id":6295},"notre-docker-compose","Notre Docker Compose",[65,6298,6300],{"className":2046,"code":6299,"filename":2005,"language":2048,"meta":73,"style":73},"services:\n  mongo:\n    image: 'mongo:7.0-jammy'\n    networks:\n      - express\n    volumes:\n      - '$PWD\u002Fdocker\u002Fmongo\u002Finit.js:\u002Fdocker-entrypoint-initdb.d\u002Fmongo-init.js'\n      - 'mongo-data:\u002Fdata\u002Fdb'\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n    networks:\n      - express\n    environment:\n      MONGODB_HOST: 'mongodb:\u002F\u002Fexpress:password@mongo:27017\u002Fexpress'\n      NODE_ENV: development\n    volumes:\n      - 'app-data:\u002Fapp'\n    depends_on:\n      - mongo\n  nginx:\n    image: 'nginx:stable-alpine3.17-slim'\n    networks:\n      - express\n    ports:\n      - '${NGINX_PORT:-80}:80'\n    volumes:\n      - '$PWD\u002Fdocker\u002Fnginx\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf:ro'\n      - 'nginx-logs:\u002Fvar\u002Flogs\u002Fnginx'\n    depends_on:\n      - app\nnetworks:\n  express:\n    driver: bridge\nvolumes:\n  mongo-data:\n  app-data:\n  nginx-logs:\n\n",[24,6301,6302,6308,6315,6328,6334,6341,6347,6358,6369,6376,6383,6393,6403,6409,6415,6421,6435,6445,6451,6462,6468,6475,6482,6495,6501,6507,6514,6525,6531,6542,6553,6559,6566,6572,6579,6587,6593,6600,6607],{"__ignoreMap":73},[174,6303,6304,6306],{"class":176,"line":177},[174,6305,2129],{"class":199},[174,6307,2058],{"class":192},[174,6309,6310,6313],{"class":176,"line":196},[174,6311,6312],{"class":199},"  mongo",[174,6314,2058],{"class":192},[174,6316,6317,6319,6321,6323,6326],{"class":176,"line":209},[174,6318,2153],{"class":199},[174,6320,203],{"class":192},[174,6322,2158],{"class":192},[174,6324,6325],{"class":1412},"mongo:7.0-jammy",[174,6327,2164],{"class":192},[174,6329,6330,6332],{"class":176,"line":220},[174,6331,2261],{"class":199},[174,6333,2058],{"class":192},[174,6335,6336,6338],{"class":176,"line":230},[174,6337,2220],{"class":192},[174,6339,6340],{"class":1412}," express\n",[174,6342,6343,6345],{"class":176,"line":240},[174,6344,2212],{"class":199},[174,6346,2058],{"class":192},[174,6348,6349,6351,6353,6356],{"class":176,"line":250},[174,6350,2220],{"class":192},[174,6352,2158],{"class":192},[174,6354,6355],{"class":1412},"$PWD\u002Fdocker\u002Fmongo\u002Finit.js:\u002Fdocker-entrypoint-initdb.d\u002Fmongo-init.js",[174,6357,2164],{"class":192},[174,6359,6360,6362,6364,6367],{"class":176,"line":260},[174,6361,2220],{"class":192},[174,6363,2158],{"class":192},[174,6365,6366],{"class":1412},"mongo-data:\u002Fdata\u002Fdb",[174,6368,2164],{"class":192},[174,6370,6371,6374],{"class":176,"line":270},[174,6372,6373],{"class":199},"  app",[174,6375,2058],{"class":192},[174,6377,6378,6381],{"class":176,"line":280},[174,6379,6380],{"class":199},"    build",[174,6382,2058],{"class":192},[174,6384,6385,6388,6390],{"class":176,"line":290},[174,6386,6387],{"class":199},"      context",[174,6389,203],{"class":192},[174,6391,6392],{"class":649}," .\n",[174,6394,6395,6398,6400],{"class":176,"line":3},[174,6396,6397],{"class":199},"      dockerfile",[174,6399,203],{"class":192},[174,6401,6402],{"class":1412}," Dockerfile\n",[174,6404,6405,6407],{"class":176,"line":554},[174,6406,2261],{"class":199},[174,6408,2058],{"class":192},[174,6410,6411,6413],{"class":176,"line":562},[174,6412,2220],{"class":192},[174,6414,6340],{"class":1412},[174,6416,6417,6419],{"class":176,"line":2167},[174,6418,2277],{"class":199},[174,6420,2058],{"class":192},[174,6422,6423,6426,6428,6430,6433],{"class":176,"line":2177},[174,6424,6425],{"class":199},"      MONGODB_HOST",[174,6427,203],{"class":192},[174,6429,2158],{"class":192},[174,6431,6432],{"class":1412},"mongodb:\u002F\u002Fexpress:password@mongo:27017\u002Fexpress",[174,6434,2164],{"class":192},[174,6436,6437,6440,6442],{"class":176,"line":2209},[174,6438,6439],{"class":199},"      NODE_ENV",[174,6441,203],{"class":192},[174,6443,6444],{"class":1412}," development\n",[174,6446,6447,6449],{"class":176,"line":2217},[174,6448,2212],{"class":199},[174,6450,2058],{"class":192},[174,6452,6453,6455,6457,6460],{"class":176,"line":2226},[174,6454,2220],{"class":192},[174,6456,2158],{"class":192},[174,6458,6459],{"class":1412},"app-data:\u002Fapp",[174,6461,2164],{"class":192},[174,6463,6464,6466],{"class":176,"line":2234},[174,6465,2782],{"class":199},[174,6467,2058],{"class":192},[174,6469,6470,6472],{"class":176,"line":2246},[174,6471,2220],{"class":192},[174,6473,6474],{"class":1412}," mongo\n",[174,6476,6477,6480],{"class":176,"line":2258},[174,6478,6479],{"class":199},"  nginx",[174,6481,2058],{"class":192},[174,6483,6484,6486,6488,6490,6493],{"class":176,"line":2266},[174,6485,2153],{"class":199},[174,6487,203],{"class":192},[174,6489,2158],{"class":192},[174,6491,6492],{"class":1412},"nginx:stable-alpine3.17-slim",[174,6494,2164],{"class":192},[174,6496,6497,6499],{"class":176,"line":2274},[174,6498,2261],{"class":199},[174,6500,2058],{"class":192},[174,6502,6503,6505],{"class":176,"line":2282},[174,6504,2220],{"class":192},[174,6506,6340],{"class":1412},[174,6508,6509,6512],{"class":176,"line":2297},[174,6510,6511],{"class":199},"    ports",[174,6513,2058],{"class":192},[174,6515,6516,6518,6520,6523],{"class":176,"line":2312},[174,6517,2220],{"class":192},[174,6519,2158],{"class":192},[174,6521,6522],{"class":1412},"${NGINX_PORT:-80}:80",[174,6524,2164],{"class":192},[174,6526,6527,6529],{"class":176,"line":2327},[174,6528,2212],{"class":199},[174,6530,2058],{"class":192},[174,6532,6533,6535,6537,6540],{"class":176,"line":2335},[174,6534,2220],{"class":192},[174,6536,2158],{"class":192},[174,6538,6539],{"class":1412},"$PWD\u002Fdocker\u002Fnginx\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf:ro",[174,6541,2164],{"class":192},[174,6543,6544,6546,6548,6551],{"class":176,"line":2390},[174,6545,2220],{"class":192},[174,6547,2158],{"class":192},[174,6549,6550],{"class":1412},"nginx-logs:\u002Fvar\u002Flogs\u002Fnginx",[174,6552,2164],{"class":192},[174,6554,6555,6557],{"class":176,"line":2401},[174,6556,2782],{"class":199},[174,6558,2058],{"class":192},[174,6560,6561,6563],{"class":176,"line":2412},[174,6562,2220],{"class":192},[174,6564,6565],{"class":1412}," app\n",[174,6567,6568,6570],{"class":176,"line":2423},[174,6569,2055],{"class":199},[174,6571,2058],{"class":192},[174,6573,6574,6577],{"class":176,"line":2434},[174,6575,6576],{"class":199},"  express",[174,6578,2058],{"class":192},[174,6580,6581,6583,6585],{"class":176,"line":2442},[174,6582,2070],{"class":199},[174,6584,203],{"class":192},[174,6586,2075],{"class":1412},[174,6588,6589,6591],{"class":176,"line":2452},[174,6590,2080],{"class":199},[174,6592,2058],{"class":192},[174,6594,6595,6598],{"class":176,"line":2465},[174,6596,6597],{"class":199},"  mongo-data",[174,6599,2058],{"class":192},[174,6601,6602,6605],{"class":176,"line":2474},[174,6603,6604],{"class":199},"  app-data",[174,6606,2058],{"class":192},[174,6608,6609,6612],{"class":176,"line":2481},[174,6610,6611],{"class":199},"  nginx-logs",[174,6613,2058],{"class":192},[17,6615,6616,6617,2015,6619,2019,6621,6624],{},"Notre Docker Compose inclut trois services (",[24,6618,1521],{},[24,6620,6107],{},[24,6622,6623],{},"app",") connectés via un\nréseau docker pour isoler notre application des autres projets. Le service Node.js est servi\nvia le serveur Nginx, qui écoute par défaut le port 80 (tu peux utiliser 8000 pour le\ndéveloppement). On inclut également des volumes pour stocker les données de l'application.\nC'est critique pour la base Mongo puisqu'on peut avoir besoin de réaliser des opérations de\nbackup. La rétention long-terme des logs applicatifs peut nécessiter des volumes pour les\nservices nginx et app.",[12,6626,1800],{"id":1799},[17,6628,6629],{},"Pour résumer, cet article t'a guidé à travers une technique complète de déploiement d'une\napplication Express avec Docker, en se concentrant sur des aspects clés comme la performance\nMongoDB, les builds Docker multi-stage, les tests end-to-end avec Jest, et l'utilisation de\nNginx comme reverse proxy. On a vu comment Docker peut aider à accélérer le process de\ndéveloppement et de déploiement tout en maintenant la cohérence entre les environnements. Le\nstyle Singleton de MongoDB garantit une connexion stable et efficace, tandis que les builds\nDocker multi-stage réduisent la taille de l'image finale sans sacrifier la sécurité ou les\nfonctionnalités. De plus, incorporer Jest pour les tests end-to-end garantit que l'application\nest fiable et résiliente avant sa mise en production. Enfin, Nginx en reverse proxy améliore\nles performances tout en apportant une couche de protection supplémentaire.",[17,6631,6632],{},"Que tu sois un développeur expérimenté ou nouveau sur Docker, ce guide est conçu pour\néquilibrer simplicité, sécurité et performance, ce qui en fait un excellent point de départ\npour livrer des applications web scalables et efficaces.",[1255,6634,6635],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s6hCs, html code.shiki .s6hCs{--shiki-light:#9C3EDA;--shiki-light-font-style:italic;--shiki-default:#C792EA;--shiki-default-font-style:italic;--shiki-dark:#C792EA;--shiki-dark-font-style:italic}html pre.shiki code .sFweD, html code.shiki .sFweD{--shiki-light:#E2931D;--shiki-light-font-style:italic;--shiki-default:#FFCB6B;--shiki-default-font-style:italic;--shiki-dark:#FFCB6B;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":73,"searchDepth":196,"depth":196,"links":6637},[6638,6639,6640,6641,6645,6650,6651,6654,6657],{"id":14,"depth":196,"text":15},{"id":4923,"depth":196,"text":4924},{"id":1901,"depth":196,"text":1894},{"id":4942,"depth":196,"text":4943,"children":6642},[6643,6644],{"id":4954,"depth":209,"text":4955},{"id":4978,"depth":209,"text":4979},{"id":1409,"depth":196,"text":1895,"children":6646},[6647,6648,6649],{"id":5444,"depth":209,"text":5445},{"id":5459,"depth":209,"text":5460},{"id":5492,"depth":209,"text":5493},{"id":5704,"depth":196,"text":5705},{"id":6107,"depth":196,"text":6108,"children":6652},[6653],{"id":6126,"depth":209,"text":6127},{"id":3943,"depth":196,"text":6287,"children":6655},[6656],{"id":6295,"depth":209,"text":6296},{"id":1799,"depth":196,"text":1800},"2024-04-11","Comment déployer une API REST Express.js avec Docker, avec un focus sur les performances MongoDB, les builds Docker multi-stage, Jest pour les tests, et Nginx comme reverse proxy.","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fdocker-nodejs-mongodb",{},"\u002Fdevops\u002Fdeploy-expressjs-api-using-docker.fr",{"title":4904,"description":6659},"deploy-expressjs-api-using-docker","devops\u002F1.deploy-expressjs-api-using-docker.fr",[6667,6668,6669,6670,6671,1895],"Express.JS","API","Node.js","Mongo","CI\u002FCD",[6673,1900],"backend","hJ_MlCMxsyleUu0AOTv2Glxw2YHMWyizxKcjRSTb4y8",{"id":6676,"title":6677,"body":6678,"date":6924,"description":6925,"extension":1275,"img":6926,"meta":6927,"navigation":1096,"path":6928,"seo":6929,"slug":6930,"stem":6931,"tags":6932,"topics":6935,"__hash__":6937},"content\u002F4.cloud\u002Faws\u002F1.how-i-passed-clf-exam.fr.md","Mon parcours vers la certification AWS Cloud Practitioner",{"type":9,"value":6679,"toc":6909},[6680,6682,6689,6698,6702,6705,6740,6743,6747,6754,6774,6778,6781,6819,6823,6826,6830,6856,6860,6867,6871,6874,6878,6881,6884,6887,6890,6894,6897,6899,6906],[12,6681,15],{"id":14},[17,6683,6684,6685,6688],{},"Après des semaines d'étude assidue et de travail acharné, je suis ravi d'annoncer que j'ai\nrécemment atteint une étape majeure de ma carrière. Je suis maintenant ",[580,6686,6687],{},"AWS Certified Cloud\nPractitioner"," ! Cette certification a ouvert un univers d'opportunités dans le secteur du cloud\ncomputing, et j'ai hâte de partager mon parcours et mes apprentissages.",[17,6690,6691,6692,370],{},"Voir mon\n",[1241,6693,6697],{"href":6694,"rel":6695,"title":6696},"https:\u002F\u002Fwww.credly.com\u002Fbadges\u002F1a490efa-6214-427a-964a-eca1e6898685\u002Fpublic_url",[1245],"Badge vérifié sur Credly","badge vérifié",[12,6699,6701],{"id":6700},"pourquoi-amazon-web-services-aws","Pourquoi Amazon Web Services (AWS)",[17,6703,6704],{},"Tu te demandes pourquoi AWS ? Tout est parti de ma fascination pour le cloud computing et de la\ndemande croissante du marché pour des experts cloud. Amazon Web Services a révolutionné notre\nfaçon de penser le calcul, le stockage et la scalabilité.",[101,6706,6707,6716,6724,6732],{},[39,6708,6709,6712,6715],{},[580,6710,6711],{},"Portefeuille de services exhaustif",[6713,6714],"br",{},"Avec la gamme de services cloud la plus large du marché, AWS est le meilleur choix pour les\nentreprises de toutes tailles et de tous secteurs. Du calcul au réseau, du stockage aux bases\nde données, jusqu'au machine learning et bien plus encore.",[39,6717,6718,6721,6723],{},[580,6719,6720],{},"Présence mondiale",[6713,6722],{},"AWS offre une faible latence et une haute disponibilité aux clients et organisations partout\ndans le monde, avec des datacenters et des régions sur tous les continents, assurant la\nredondance et le rayonnement mondial dont les applications ont besoin.",[39,6725,6726,6729,6731],{},[580,6727,6728],{},"Fiabilité et sécurité",[6713,6730],{},"AWS a un historique de fiabilité et de sécurité solide. De la santé à la finance, il fait\ntourner certaines des charges de travail les plus critiques au monde. C'est un choix de\nconfiance grâce à ses fonctionnalités de sécurité et ses certifications de conformité.",[39,6733,6734,6737,6739],{},[580,6735,6736],{},"Une communauté et un support solides",[6713,6738],{},"La communauté AWS est vaste et dynamique, offrant une mine de ressources, de forums et de\ndocumentation. Tu peux y trouver du support et des solutions à tes défis cloud.",[17,6741,6742],{},"En résumé, AWS n'est pas qu'un fournisseur cloud ; c'est un leader du secteur qui propose les\nsolutions cloud les plus complètes, fiables et innovantes. Choisir AWS est un mouvement\nstratégique qui peut propulser ta carrière et ton entreprise vers de nouveaux sommets.",[12,6744,6746],{"id":6745},"les-objectifs-de-la-certification-cloud-practitioner","Les objectifs de la certification Cloud Practitioner",[17,6748,6749,6750,6753],{},"L'examen ",[580,6751,6752],{},"AWS Certified Cloud Practitioner (CLF-C02)"," est conçu pour les personnes capables de\ndémontrer une connaissance globale du cloud AWS, indépendamment d'un rôle métier spécifique.\nL'examen valide la capacité du candidat à :",[36,6755,6756,6759,6762,6765,6768,6771],{},[39,6757,6758],{},"Expliquer la valeur du cloud AWS.",[39,6760,6761],{},"Comprendre et expliquer le modèle de responsabilité partagée d'AWS.",[39,6763,6764],{},"Comprendre les bonnes pratiques de sécurité.",[39,6766,6767],{},"Comprendre les coûts du cloud AWS, son économie et la facturation.",[39,6769,6770],{},"Décrire et positionner les services AWS principaux : compute, réseau, base de données, stockage.",[39,6772,6773],{},"Identifier les services AWS pour les cas d'usage courants.",[151,6775,6777],{"id":6776},"le-programme-aws-cloud-practitioner","Le programme AWS Cloud Practitioner",[17,6779,6780],{},"Le programme de l'examen couvre quatre domaines :",[101,6782,6783,6792,6801,6810],{},[39,6784,6785,6788,6789,6791],{},[580,6786,6787],{},"Concepts du cloud"," (24 % du contenu noté)",[6713,6790],{},"Une compréhension fondamentale des concepts du cloud computing est nécessaire pour réussir\nl'examen. Ce domaine couvre l'élasticité, la scalabilité, la tolérance aux pannes et la haute\ndisponibilité.",[39,6793,6794,6797,6798,6800],{},[580,6795,6796],{},"Sécurité et conformité"," (30 % du contenu noté)",[6713,6799],{},"Autre sujet crucial. Ce domaine couvre divers aspects de sécurité, conformité et gestion\nresponsable.",[39,6802,6803,6806,6807,6809],{},[580,6804,6805],{},"Technologies et services cloud"," (34 % du contenu noté)",[6713,6808],{},"Le domaine le plus important de l'examen. Pour le maîtriser, il faut connaître les services\nAWS principaux comme EC2, RDS, S3, Lambda.",[39,6811,6812,6815,6816,6818],{},[580,6813,6814],{},"Facturation, tarification et support"," (12 % du contenu noté)",[6713,6817],{},"Tu dois avoir une compréhension de base de l'économie du cloud AWS, de la facturation, des\nplans de support, des types de coûts, des process de facturation et de la facturation\nconsolidée.",[12,6820,6822],{"id":6821},"ressources-détude","Ressources d'étude",[17,6824,6825],{},"Préparer l'examen AWS Cloud Practitioner suppose de planifier ses révisions, de découper les\nsujets et de se fixer des objectifs. La régularité est clé, et je recommande de consacrer du\ntemps chaque jour à l'étude. Sers-toi aussi des white papers et de la documentation AWS pour\napprofondir. L'un des facteurs clés de mon succès a été l'accès à d'excellentes ressources.\nVoici celles que j'ai utilisées :",[151,6827,6829],{"id":6828},"cours-aws","Cours AWS",[36,6831,6832,6838,6844,6850],{},[39,6833,6834,6837],{},[580,6835,6836],{},"AWS Cloud Practitioner Essentials"," : pour quiconque souhaite une compréhension globale du\ncloud AWS. Tu y apprends les concepts AWS, les services, la sécurité, l'architecture, la\ntarification et le support.",[39,6839,6840,6843],{},[580,6841,6842],{},"AWS Technical Essentials"," : introduction aux services Amazon Web Services essentiels et\naux solutions courantes. Couvre les concepts fondamentaux : compute, base de données, stockage,\nréseau, monitoring, sécurité.",[39,6845,6846,6849],{},[580,6847,6848],{},"AWS Cloud Quest: Cloud Practitioner"," : un jeu de rôle pour développer des compétences AWS\nrecherchées.",[39,6851,6852,6855],{},[580,6853,6854],{},"KodeKloud AWS Cloud Practitioner (CLF-C02)"," : un cours bien structuré qui m'a apporté\nbeaucoup. Le module Facturation & Tarification est particulièrement instructif.",[151,6857,6859],{"id":6858},"se-familiariser-avec-aws-loffre-free-tier","Se familiariser avec AWS : l'offre Free Tier",[17,6861,6862,6863,6866],{},"Le ",[580,6864,6865],{},"Free Tier"," d'AWS donne aux nouveaux utilisateurs un accès gratuit et limité à divers\nservices pendant 12 mois. Ça permet d'explorer et d'expérimenter le cloud AWS sans frais — un\nexcellent point de départ pour les débutants ou pour gagner de l'expérience pratique.",[151,6868,6870],{"id":6869},"examens-blancs-aws-cloud-practitioner","Examens blancs AWS Cloud Practitioner",[17,6872,6873],{},"La meilleure façon d'évaluer ta préparation est de passer des examens blancs. Sers-toi des\nexamens blancs gratuits AWS Certified Cloud Practitioner si tu veux viser un score élevé. Le\nformat et le contenu de ces tests imitent fidèlement l'examen réel.",[12,6875,6877],{"id":6876},"la-réservation-et-le-jour-j","La réservation et le jour J",[17,6879,6880],{},"Pour démarrer le process d'examen, j'ai dû réserver un créneau sur le site Pearson VUE. La\nréservation est simple : choisir une date et une heure adaptées. Il faut également acheter un\nvoucher d'examen, à 100 USD. Le voucher couvre le coût total de l'examen.",[17,6882,6883],{},"Plusieurs décisions clés se posent, dont le mode d'examen et les modalités. J'ai opté pour\nl'examen en centre Pearson VUE, principalement pour un environnement contrôlé et concentré.\nL'examen en ligne depuis chez soi (proctored) est possible, mais je le voyais comme une source\npotentielle de distractions. Le centre offre un espace silencieux dédié, qui limite les\nperturbations.",[17,6885,6886],{},"L'examen comprend 65 questions à choix multiples, et il faut un score minimum de 700 sur 1000\npour réussir. Les questions couvrent un éventail de sujets liés aux concepts fondamentaux du\ncloud AWS. Les résultats sont disponibles immédiatement à la fin : l'écran annonce ta réussite\n(ou non), ce qui apporte une vraie clôture à l'expérience.",[17,6888,6889],{},"Trois jours après ma réussite, j'ai reçu mon badge numérique sur Credly — un symbole concret de\nmon accomplissement. Le badge était accompagné d'un email de félicitations avec des instructions\net informations utiles sur les prochaines étapes du parcours de certification AWS. Cette\nréactivité ajoute une touche personnelle au process.",[12,6891,6893],{"id":6892},"le-mot-de-la-fin","Le mot de la fin",[17,6895,6896],{},"Si tu envisages une certification AWS, en particulier la Cloud Practitioner, sache que tu n'es\npas seul dans ce parcours. N'hésite pas à me contacter pour des questions ou de l'aide. Le monde\nd'AWS et du cloud computing regorge de possibilités, et j'ai hâte de voir où ça te mènera.",[12,6898,1800],{"id":1799},[17,6900,6901,6902,6905],{},"Pour conclure : pour quiconque s'intéresse aux technologies cloud, la certification AWS Cloud\nPractitioner est un excellent point de départ. Elle te donne les bases dont tu as besoin et\nouvre l'accès à de nombreuses options de carrière dans le cloud. En me réjouissant de cette\nréussite, je prépare déjà activement la certification ",[580,6903,6904],{},"AWS Solutions Architect Associate",", que\nje vise dans les six prochains mois. Cet engagement continu reflète ma volonté de rester à la\npointe des technologies cloud et d'approfondir mon expertise dans l'écosystème AWS.",[17,6907,6908],{},"J'espère que mon expérience t'a motivé à viser ta certification AWS. Je te souhaite plein de\nsuccès et la réalisation de tes objectifs cloud.",{"title":73,"searchDepth":196,"depth":196,"links":6910},[6911,6912,6913,6916,6921,6922,6923],{"id":14,"depth":196,"text":15},{"id":6700,"depth":196,"text":6701},{"id":6745,"depth":196,"text":6746,"children":6914},[6915],{"id":6776,"depth":209,"text":6777},{"id":6821,"depth":196,"text":6822,"children":6917},[6918,6919,6920],{"id":6828,"depth":209,"text":6829},{"id":6858,"depth":209,"text":6859},{"id":6869,"depth":209,"text":6870},{"id":6876,"depth":196,"text":6877},{"id":6892,"depth":196,"text":6893},{"id":1799,"depth":196,"text":1800},"2023-11-09","Suis mon parcours vers la certification AWS Cloud Practitioner — exploration des concepts cloud, réussite à l'examen, et les opportunités infinies dans le monde AWS.","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Fbadges\u002Feondglodvvgm9s9gietk",{},"\u002Fcloud\u002Faws\u002Fhow-i-passed-clf-exam.fr",{"title":6677,"description":6925},"how-i-passed-aws-cloud-practitioner-exam","4.cloud\u002Faws\u002F1.how-i-passed-clf-exam.fr",[6933,6934],"AWS","Certification",[6936],"cloud","XxraJQsMtwPG9Y25amy9mQZBjx45Wr-Z4Vaa8FLHoLY",{"id":6939,"title":6940,"body":6941,"date":10234,"description":10235,"extension":1275,"img":10236,"meta":10237,"navigation":1096,"path":10238,"seo":10239,"slug":10240,"stem":10241,"tags":10242,"topics":10245,"__hash__":10247},"content\u002F2.backend\u002F1.nest-js\u002F4.unlocking-the-power-of-relationships-with-typeorm.fr.md","Maîtriser NestJS : libérer la puissance des relations avec TypeORM et les bases SQL",{"type":9,"value":6942,"toc":10218},[6943,6945,6948,6951,6962,6966,6969,6972,7005,7008,7012,7022,7025,7055,7058,7062,7068,7364,7373,7379,7628,7634,7649,7653,7656,7659,7972,7977,7987,7991,8623,8627,8636,8639,8655,8768,9064,9101,9105,9108,9662,9666,9675,9680,9810,9826,9830,9833,10079,10083,10086,10208,10212,10215],[12,6944,15],{"id":14},[17,6946,6947],{},"Bienvenue dans « Maîtriser NestJS : libérer la puissance des relations avec TypeORM et les\nbases SQL ». Dans cet article, on regarde comment NestJS, conjointement avec TypeORM et les\nbases SQL, peut t'aider à construire des structures de données complexes et à gérer leurs\ninteractions. À la fin de ce tutoriel, tu seras capable de concevoir des APIs qui gèrent des\nconnexions de données complexes avec aisance, et tu feras passer ta maîtrise de NestJS au\nniveau supérieur.",[17,6949,6950],{},"Que tu sois un développeur NestJS expérimenté qui veut élargir ses connaissances ou un\ndébutant impatient de maîtriser les subtilités des relations de données, cette exploration\ncomplète te donnera l'expérience nécessaire pour construire des applications à la pointe.\nAlors, c'est parti — apprenons à concevoir des systèmes solides et interconnectés avec NestJS,\nTypeORM et les bases SQL.",[1593,6952,6953],{},[17,6954,6955,6956,370],{},"J'ai créé un dépôt GitHub pour cette série, accessible à l'\n",[1241,6957,6961],{"href":6958,"rel":6959,"title":6960},"https:\u002F\u002Fgithub.com\u002Fdenisakp\u002Fawesome-nest-js",[1245],"Projet awesome nest-js","adresse suivante",[12,6963,6965],{"id":6964},"cest-quoi-les-relations","C'est quoi les relations",[17,6967,6968],{},"Les relations sont des connexions formées entre deux tables ou plus. Elles s'établissent via\ndes champs communs aux tables, qui incluent souvent des clés primaires et étrangères.",[17,6970,6971],{},"Il existe trois types de relations :",[101,6973,6974,6983,6995],{},[39,6975,6976,6979,6980,370],{},[580,6977,6978],{},"One-to-one"," : chaque ligne de la table primaire a exactement une ligne dans la table\nétrangère. Pour définir ce type de relation, on utilise le décorateur ",[24,6981,6982],{},"@OneToOne()",[39,6984,6985,6988,6989,2019,6992,370],{},[580,6986,6987],{},"One-to-many \u002F Many-to-one"," : chaque ligne de la table primaire est connectée à une ou\nplusieurs lignes de la table étrangère. Pour définir ce type de relation, on utilise les\ndécorateurs ",[24,6990,6991],{},"@OneToMany()",[24,6993,6994],{},"@ManyToOne()",[39,6996,6997,7000,7001,7004],{},[580,6998,6999],{},"Many-to-many"," : chaque ligne de la table primaire a plusieurs lignes liées dans la table\nétrangère, et chaque enregistrement de la table étrangère a plusieurs lignes liées dans la\ntable primaire. On utilise ",[24,7002,7003],{},"@ManyToMany()"," pour définir ce type de relation.",[17,7006,7007],{},"On va passer en revue chacun de ces termes en détail.",[12,7009,7011],{"id":7010},"one-to-one","One-To-One",[17,7013,7014,7015,2019,7018,7021],{},"One-to-one est une relation où A contient une seule instance de B, et B contient une seule\ninstance de A. Prenons par exemple les entités ",[580,7016,7017],{},"User",[580,7019,7020],{},"Profile",". Un utilisateur ne peut\navoir qu'un seul profil, et un profil n'appartient qu'à un seul utilisateur.",[17,7023,7024],{},"Puisqu'on va implémenter l'authentification et l'autorisation dans le prochain article, on\ncommence par créer les ressources liées. Créons donc les ressources REST API profiles et users.",[65,7026,7028],{"className":1400,"code":7027,"filename":3012,"language":1402,"meta":73,"style":73},"     nest g resource profiles\n     nest g resource users\n",[24,7029,7030,7044],{"__ignoreMap":73},[174,7031,7032,7035,7038,7041],{"class":176,"line":177},[174,7033,7034],{"class":188},"     nest",[174,7036,7037],{"class":1412}," g",[174,7039,7040],{"class":1412}," resource",[174,7042,7043],{"class":1412}," profiles\n",[174,7045,7046,7048,7050,7052],{"class":176,"line":196},[174,7047,7034],{"class":188},[174,7049,7037],{"class":1412},[174,7051,7040],{"class":1412},[174,7053,7054],{"class":1412}," users\n",[17,7056,7057],{},"Voici la sortie pour la création de la ressource profiles :",[1557,7059],{"alt":7060,"source":7061},"Création d'une ressource Nest","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Ff_auto,q_auto\u002Fv1\u002FBlog\u002Farticles\u002Fnest-js\u002F4.unlocking-the-power-of-relationships-with-typeorm\u002Fhj3fhthehvn3vnltgewb",[17,7063,7064,7065,2899],{},"Explorons ",[24,7066,7067],{},"user.entity.ts",[65,7069,7073],{"className":7070,"code":7071,"filename":7067,"language":7072,"meta":73,"style":73},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { UserRole } from 'src\u002Fenums\u002Fuser.role';\nimport { Profile } from 'src\u002Fprofiles\u002Fentities\u002Fprofile.entity';\nimport { Column, Entity, OneToOne, PrimaryGeneratedColumn } from 'typeorm';\n\n@Entity('users')\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  name: string;\n\n  @Column({ unique: true })\n  email: string;\n\n  @Column()\n  password: string;\n\n  @Column({ type: 'enum', enum: UserRole, default: UserRole.CUSTOMER })\n  role: string;\n}\n\n","ts",[24,7074,7075,7098,7120,7157,7161,7179,7191,7201,7213,7217,7226,7237,7241,7262,7273,7277,7285,7296,7300,7349,7360],{"__ignoreMap":73},[174,7076,7077,7079,7081,7084,7086,7089,7091,7094,7096],{"class":176,"line":177},[174,7078,5749],{"class":180},[174,7080,5051],{"class":192},[174,7082,7083],{"class":350}," UserRole",[174,7085,1172],{"class":192},[174,7087,7088],{"class":180}," from",[174,7090,2158],{"class":192},[174,7092,7093],{"class":1412},"src\u002Fenums\u002Fuser.role",[174,7095,3118],{"class":192},[174,7097,3128],{"class":192},[174,7099,7100,7102,7104,7107,7109,7111,7113,7116,7118],{"class":176,"line":196},[174,7101,5749],{"class":180},[174,7103,5051],{"class":192},[174,7105,7106],{"class":350}," Profile",[174,7108,1172],{"class":192},[174,7110,7088],{"class":180},[174,7112,2158],{"class":192},[174,7114,7115],{"class":1412},"src\u002Fprofiles\u002Fentities\u002Fprofile.entity",[174,7117,3118],{"class":192},[174,7119,3128],{"class":192},[174,7121,7122,7124,7126,7129,7131,7134,7136,7139,7141,7144,7146,7148,7150,7153,7155],{"class":176,"line":209},[174,7123,5749],{"class":180},[174,7125,5051],{"class":192},[174,7127,7128],{"class":350}," Column",[174,7130,1050],{"class":192},[174,7132,7133],{"class":350}," Entity",[174,7135,1050],{"class":192},[174,7137,7138],{"class":350}," OneToOne",[174,7140,1050],{"class":192},[174,7142,7143],{"class":350}," PrimaryGeneratedColumn",[174,7145,1172],{"class":192},[174,7147,7088],{"class":180},[174,7149,2158],{"class":192},[174,7151,7152],{"class":1412},"typeorm",[174,7154,3118],{"class":192},[174,7156,3128],{"class":192},[174,7158,7159],{"class":176,"line":220},[174,7160,1097],{"emptyLinePlaceholder":1096},[174,7162,7163,7165,7168,7170,7172,7175,7177],{"class":176,"line":230},[174,7164,5035],{"class":192},[174,7166,7167],{"class":322},"Entity",[174,7169,326],{"class":350},[174,7171,3118],{"class":192},[174,7173,7174],{"class":1412},"users",[174,7176,3118],{"class":192},[174,7178,495],{"class":350},[174,7180,7181,7183,7186,7189],{"class":176,"line":240},[174,7182,181],{"class":180},[174,7184,7185],{"class":184}," class",[174,7187,7188],{"class":188}," User",[174,7190,193],{"class":192},[174,7192,7193,7196,7199],{"class":176,"line":250},[174,7194,7195],{"class":192},"  @",[174,7197,7198],{"class":322},"PrimaryGeneratedColumn",[174,7200,4030],{"class":350},[174,7202,7203,7206,7208,7211],{"class":176,"line":260},[174,7204,7205],{"class":199},"  id",[174,7207,203],{"class":192},[174,7209,7210],{"class":188}," number",[174,7212,3128],{"class":192},[174,7214,7215],{"class":176,"line":270},[174,7216,1097],{"emptyLinePlaceholder":1096},[174,7218,7219,7221,7224],{"class":176,"line":280},[174,7220,7195],{"class":192},[174,7222,7223],{"class":322},"Column",[174,7225,4030],{"class":350},[174,7227,7228,7231,7233,7235],{"class":176,"line":290},[174,7229,7230],{"class":199},"  name",[174,7232,203],{"class":192},[174,7234,340],{"class":188},[174,7236,3128],{"class":192},[174,7238,7239],{"class":176,"line":3},[174,7240,1097],{"emptyLinePlaceholder":1096},[174,7242,7243,7245,7247,7249,7251,7254,7256,7258,7260],{"class":176,"line":554},[174,7244,7195],{"class":192},[174,7246,7223],{"class":322},[174,7248,326],{"class":350},[174,7250,1155],{"class":192},[174,7252,7253],{"class":199}," unique",[174,7255,203],{"class":192},[174,7257,3361],{"class":930},[174,7259,1172],{"class":192},[174,7261,495],{"class":350},[174,7263,7264,7267,7269,7271],{"class":176,"line":562},[174,7265,7266],{"class":199},"  email",[174,7268,203],{"class":192},[174,7270,340],{"class":188},[174,7272,3128],{"class":192},[174,7274,7275],{"class":176,"line":2167},[174,7276,1097],{"emptyLinePlaceholder":1096},[174,7278,7279,7281,7283],{"class":176,"line":2177},[174,7280,7195],{"class":192},[174,7282,7223],{"class":322},[174,7284,4030],{"class":350},[174,7286,7287,7290,7292,7294],{"class":176,"line":2209},[174,7288,7289],{"class":199},"  password",[174,7291,203],{"class":192},[174,7293,340],{"class":188},[174,7295,3128],{"class":192},[174,7297,7298],{"class":176,"line":2217},[174,7299,1097],{"emptyLinePlaceholder":1096},[174,7301,7302,7304,7306,7308,7310,7313,7315,7317,7320,7322,7324,7327,7329,7331,7333,7336,7338,7340,7342,7345,7347],{"class":176,"line":2226},[174,7303,7195],{"class":192},[174,7305,7223],{"class":322},[174,7307,326],{"class":350},[174,7309,1155],{"class":192},[174,7311,7312],{"class":199}," type",[174,7314,203],{"class":192},[174,7316,2158],{"class":192},[174,7318,7319],{"class":1412},"enum",[174,7321,3118],{"class":192},[174,7323,1050],{"class":192},[174,7325,7326],{"class":199}," enum",[174,7328,203],{"class":192},[174,7330,7083],{"class":350},[174,7332,1050],{"class":192},[174,7334,7335],{"class":199}," default",[174,7337,203],{"class":192},[174,7339,7083],{"class":350},[174,7341,370],{"class":192},[174,7343,7344],{"class":350},"CUSTOMER ",[174,7346,3519],{"class":192},[174,7348,495],{"class":350},[174,7350,7351,7354,7356,7358],{"class":176,"line":2234},[174,7352,7353],{"class":199},"  role",[174,7355,203],{"class":192},[174,7357,340],{"class":188},[174,7359,3128],{"class":192},[174,7361,7362],{"class":176,"line":2246},[174,7363,302],{"class":192},[1593,7365,7366],{},[17,7367,7368,7369,7372],{},"L'enum ",[24,7370,7371],{},"UserRole"," est disponible dans le code source. Tu devrais y jeter un œil si tu veux\nvoir comment il est implémenté.",[17,7374,7375,7376,2899],{},"Maintenant explorons le contenu de ",[24,7377,7378],{},"profile.entity.ts",[65,7380,7382],{"className":7070,"code":7381,"filename":7378,"language":7072,"meta":73,"style":73},"import { User } from 'src\u002Fusers\u002Fentities\u002Fuser.entity';\nimport { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm';\n\n@Entity('profiles')\nexport class Profile {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ nullable: true })\n  full_address?: string;\n\n  @Column({ nullable: true })\n  photo?: string;\n\n  @OneToOne(() => User, (user) => user.profile) \u002F\u002F specify inverse side as a second parameter\n  @JoinColumn()\n  user: User;\n}\n\n",[24,7383,7384,7405,7442,7446,7463,7473,7481,7491,7495,7516,7528,7532,7552,7563,7567,7604,7613,7624],{"__ignoreMap":73},[174,7385,7386,7388,7390,7392,7394,7396,7398,7401,7403],{"class":176,"line":177},[174,7387,5749],{"class":180},[174,7389,5051],{"class":192},[174,7391,7188],{"class":350},[174,7393,1172],{"class":192},[174,7395,7088],{"class":180},[174,7397,2158],{"class":192},[174,7399,7400],{"class":1412},"src\u002Fusers\u002Fentities\u002Fuser.entity",[174,7402,3118],{"class":192},[174,7404,3128],{"class":192},[174,7406,7407,7409,7411,7413,7415,7417,7419,7422,7424,7426,7428,7430,7432,7434,7436,7438,7440],{"class":176,"line":196},[174,7408,5749],{"class":180},[174,7410,5051],{"class":192},[174,7412,7128],{"class":350},[174,7414,1050],{"class":192},[174,7416,7133],{"class":350},[174,7418,1050],{"class":192},[174,7420,7421],{"class":350}," JoinColumn",[174,7423,1050],{"class":192},[174,7425,7138],{"class":350},[174,7427,1050],{"class":192},[174,7429,7143],{"class":350},[174,7431,1172],{"class":192},[174,7433,7088],{"class":180},[174,7435,2158],{"class":192},[174,7437,7152],{"class":1412},[174,7439,3118],{"class":192},[174,7441,3128],{"class":192},[174,7443,7444],{"class":176,"line":209},[174,7445,1097],{"emptyLinePlaceholder":1096},[174,7447,7448,7450,7452,7454,7456,7459,7461],{"class":176,"line":220},[174,7449,5035],{"class":192},[174,7451,7167],{"class":322},[174,7453,326],{"class":350},[174,7455,3118],{"class":192},[174,7457,7458],{"class":1412},"profiles",[174,7460,3118],{"class":192},[174,7462,495],{"class":350},[174,7464,7465,7467,7469,7471],{"class":176,"line":230},[174,7466,181],{"class":180},[174,7468,7185],{"class":184},[174,7470,7106],{"class":188},[174,7472,193],{"class":192},[174,7474,7475,7477,7479],{"class":176,"line":240},[174,7476,7195],{"class":192},[174,7478,7198],{"class":322},[174,7480,4030],{"class":350},[174,7482,7483,7485,7487,7489],{"class":176,"line":250},[174,7484,7205],{"class":199},[174,7486,203],{"class":192},[174,7488,7210],{"class":188},[174,7490,3128],{"class":192},[174,7492,7493],{"class":176,"line":260},[174,7494,1097],{"emptyLinePlaceholder":1096},[174,7496,7497,7499,7501,7503,7505,7508,7510,7512,7514],{"class":176,"line":270},[174,7498,7195],{"class":192},[174,7500,7223],{"class":322},[174,7502,326],{"class":350},[174,7504,1155],{"class":192},[174,7506,7507],{"class":199}," nullable",[174,7509,203],{"class":192},[174,7511,3361],{"class":930},[174,7513,1172],{"class":192},[174,7515,495],{"class":350},[174,7517,7518,7521,7524,7526],{"class":176,"line":280},[174,7519,7520],{"class":199},"  full_address",[174,7522,7523],{"class":192},"?:",[174,7525,340],{"class":188},[174,7527,3128],{"class":192},[174,7529,7530],{"class":176,"line":290},[174,7531,1097],{"emptyLinePlaceholder":1096},[174,7533,7534,7536,7538,7540,7542,7544,7546,7548,7550],{"class":176,"line":3},[174,7535,7195],{"class":192},[174,7537,7223],{"class":322},[174,7539,326],{"class":350},[174,7541,1155],{"class":192},[174,7543,7507],{"class":199},[174,7545,203],{"class":192},[174,7547,3361],{"class":930},[174,7549,1172],{"class":192},[174,7551,495],{"class":350},[174,7553,7554,7557,7559,7561],{"class":176,"line":554},[174,7555,7556],{"class":199},"  photo",[174,7558,7523],{"class":192},[174,7560,340],{"class":188},[174,7562,3128],{"class":192},[174,7564,7565],{"class":176,"line":562},[174,7566,1097],{"emptyLinePlaceholder":1096},[174,7568,7569,7571,7574,7576,7578,7580,7582,7584,7586,7589,7591,7593,7596,7598,7601],{"class":176,"line":2167},[174,7570,7195],{"class":192},[174,7572,7573],{"class":322},"OneToOne",[174,7575,326],{"class":350},[174,7577,3327],{"class":192},[174,7579,515],{"class":184},[174,7581,7188],{"class":350},[174,7583,1050],{"class":192},[174,7585,365],{"class":192},[174,7587,7588],{"class":329},"user",[174,7590,512],{"class":192},[174,7592,515],{"class":184},[174,7594,7595],{"class":350}," user",[174,7597,370],{"class":192},[174,7599,7600],{"class":350},"profile) ",[174,7602,7603],{"class":1419},"\u002F\u002F specify inverse side as a second parameter\n",[174,7605,7606,7608,7611],{"class":176,"line":2177},[174,7607,7195],{"class":192},[174,7609,7610],{"class":322},"JoinColumn",[174,7612,4030],{"class":350},[174,7614,7615,7618,7620,7622],{"class":176,"line":2209},[174,7616,7617],{"class":199},"  user",[174,7619,203],{"class":192},[174,7621,7188],{"class":188},[174,7623,3128],{"class":192},[174,7625,7626],{"class":176,"line":2217},[174,7627,302],{"class":192},[17,7629,7630,7631,7633],{},"Ci-dessus, on a utilisé le décorateur ",[24,7632,6982],{},". Son argument est une fonction qui renvoie\nla classe de l'entité avec laquelle on souhaite établir une relation.",[17,7635,7636,7637,7640,7641,7644,7645,7648],{},"Le décorateur ",[24,7638,7639],{},"@JoinColumn()"," spécifie que la relation est détenue par l'entité Profile. Il\nsignifie que les lignes de la table Profile contiennent la colonne ",[24,7642,7643],{},"userId",", qui peut stocker\nl'",[24,7646,7647],{},"id"," d'un utilisateur. On ne l'utilise que d'un côté de la relation.",[151,7650,7652],{"id":7651},"relation-bidirectionnelle","Relation bidirectionnelle",[17,7654,7655],{},"Notre relation est actuellement unidirectionnelle. Ça signifie qu'un seul côté de la relation\nconnaît l'autre. Avec TypeORM, les relations peuvent être uni- ou bidirectionnelles. Les\nunidirectionnelles n'ont un décorateur de relation que d'un seul côté. Les bidirectionnelles\nen ont des deux côtés.",[17,7657,7658],{},"On vient de créer une relation unidirectionnelle. Rendons-la bidirectionnelle :",[65,7660,7662],{"className":7070,"code":7661,"filename":7378,"language":7072,"meta":73,"style":73},"import { UserRole } from 'src\u002Fenums\u002Fuser.role';\nimport { Profile } from 'src\u002Fprofiles\u002Fentities\u002Fprofile.entity';\nimport { Column, Entity, OneToOne, PrimaryGeneratedColumn } from 'typeorm';\n\n@Entity('users')\nexport class User {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column()\n  name: string;\n\n  @Column({ unique: true })\n  email: string;\n\n  @Column()\n  password: string;\n\n  @Column({ type: 'enum', enum: UserRole, default: UserRole.CUSTOMER })\n  role: string;\n\n  @OneToOne(() => Profile, (profile) => profile.user)\n  profile: Profile;\n}\n",[24,7663,7664,7684,7704,7736,7740,7756,7766,7774,7784,7788,7796,7806,7810,7830,7840,7844,7852,7862,7866,7910,7920,7924,7957,7968],{"__ignoreMap":73},[174,7665,7666,7668,7670,7672,7674,7676,7678,7680,7682],{"class":176,"line":177},[174,7667,5749],{"class":180},[174,7669,5051],{"class":192},[174,7671,7083],{"class":350},[174,7673,1172],{"class":192},[174,7675,7088],{"class":180},[174,7677,2158],{"class":192},[174,7679,7093],{"class":1412},[174,7681,3118],{"class":192},[174,7683,3128],{"class":192},[174,7685,7686,7688,7690,7692,7694,7696,7698,7700,7702],{"class":176,"line":196},[174,7687,5749],{"class":180},[174,7689,5051],{"class":192},[174,7691,7106],{"class":350},[174,7693,1172],{"class":192},[174,7695,7088],{"class":180},[174,7697,2158],{"class":192},[174,7699,7115],{"class":1412},[174,7701,3118],{"class":192},[174,7703,3128],{"class":192},[174,7705,7706,7708,7710,7712,7714,7716,7718,7720,7722,7724,7726,7728,7730,7732,7734],{"class":176,"line":209},[174,7707,5749],{"class":180},[174,7709,5051],{"class":192},[174,7711,7128],{"class":350},[174,7713,1050],{"class":192},[174,7715,7133],{"class":350},[174,7717,1050],{"class":192},[174,7719,7138],{"class":350},[174,7721,1050],{"class":192},[174,7723,7143],{"class":350},[174,7725,1172],{"class":192},[174,7727,7088],{"class":180},[174,7729,2158],{"class":192},[174,7731,7152],{"class":1412},[174,7733,3118],{"class":192},[174,7735,3128],{"class":192},[174,7737,7738],{"class":176,"line":220},[174,7739,1097],{"emptyLinePlaceholder":1096},[174,7741,7742,7744,7746,7748,7750,7752,7754],{"class":176,"line":230},[174,7743,5035],{"class":192},[174,7745,7167],{"class":322},[174,7747,326],{"class":350},[174,7749,3118],{"class":192},[174,7751,7174],{"class":1412},[174,7753,3118],{"class":192},[174,7755,495],{"class":350},[174,7757,7758,7760,7762,7764],{"class":176,"line":240},[174,7759,181],{"class":180},[174,7761,7185],{"class":184},[174,7763,7188],{"class":188},[174,7765,193],{"class":192},[174,7767,7768,7770,7772],{"class":176,"line":250},[174,7769,7195],{"class":192},[174,7771,7198],{"class":322},[174,7773,4030],{"class":350},[174,7775,7776,7778,7780,7782],{"class":176,"line":260},[174,7777,7205],{"class":199},[174,7779,203],{"class":192},[174,7781,7210],{"class":188},[174,7783,3128],{"class":192},[174,7785,7786],{"class":176,"line":270},[174,7787,1097],{"emptyLinePlaceholder":1096},[174,7789,7790,7792,7794],{"class":176,"line":280},[174,7791,7195],{"class":192},[174,7793,7223],{"class":322},[174,7795,4030],{"class":350},[174,7797,7798,7800,7802,7804],{"class":176,"line":290},[174,7799,7230],{"class":199},[174,7801,203],{"class":192},[174,7803,340],{"class":188},[174,7805,3128],{"class":192},[174,7807,7808],{"class":176,"line":3},[174,7809,1097],{"emptyLinePlaceholder":1096},[174,7811,7812,7814,7816,7818,7820,7822,7824,7826,7828],{"class":176,"line":554},[174,7813,7195],{"class":192},[174,7815,7223],{"class":322},[174,7817,326],{"class":350},[174,7819,1155],{"class":192},[174,7821,7253],{"class":199},[174,7823,203],{"class":192},[174,7825,3361],{"class":930},[174,7827,1172],{"class":192},[174,7829,495],{"class":350},[174,7831,7832,7834,7836,7838],{"class":176,"line":562},[174,7833,7266],{"class":199},[174,7835,203],{"class":192},[174,7837,340],{"class":188},[174,7839,3128],{"class":192},[174,7841,7842],{"class":176,"line":2167},[174,7843,1097],{"emptyLinePlaceholder":1096},[174,7845,7846,7848,7850],{"class":176,"line":2177},[174,7847,7195],{"class":192},[174,7849,7223],{"class":322},[174,7851,4030],{"class":350},[174,7853,7854,7856,7858,7860],{"class":176,"line":2209},[174,7855,7289],{"class":199},[174,7857,203],{"class":192},[174,7859,340],{"class":188},[174,7861,3128],{"class":192},[174,7863,7864],{"class":176,"line":2217},[174,7865,1097],{"emptyLinePlaceholder":1096},[174,7867,7868,7870,7872,7874,7876,7878,7880,7882,7884,7886,7888,7890,7892,7894,7896,7898,7900,7902,7904,7906,7908],{"class":176,"line":2226},[174,7869,7195],{"class":192},[174,7871,7223],{"class":322},[174,7873,326],{"class":350},[174,7875,1155],{"class":192},[174,7877,7312],{"class":199},[174,7879,203],{"class":192},[174,7881,2158],{"class":192},[174,7883,7319],{"class":1412},[174,7885,3118],{"class":192},[174,7887,1050],{"class":192},[174,7889,7326],{"class":199},[174,7891,203],{"class":192},[174,7893,7083],{"class":350},[174,7895,1050],{"class":192},[174,7897,7335],{"class":199},[174,7899,203],{"class":192},[174,7901,7083],{"class":350},[174,7903,370],{"class":192},[174,7905,7344],{"class":350},[174,7907,3519],{"class":192},[174,7909,495],{"class":350},[174,7911,7912,7914,7916,7918],{"class":176,"line":2234},[174,7913,7353],{"class":199},[174,7915,203],{"class":192},[174,7917,340],{"class":188},[174,7919,3128],{"class":192},[174,7921,7922],{"class":176,"line":2246},[174,7923,1097],{"emptyLinePlaceholder":1096},[174,7925,7926,7928,7930,7932,7934,7936,7938,7940,7942,7945,7947,7949,7952,7954],{"class":176,"line":2258},[174,7927,7195],{"class":192},[174,7929,7573],{"class":322},[174,7931,326],{"class":350},[174,7933,3327],{"class":192},[174,7935,515],{"class":184},[174,7937,7106],{"class":350},[174,7939,1050],{"class":192},[174,7941,365],{"class":192},[174,7943,7944],{"class":329},"profile",[174,7946,512],{"class":192},[174,7948,515],{"class":184},[174,7950,7951],{"class":350}," profile",[174,7953,370],{"class":192},[174,7955,7956],{"class":350},"user)\n",[174,7958,7959,7962,7964,7966],{"class":176,"line":2266},[174,7960,7961],{"class":199},"  profile",[174,7963,203],{"class":192},[174,7965,7106],{"class":188},[174,7967,3128],{"class":192},[174,7969,7970],{"class":176,"line":2274},[174,7971,302],{"class":192},[1593,7973,7974],{},[17,7975,7976],{},"À noter : la relation inverse est une idée plutôt abstraite qui n'ajoute pas de nouvelles\ncolonnes à la base.",[17,7978,7979,7980,7983,7984,7986],{},"On vient de rendre nos relations bidirectionnelles. Attention, la relation inverse n'a pas de\n",[24,7981,7982],{},"@JoinColumn",". ",[24,7985,7982],{}," ne doit être que d'un seul côté de la relation — sur la table qui\ncontient la clé étrangère.",[151,7988,7990],{"id":7989},"sauvegarder-et-récupérer-une-relation-one-to-one","Sauvegarder et récupérer une relation one-to-one",[65,7992,7995],{"className":7070,"code":7993,"filename":7994,"language":7072,"meta":73,"style":73},"import { HttpException, HttpStatus, Injectable } from '@nestjs\u002Fcommon';\nimport { CreateUserDto } from '.\u002Fdto\u002Fcreate-user.dto';\nimport { UpdateUserDto } from '.\u002Fdto\u002Fupdate-user.dto';\nimport { InjectRepository } from '@nestjs\u002Ftypeorm';\nimport { Repository } from 'typeorm';\nimport { User } from '.\u002Fentities\u002Fuser.entity';\nimport { Profile } from 'src\u002Fprofiles\u002Fentities\u002Fprofile.entity';\nimport { UpdateProfileDto } from 'src\u002Fprofiles\u002Fdto\u002Fupdate-profile.dto';\n\n@Injectable()\nexport class UsersService {\n  constructor(\n    @InjectRepository(User) private userRepository: Repository\u003CUser>,\n    @InjectRepository(Profile) private profileRepository: Repository\u003CProfile>,\n  ) {\n  }\n\n  async create(createUSerDto: CreateUserDto) {\n    const user = await this.userRepository.save(createUSerDto); \u002F\u002F saving the user\n\n    \u002F\u002F creating the the profile object\n    const profile = new Profile();\n    profile.full_address = createUSerDto.full_address;\n    profile.photo = createUSerDto.photo;\n    profile.user = user;\n\n    await this.profileRepository.save(profile); \u002F\u002F linking the profile to user\n\n    return this.findOne(user.id); \u002F\u002F return the user with the profile\n  }\n\n  async findOne(id: number) {\n    const user = await this.userRepository.findOne({\n      where: { id },\n      relations: { profile: true }, \u002F\u002F by doing this, we're implementing the eager loading to automatically load the profile object\n    });\n\n    if (!user) throw new HttpException('user not found', HttpStatus.NOT_FOUND);\n\n    return user;\n  }\n}\n\n","user.service.ts",[24,7996,7997,8029,8051,8073,8095,8116,8137,8157,8179,8183,8192,8203,8211,8240,8264,8271,8275,8279,8299,8330,8334,8339,8355,8376,8395,8409,8413,8437,8441,8465,8469,8473,8492,8514,8528,8549,8558,8562,8603,8607,8615,8619],{"__ignoreMap":73},[174,7998,7999,8001,8003,8006,8008,8011,8013,8016,8018,8020,8022,8025,8027],{"class":176,"line":177},[174,8000,5749],{"class":180},[174,8002,5051],{"class":192},[174,8004,8005],{"class":350}," HttpException",[174,8007,1050],{"class":192},[174,8009,8010],{"class":350}," HttpStatus",[174,8012,1050],{"class":192},[174,8014,8015],{"class":350}," Injectable",[174,8017,1172],{"class":192},[174,8019,7088],{"class":180},[174,8021,2158],{"class":192},[174,8023,8024],{"class":1412},"@nestjs\u002Fcommon",[174,8026,3118],{"class":192},[174,8028,3128],{"class":192},[174,8030,8031,8033,8035,8038,8040,8042,8044,8047,8049],{"class":176,"line":196},[174,8032,5749],{"class":180},[174,8034,5051],{"class":192},[174,8036,8037],{"class":350}," CreateUserDto",[174,8039,1172],{"class":192},[174,8041,7088],{"class":180},[174,8043,2158],{"class":192},[174,8045,8046],{"class":1412},".\u002Fdto\u002Fcreate-user.dto",[174,8048,3118],{"class":192},[174,8050,3128],{"class":192},[174,8052,8053,8055,8057,8060,8062,8064,8066,8069,8071],{"class":176,"line":209},[174,8054,5749],{"class":180},[174,8056,5051],{"class":192},[174,8058,8059],{"class":350}," UpdateUserDto",[174,8061,1172],{"class":192},[174,8063,7088],{"class":180},[174,8065,2158],{"class":192},[174,8067,8068],{"class":1412},".\u002Fdto\u002Fupdate-user.dto",[174,8070,3118],{"class":192},[174,8072,3128],{"class":192},[174,8074,8075,8077,8079,8082,8084,8086,8088,8091,8093],{"class":176,"line":220},[174,8076,5749],{"class":180},[174,8078,5051],{"class":192},[174,8080,8081],{"class":350}," InjectRepository",[174,8083,1172],{"class":192},[174,8085,7088],{"class":180},[174,8087,2158],{"class":192},[174,8089,8090],{"class":1412},"@nestjs\u002Ftypeorm",[174,8092,3118],{"class":192},[174,8094,3128],{"class":192},[174,8096,8097,8099,8101,8104,8106,8108,8110,8112,8114],{"class":176,"line":230},[174,8098,5749],{"class":180},[174,8100,5051],{"class":192},[174,8102,8103],{"class":350}," Repository",[174,8105,1172],{"class":192},[174,8107,7088],{"class":180},[174,8109,2158],{"class":192},[174,8111,7152],{"class":1412},[174,8113,3118],{"class":192},[174,8115,3128],{"class":192},[174,8117,8118,8120,8122,8124,8126,8128,8130,8133,8135],{"class":176,"line":240},[174,8119,5749],{"class":180},[174,8121,5051],{"class":192},[174,8123,7188],{"class":350},[174,8125,1172],{"class":192},[174,8127,7088],{"class":180},[174,8129,2158],{"class":192},[174,8131,8132],{"class":1412},".\u002Fentities\u002Fuser.entity",[174,8134,3118],{"class":192},[174,8136,3128],{"class":192},[174,8138,8139,8141,8143,8145,8147,8149,8151,8153,8155],{"class":176,"line":250},[174,8140,5749],{"class":180},[174,8142,5051],{"class":192},[174,8144,7106],{"class":350},[174,8146,1172],{"class":192},[174,8148,7088],{"class":180},[174,8150,2158],{"class":192},[174,8152,7115],{"class":1412},[174,8154,3118],{"class":192},[174,8156,3128],{"class":192},[174,8158,8159,8161,8163,8166,8168,8170,8172,8175,8177],{"class":176,"line":260},[174,8160,5749],{"class":180},[174,8162,5051],{"class":192},[174,8164,8165],{"class":350}," UpdateProfileDto",[174,8167,1172],{"class":192},[174,8169,7088],{"class":180},[174,8171,2158],{"class":192},[174,8173,8174],{"class":1412},"src\u002Fprofiles\u002Fdto\u002Fupdate-profile.dto",[174,8176,3118],{"class":192},[174,8178,3128],{"class":192},[174,8180,8181],{"class":176,"line":270},[174,8182,1097],{"emptyLinePlaceholder":1096},[174,8184,8185,8187,8190],{"class":176,"line":280},[174,8186,5035],{"class":192},[174,8188,8189],{"class":322},"Injectable",[174,8191,4030],{"class":350},[174,8193,8194,8196,8198,8201],{"class":176,"line":290},[174,8195,181],{"class":180},[174,8197,7185],{"class":184},[174,8199,8200],{"class":188}," UsersService",[174,8202,193],{"class":192},[174,8204,8205,8208],{"class":176,"line":3},[174,8206,8207],{"class":184},"  constructor",[174,8209,8210],{"class":192},"(\n",[174,8212,8213,8216,8219,8222,8225,8228,8230,8232,8235,8237],{"class":176,"line":554},[174,8214,8215],{"class":192},"    @",[174,8217,8218],{"class":322},"InjectRepository",[174,8220,8221],{"class":350},"(User) ",[174,8223,8224],{"class":184},"private",[174,8226,8227],{"class":329}," userRepository",[174,8229,203],{"class":192},[174,8231,8103],{"class":188},[174,8233,8234],{"class":192},"\u003C",[174,8236,7017],{"class":188},[174,8238,8239],{"class":192},">,\n",[174,8241,8242,8244,8246,8249,8251,8254,8256,8258,8260,8262],{"class":176,"line":562},[174,8243,8215],{"class":192},[174,8245,8218],{"class":322},[174,8247,8248],{"class":350},"(Profile) ",[174,8250,8224],{"class":184},[174,8252,8253],{"class":329}," profileRepository",[174,8255,203],{"class":192},[174,8257,8103],{"class":188},[174,8259,8234],{"class":192},[174,8261,7020],{"class":188},[174,8263,8239],{"class":192},[174,8265,8266,8269],{"class":176,"line":2167},[174,8267,8268],{"class":192},"  )",[174,8270,193],{"class":192},[174,8272,8273],{"class":176,"line":2177},[174,8274,551],{"class":192},[174,8276,8277],{"class":176,"line":2209},[174,8278,1097],{"emptyLinePlaceholder":1096},[174,8280,8281,8284,8286,8288,8291,8293,8295,8297],{"class":176,"line":2217},[174,8282,8283],{"class":184},"  async",[174,8285,1430],{"class":199},[174,8287,326],{"class":192},[174,8289,8290],{"class":329},"createUSerDto",[174,8292,203],{"class":192},[174,8294,8037],{"class":188},[174,8296,512],{"class":192},[174,8298,193],{"class":192},[174,8300,8301,8303,8305,8307,8309,8311,8314,8316,8319,8321,8323,8325,8327],{"class":176,"line":2226},[174,8302,5886],{"class":184},[174,8304,7595],{"class":350},[174,8306,354],{"class":192},[174,8308,5350],{"class":180},[174,8310,5154],{"class":192},[174,8312,8313],{"class":350},"userRepository",[174,8315,370],{"class":192},[174,8317,8318],{"class":322},"save",[174,8320,326],{"class":199},[174,8322,8290],{"class":350},[174,8324,512],{"class":199},[174,8326,749],{"class":192},[174,8328,8329],{"class":1419}," \u002F\u002F saving the user\n",[174,8331,8332],{"class":176,"line":2234},[174,8333,1097],{"emptyLinePlaceholder":1096},[174,8335,8336],{"class":176,"line":2246},[174,8337,8338],{"class":1419},"    \u002F\u002F creating the the profile object\n",[174,8340,8341,8343,8345,8347,8349,8351,8353],{"class":176,"line":2258},[174,8342,5886],{"class":184},[174,8344,7951],{"class":350},[174,8346,354],{"class":192},[174,8348,5111],{"class":192},[174,8350,7106],{"class":322},[174,8352,3327],{"class":199},[174,8354,3128],{"class":192},[174,8356,8357,8360,8362,8365,8367,8370,8372,8374],{"class":176,"line":2266},[174,8358,8359],{"class":350},"    profile",[174,8361,370],{"class":192},[174,8363,8364],{"class":350},"full_address",[174,8366,354],{"class":192},[174,8368,8369],{"class":350}," createUSerDto",[174,8371,370],{"class":192},[174,8373,8364],{"class":350},[174,8375,3128],{"class":192},[174,8377,8378,8380,8382,8385,8387,8389,8391,8393],{"class":176,"line":2274},[174,8379,8359],{"class":350},[174,8381,370],{"class":192},[174,8383,8384],{"class":350},"photo",[174,8386,354],{"class":192},[174,8388,8369],{"class":350},[174,8390,370],{"class":192},[174,8392,8384],{"class":350},[174,8394,3128],{"class":192},[174,8396,8397,8399,8401,8403,8405,8407],{"class":176,"line":2282},[174,8398,8359],{"class":350},[174,8400,370],{"class":192},[174,8402,7588],{"class":350},[174,8404,354],{"class":192},[174,8406,7595],{"class":350},[174,8408,3128],{"class":192},[174,8410,8411],{"class":176,"line":2297},[174,8412,1097],{"emptyLinePlaceholder":1096},[174,8414,8415,8417,8419,8422,8424,8426,8428,8430,8432,8434],{"class":176,"line":2312},[174,8416,5830],{"class":180},[174,8418,5154],{"class":192},[174,8420,8421],{"class":350},"profileRepository",[174,8423,370],{"class":192},[174,8425,8318],{"class":322},[174,8427,326],{"class":199},[174,8429,7944],{"class":350},[174,8431,512],{"class":199},[174,8433,749],{"class":192},[174,8435,8436],{"class":1419}," \u002F\u002F linking the profile to user\n",[174,8438,8439],{"class":176,"line":2327},[174,8440,1097],{"emptyLinePlaceholder":1096},[174,8442,8443,8445,8447,8450,8452,8454,8456,8458,8460,8462],{"class":176,"line":2335},[174,8444,5259],{"class":180},[174,8446,5154],{"class":192},[174,8448,8449],{"class":322},"findOne",[174,8451,326],{"class":199},[174,8453,7588],{"class":350},[174,8455,370],{"class":192},[174,8457,7647],{"class":350},[174,8459,512],{"class":199},[174,8461,749],{"class":192},[174,8463,8464],{"class":1419}," \u002F\u002F return the user with the profile\n",[174,8466,8467],{"class":176,"line":2390},[174,8468,551],{"class":192},[174,8470,8471],{"class":176,"line":2401},[174,8472,1097],{"emptyLinePlaceholder":1096},[174,8474,8475,8477,8480,8482,8484,8486,8488,8490],{"class":176,"line":2412},[174,8476,8283],{"class":184},[174,8478,8479],{"class":199}," findOne",[174,8481,326],{"class":192},[174,8483,7647],{"class":329},[174,8485,203],{"class":192},[174,8487,7210],{"class":188},[174,8489,512],{"class":192},[174,8491,193],{"class":192},[174,8493,8494,8496,8498,8500,8502,8504,8506,8508,8510,8512],{"class":176,"line":2423},[174,8495,5886],{"class":184},[174,8497,7595],{"class":350},[174,8499,354],{"class":192},[174,8501,5350],{"class":180},[174,8503,5154],{"class":192},[174,8505,8313],{"class":350},[174,8507,370],{"class":192},[174,8509,8449],{"class":322},[174,8511,326],{"class":199},[174,8513,469],{"class":192},[174,8515,8516,8519,8521,8523,8526],{"class":176,"line":2434},[174,8517,8518],{"class":199},"      where",[174,8520,203],{"class":192},[174,8522,5051],{"class":192},[174,8524,8525],{"class":350}," id",[174,8527,3215],{"class":192},[174,8529,8530,8533,8535,8537,8539,8541,8543,8546],{"class":176,"line":2442},[174,8531,8532],{"class":199},"      relations",[174,8534,203],{"class":192},[174,8536,5051],{"class":192},[174,8538,7951],{"class":199},[174,8540,203],{"class":192},[174,8542,3361],{"class":930},[174,8544,8545],{"class":192}," },",[174,8547,8548],{"class":1419}," \u002F\u002F by doing this, we're implementing the eager loading to automatically load the profile object\n",[174,8550,8551,8554,8556],{"class":176,"line":2452},[174,8552,8553],{"class":192},"    }",[174,8555,512],{"class":199},[174,8557,3128],{"class":192},[174,8559,8560],{"class":176,"line":2465},[174,8561,1097],{"emptyLinePlaceholder":1096},[174,8563,8564,8566,8568,8570,8572,8574,8577,8579,8581,8583,8585,8588,8590,8592,8594,8596,8599,8601],{"class":176,"line":2474},[174,8565,3334],{"class":180},[174,8567,365],{"class":199},[174,8569,3443],{"class":192},[174,8571,7588],{"class":350},[174,8573,376],{"class":199},[174,8575,8576],{"class":180},"throw",[174,8578,5111],{"class":192},[174,8580,8005],{"class":322},[174,8582,326],{"class":199},[174,8584,3118],{"class":192},[174,8586,8587],{"class":1412},"user not found",[174,8589,3118],{"class":192},[174,8591,1050],{"class":192},[174,8593,8010],{"class":350},[174,8595,370],{"class":192},[174,8597,8598],{"class":350},"NOT_FOUND",[174,8600,512],{"class":199},[174,8602,3128],{"class":192},[174,8604,8605],{"class":176,"line":2481},[174,8606,1097],{"emptyLinePlaceholder":1096},[174,8608,8609,8611,8613],{"class":176,"line":2489},[174,8610,5259],{"class":180},[174,8612,7595],{"class":350},[174,8614,3128],{"class":192},[174,8616,8617],{"class":176,"line":2497},[174,8618,551],{"class":192},[174,8620,8621],{"class":176,"line":2508},[174,8622,302],{"class":192},[12,8624,8626],{"id":8625},"many-to-one-one-to-many","Many-To-One \u002F One-To-Many",[17,8628,8629,8630,2019,8632,8635],{},"Many-to-one \u002F one-to-many est une relation où A contient plusieurs instances de B, mais B ne\ncontient qu'une seule instance de A. Prenons par exemple les entités ",[580,8631,7017],{},[580,8633,8634],{},"Order",". Un\nutilisateur peut avoir plusieurs commandes, mais chaque commande n'appartient qu'à un seul\nutilisateur.",[17,8637,8638],{},"Mettons rapidement en place la ressource REST API orders.",[65,8640,8642],{"className":1400,"code":8641,"filename":3012,"language":1402,"meta":73,"style":73},"     nest g resource orders\n",[24,8643,8644],{"__ignoreMap":73},[174,8645,8646,8648,8650,8652],{"class":176,"line":177},[174,8647,7034],{"class":188},[174,8649,7037],{"class":1412},[174,8651,7040],{"class":1412},[174,8653,8654],{"class":1412}," orders\n",[65,8656,8658],{"className":7070,"code":8657,"filename":7067,"language":7072,"meta":73,"style":73},"import { Order } from 'src\u002Forders\u002Fentities\u002Forder.entity';\n\n@Entity('users')\nexport class User {\n  \u002F\u002F we add the following to user entity\n  @OneToMany(() => Order, (order) => order.user)\n  orders: Order[];\n}\n",[24,8659,8660,8682,8686,8702,8712,8717,8750,8764],{"__ignoreMap":73},[174,8661,8662,8664,8666,8669,8671,8673,8675,8678,8680],{"class":176,"line":177},[174,8663,5749],{"class":180},[174,8665,5051],{"class":192},[174,8667,8668],{"class":350}," Order",[174,8670,1172],{"class":192},[174,8672,7088],{"class":180},[174,8674,2158],{"class":192},[174,8676,8677],{"class":1412},"src\u002Forders\u002Fentities\u002Forder.entity",[174,8679,3118],{"class":192},[174,8681,3128],{"class":192},[174,8683,8684],{"class":176,"line":196},[174,8685,1097],{"emptyLinePlaceholder":1096},[174,8687,8688,8690,8692,8694,8696,8698,8700],{"class":176,"line":209},[174,8689,5035],{"class":192},[174,8691,7167],{"class":322},[174,8693,326],{"class":350},[174,8695,3118],{"class":192},[174,8697,7174],{"class":1412},[174,8699,3118],{"class":192},[174,8701,495],{"class":350},[174,8703,8704,8706,8708,8710],{"class":176,"line":220},[174,8705,181],{"class":180},[174,8707,7185],{"class":184},[174,8709,7188],{"class":188},[174,8711,193],{"class":192},[174,8713,8714],{"class":176,"line":230},[174,8715,8716],{"class":1419},"  \u002F\u002F we add the following to user entity\n",[174,8718,8719,8721,8724,8726,8728,8730,8732,8734,8736,8739,8741,8743,8746,8748],{"class":176,"line":240},[174,8720,7195],{"class":192},[174,8722,8723],{"class":322},"OneToMany",[174,8725,326],{"class":350},[174,8727,3327],{"class":192},[174,8729,515],{"class":184},[174,8731,8668],{"class":350},[174,8733,1050],{"class":192},[174,8735,365],{"class":192},[174,8737,8738],{"class":329},"order",[174,8740,512],{"class":192},[174,8742,515],{"class":184},[174,8744,8745],{"class":350}," order",[174,8747,370],{"class":192},[174,8749,7956],{"class":350},[174,8751,8752,8755,8757,8759,8762],{"class":176,"line":250},[174,8753,8754],{"class":199},"  orders",[174,8756,203],{"class":192},[174,8758,8668],{"class":188},[174,8760,8761],{"class":350},"[]",[174,8763,3128],{"class":192},[174,8765,8766],{"class":176,"line":260},[174,8767,302],{"class":192},[65,8769,8772],{"className":7070,"code":8770,"filename":8771,"language":7072,"meta":73,"style":73},"import { OrderStatus } from 'src\u002Fenums\u002Forder.status';\nimport { User } from 'src\u002Fusers\u002Fentities\u002Fuser.entity';\nimport { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';\n\n@Entity('orders')\nexport class Order {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ type: 'enum', enum: OrderStatus, default: OrderStatus.PLACED })\n  status: string; \u002F\u002F the order status\n\n  @Column()\n  amount: number;\n\n  @ManyToOne(() => User, (user) => user.orders)\n  user: User;\n\n  @CreateDateColumn()\n  created_datetime?: Date;\n}\n","order.entity.ts",[24,8773,8774,8796,8816,8854,8858,8875,8885,8893,8903,8907,8952,8966,8970,8978,8989,8993,9025,9035,9039,9048,9060],{"__ignoreMap":73},[174,8775,8776,8778,8780,8783,8785,8787,8789,8792,8794],{"class":176,"line":177},[174,8777,5749],{"class":180},[174,8779,5051],{"class":192},[174,8781,8782],{"class":350}," OrderStatus",[174,8784,1172],{"class":192},[174,8786,7088],{"class":180},[174,8788,2158],{"class":192},[174,8790,8791],{"class":1412},"src\u002Fenums\u002Forder.status",[174,8793,3118],{"class":192},[174,8795,3128],{"class":192},[174,8797,8798,8800,8802,8804,8806,8808,8810,8812,8814],{"class":176,"line":196},[174,8799,5749],{"class":180},[174,8801,5051],{"class":192},[174,8803,7188],{"class":350},[174,8805,1172],{"class":192},[174,8807,7088],{"class":180},[174,8809,2158],{"class":192},[174,8811,7400],{"class":1412},[174,8813,3118],{"class":192},[174,8815,3128],{"class":192},[174,8817,8818,8820,8822,8824,8826,8829,8831,8833,8835,8838,8840,8842,8844,8846,8848,8850,8852],{"class":176,"line":209},[174,8819,5749],{"class":180},[174,8821,5051],{"class":192},[174,8823,7128],{"class":350},[174,8825,1050],{"class":192},[174,8827,8828],{"class":350}," CreateDateColumn",[174,8830,1050],{"class":192},[174,8832,7133],{"class":350},[174,8834,1050],{"class":192},[174,8836,8837],{"class":350}," ManyToOne",[174,8839,1050],{"class":192},[174,8841,7143],{"class":350},[174,8843,1172],{"class":192},[174,8845,7088],{"class":180},[174,8847,2158],{"class":192},[174,8849,7152],{"class":1412},[174,8851,3118],{"class":192},[174,8853,3128],{"class":192},[174,8855,8856],{"class":176,"line":220},[174,8857,1097],{"emptyLinePlaceholder":1096},[174,8859,8860,8862,8864,8866,8868,8871,8873],{"class":176,"line":230},[174,8861,5035],{"class":192},[174,8863,7167],{"class":322},[174,8865,326],{"class":350},[174,8867,3118],{"class":192},[174,8869,8870],{"class":1412},"orders",[174,8872,3118],{"class":192},[174,8874,495],{"class":350},[174,8876,8877,8879,8881,8883],{"class":176,"line":240},[174,8878,181],{"class":180},[174,8880,7185],{"class":184},[174,8882,8668],{"class":188},[174,8884,193],{"class":192},[174,8886,8887,8889,8891],{"class":176,"line":250},[174,8888,7195],{"class":192},[174,8890,7198],{"class":322},[174,8892,4030],{"class":350},[174,8894,8895,8897,8899,8901],{"class":176,"line":260},[174,8896,7205],{"class":199},[174,8898,203],{"class":192},[174,8900,7210],{"class":188},[174,8902,3128],{"class":192},[174,8904,8905],{"class":176,"line":270},[174,8906,1097],{"emptyLinePlaceholder":1096},[174,8908,8909,8911,8913,8915,8917,8919,8921,8923,8925,8927,8929,8931,8933,8935,8937,8939,8941,8943,8945,8948,8950],{"class":176,"line":280},[174,8910,7195],{"class":192},[174,8912,7223],{"class":322},[174,8914,326],{"class":350},[174,8916,1155],{"class":192},[174,8918,7312],{"class":199},[174,8920,203],{"class":192},[174,8922,2158],{"class":192},[174,8924,7319],{"class":1412},[174,8926,3118],{"class":192},[174,8928,1050],{"class":192},[174,8930,7326],{"class":199},[174,8932,203],{"class":192},[174,8934,8782],{"class":350},[174,8936,1050],{"class":192},[174,8938,7335],{"class":199},[174,8940,203],{"class":192},[174,8942,8782],{"class":350},[174,8944,370],{"class":192},[174,8946,8947],{"class":350},"PLACED ",[174,8949,3519],{"class":192},[174,8951,495],{"class":350},[174,8953,8954,8957,8959,8961,8963],{"class":176,"line":290},[174,8955,8956],{"class":199},"  status",[174,8958,203],{"class":192},[174,8960,340],{"class":188},[174,8962,749],{"class":192},[174,8964,8965],{"class":1419}," \u002F\u002F the order status\n",[174,8967,8968],{"class":176,"line":3},[174,8969,1097],{"emptyLinePlaceholder":1096},[174,8971,8972,8974,8976],{"class":176,"line":554},[174,8973,7195],{"class":192},[174,8975,7223],{"class":322},[174,8977,4030],{"class":350},[174,8979,8980,8983,8985,8987],{"class":176,"line":562},[174,8981,8982],{"class":199},"  amount",[174,8984,203],{"class":192},[174,8986,7210],{"class":188},[174,8988,3128],{"class":192},[174,8990,8991],{"class":176,"line":2167},[174,8992,1097],{"emptyLinePlaceholder":1096},[174,8994,8995,8997,9000,9002,9004,9006,9008,9010,9012,9014,9016,9018,9020,9022],{"class":176,"line":2177},[174,8996,7195],{"class":192},[174,8998,8999],{"class":322},"ManyToOne",[174,9001,326],{"class":350},[174,9003,3327],{"class":192},[174,9005,515],{"class":184},[174,9007,7188],{"class":350},[174,9009,1050],{"class":192},[174,9011,365],{"class":192},[174,9013,7588],{"class":329},[174,9015,512],{"class":192},[174,9017,515],{"class":184},[174,9019,7595],{"class":350},[174,9021,370],{"class":192},[174,9023,9024],{"class":350},"orders)\n",[174,9026,9027,9029,9031,9033],{"class":176,"line":2209},[174,9028,7617],{"class":199},[174,9030,203],{"class":192},[174,9032,7188],{"class":188},[174,9034,3128],{"class":192},[174,9036,9037],{"class":176,"line":2217},[174,9038,1097],{"emptyLinePlaceholder":1096},[174,9040,9041,9043,9046],{"class":176,"line":2226},[174,9042,7195],{"class":192},[174,9044,9045],{"class":322},"CreateDateColumn",[174,9047,4030],{"class":350},[174,9049,9050,9053,9055,9058],{"class":176,"line":2234},[174,9051,9052],{"class":199},"  created_datetime",[174,9054,7523],{"class":192},[174,9056,9057],{"class":188}," Date",[174,9059,3128],{"class":192},[174,9061,9062],{"class":176,"line":2246},[174,9063,302],{"class":192},[17,9065,9066,9067,9070,9071,9074,9075,9077,9078,9080,9081,9083,9084,7983,9086,9088,9089,9091,9092,9094,9095,9097,9098,9100],{},"On a ajouté ",[24,9068,9069],{},"@OneToMany"," à la propriété orders et défini Order comme le type de la relation\ncible. Dans une relation ",[24,9072,9073],{},"@ManyToOne"," \u002F ",[24,9076,9069],{},", tu peux omettre ",[24,9079,7982],{},".\n",[24,9082,9073],{}," ne peut pas exister sans ",[24,9085,9069],{},[24,9087,9073],{}," est requis si tu veux\nutiliser ",[24,9090,9069],{},". Cependant, si seul ",[24,9093,9073],{}," t'intéresse, tu peux le définir sans\navoir ",[24,9096,9069],{}," sur l'entité associée. Partout où ",[24,9099,9073],{}," est configuré, son entité\nliée aura un « relation id » et une clé étrangère.",[151,9102,9104],{"id":9103},"sauvegarder-et-récupérer-une-relation-one-to-many-many-to-one","Sauvegarder et récupérer une relation one-to-many \u002F many-to-one",[17,9106,9107],{},"Voici le service order complet qui implémente un CRUD sur les commandes :",[65,9109,9112],{"className":7070,"code":9110,"filename":9111,"language":7072,"meta":73,"style":73},"import { Injectable } from '@nestjs\u002Fcommon';\nimport { CreateOrderDto } from '.\u002Fdto\u002Fcreate-order.dto';\nimport { UpdateOrderDto } from '.\u002Fdto\u002Fupdate-order.dto';\nimport { InjectRepository } from '@nestjs\u002Ftypeorm';\nimport { Order } from '.\u002Fentities\u002Forder.entity';\nimport { Repository } from 'typeorm';\nimport { UsersService } from 'src\u002Fusers\u002Fusers.service';\n\n@Injectable()\nexport class OrdersService {\n  constructor(\n    @InjectRepository(Order)\n    private readonly orderRepository: Repository\u003COrder>,\n    private readonly userService: UsersService,\n  ) {\n  }\n\n  async create(userId: number, createOrderDto: CreateOrderDto) {\n    const user = await this.userService.findOne(userId);\n\n    const order = new Order();\n    order.amount = createOrderDto.amount;\n    order.user = user;\n\n    return await this.orderRepository.save(order);\n  }\n\n  async findAll(userId: number): Promise\u003COrder[]> {\n    const user = await this.userService.findOne(userId);\n    return await this.orderRepository.find({\n      where: { user },\n    });\n  }\n\n  async findOne(id: number): Promise\u003COrder | null> {\n    return await this.orderRepository.findOneBy({ id });\n  }\n}\n\n","order.Service.ts",[24,9113,9114,9134,9156,9178,9198,9219,9239,9260,9264,9272,9283,9289,9298,9319,9334,9340,9344,9348,9375,9402,9406,9422,9442,9456,9460,9483,9487,9491,9522,9548,9567,9579,9587,9591,9595,9627,9654,9658],{"__ignoreMap":73},[174,9115,9116,9118,9120,9122,9124,9126,9128,9130,9132],{"class":176,"line":177},[174,9117,5749],{"class":180},[174,9119,5051],{"class":192},[174,9121,8015],{"class":350},[174,9123,1172],{"class":192},[174,9125,7088],{"class":180},[174,9127,2158],{"class":192},[174,9129,8024],{"class":1412},[174,9131,3118],{"class":192},[174,9133,3128],{"class":192},[174,9135,9136,9138,9140,9143,9145,9147,9149,9152,9154],{"class":176,"line":196},[174,9137,5749],{"class":180},[174,9139,5051],{"class":192},[174,9141,9142],{"class":350}," CreateOrderDto",[174,9144,1172],{"class":192},[174,9146,7088],{"class":180},[174,9148,2158],{"class":192},[174,9150,9151],{"class":1412},".\u002Fdto\u002Fcreate-order.dto",[174,9153,3118],{"class":192},[174,9155,3128],{"class":192},[174,9157,9158,9160,9162,9165,9167,9169,9171,9174,9176],{"class":176,"line":209},[174,9159,5749],{"class":180},[174,9161,5051],{"class":192},[174,9163,9164],{"class":350}," UpdateOrderDto",[174,9166,1172],{"class":192},[174,9168,7088],{"class":180},[174,9170,2158],{"class":192},[174,9172,9173],{"class":1412},".\u002Fdto\u002Fupdate-order.dto",[174,9175,3118],{"class":192},[174,9177,3128],{"class":192},[174,9179,9180,9182,9184,9186,9188,9190,9192,9194,9196],{"class":176,"line":220},[174,9181,5749],{"class":180},[174,9183,5051],{"class":192},[174,9185,8081],{"class":350},[174,9187,1172],{"class":192},[174,9189,7088],{"class":180},[174,9191,2158],{"class":192},[174,9193,8090],{"class":1412},[174,9195,3118],{"class":192},[174,9197,3128],{"class":192},[174,9199,9200,9202,9204,9206,9208,9210,9212,9215,9217],{"class":176,"line":230},[174,9201,5749],{"class":180},[174,9203,5051],{"class":192},[174,9205,8668],{"class":350},[174,9207,1172],{"class":192},[174,9209,7088],{"class":180},[174,9211,2158],{"class":192},[174,9213,9214],{"class":1412},".\u002Fentities\u002Forder.entity",[174,9216,3118],{"class":192},[174,9218,3128],{"class":192},[174,9220,9221,9223,9225,9227,9229,9231,9233,9235,9237],{"class":176,"line":240},[174,9222,5749],{"class":180},[174,9224,5051],{"class":192},[174,9226,8103],{"class":350},[174,9228,1172],{"class":192},[174,9230,7088],{"class":180},[174,9232,2158],{"class":192},[174,9234,7152],{"class":1412},[174,9236,3118],{"class":192},[174,9238,3128],{"class":192},[174,9240,9241,9243,9245,9247,9249,9251,9253,9256,9258],{"class":176,"line":250},[174,9242,5749],{"class":180},[174,9244,5051],{"class":192},[174,9246,8200],{"class":350},[174,9248,1172],{"class":192},[174,9250,7088],{"class":180},[174,9252,2158],{"class":192},[174,9254,9255],{"class":1412},"src\u002Fusers\u002Fusers.service",[174,9257,3118],{"class":192},[174,9259,3128],{"class":192},[174,9261,9262],{"class":176,"line":260},[174,9263,1097],{"emptyLinePlaceholder":1096},[174,9265,9266,9268,9270],{"class":176,"line":270},[174,9267,5035],{"class":192},[174,9269,8189],{"class":322},[174,9271,4030],{"class":350},[174,9273,9274,9276,9278,9281],{"class":176,"line":280},[174,9275,181],{"class":180},[174,9277,7185],{"class":184},[174,9279,9280],{"class":188}," OrdersService",[174,9282,193],{"class":192},[174,9284,9285,9287],{"class":176,"line":290},[174,9286,8207],{"class":184},[174,9288,8210],{"class":192},[174,9290,9291,9293,9295],{"class":176,"line":3},[174,9292,8215],{"class":192},[174,9294,8218],{"class":322},[174,9296,9297],{"class":350},"(Order)\n",[174,9299,9300,9303,9306,9309,9311,9313,9315,9317],{"class":176,"line":554},[174,9301,9302],{"class":184},"    private",[174,9304,9305],{"class":184}," readonly",[174,9307,9308],{"class":329}," orderRepository",[174,9310,203],{"class":192},[174,9312,8103],{"class":188},[174,9314,8234],{"class":192},[174,9316,8634],{"class":188},[174,9318,8239],{"class":192},[174,9320,9321,9323,9325,9328,9330,9332],{"class":176,"line":562},[174,9322,9302],{"class":184},[174,9324,9305],{"class":184},[174,9326,9327],{"class":329}," userService",[174,9329,203],{"class":192},[174,9331,8200],{"class":188},[174,9333,2910],{"class":192},[174,9335,9336,9338],{"class":176,"line":2167},[174,9337,8268],{"class":192},[174,9339,193],{"class":192},[174,9341,9342],{"class":176,"line":2177},[174,9343,551],{"class":192},[174,9345,9346],{"class":176,"line":2209},[174,9347,1097],{"emptyLinePlaceholder":1096},[174,9349,9350,9352,9354,9356,9358,9360,9362,9364,9367,9369,9371,9373],{"class":176,"line":2217},[174,9351,8283],{"class":184},[174,9353,1430],{"class":199},[174,9355,326],{"class":192},[174,9357,7643],{"class":329},[174,9359,203],{"class":192},[174,9361,7210],{"class":188},[174,9363,1050],{"class":192},[174,9365,9366],{"class":329}," createOrderDto",[174,9368,203],{"class":192},[174,9370,9142],{"class":188},[174,9372,512],{"class":192},[174,9374,193],{"class":192},[174,9376,9377,9379,9381,9383,9385,9387,9390,9392,9394,9396,9398,9400],{"class":176,"line":2226},[174,9378,5886],{"class":184},[174,9380,7595],{"class":350},[174,9382,354],{"class":192},[174,9384,5350],{"class":180},[174,9386,5154],{"class":192},[174,9388,9389],{"class":350},"userService",[174,9391,370],{"class":192},[174,9393,8449],{"class":322},[174,9395,326],{"class":199},[174,9397,7643],{"class":350},[174,9399,512],{"class":199},[174,9401,3128],{"class":192},[174,9403,9404],{"class":176,"line":2234},[174,9405,1097],{"emptyLinePlaceholder":1096},[174,9407,9408,9410,9412,9414,9416,9418,9420],{"class":176,"line":2246},[174,9409,5886],{"class":184},[174,9411,8745],{"class":350},[174,9413,354],{"class":192},[174,9415,5111],{"class":192},[174,9417,8668],{"class":322},[174,9419,3327],{"class":199},[174,9421,3128],{"class":192},[174,9423,9424,9427,9429,9432,9434,9436,9438,9440],{"class":176,"line":2258},[174,9425,9426],{"class":350},"    order",[174,9428,370],{"class":192},[174,9430,9431],{"class":350},"amount",[174,9433,354],{"class":192},[174,9435,9366],{"class":350},[174,9437,370],{"class":192},[174,9439,9431],{"class":350},[174,9441,3128],{"class":192},[174,9443,9444,9446,9448,9450,9452,9454],{"class":176,"line":2266},[174,9445,9426],{"class":350},[174,9447,370],{"class":192},[174,9449,7588],{"class":350},[174,9451,354],{"class":192},[174,9453,7595],{"class":350},[174,9455,3128],{"class":192},[174,9457,9458],{"class":176,"line":2274},[174,9459,1097],{"emptyLinePlaceholder":1096},[174,9461,9462,9464,9466,9468,9471,9473,9475,9477,9479,9481],{"class":176,"line":2282},[174,9463,5259],{"class":180},[174,9465,5350],{"class":180},[174,9467,5154],{"class":192},[174,9469,9470],{"class":350},"orderRepository",[174,9472,370],{"class":192},[174,9474,8318],{"class":322},[174,9476,326],{"class":199},[174,9478,8738],{"class":350},[174,9480,512],{"class":199},[174,9482,3128],{"class":192},[174,9484,9485],{"class":176,"line":2297},[174,9486,551],{"class":192},[174,9488,9489],{"class":176,"line":2312},[174,9490,1097],{"emptyLinePlaceholder":1096},[174,9492,9493,9495,9498,9500,9502,9504,9506,9508,9511,9513,9515,9517,9520],{"class":176,"line":2327},[174,9494,8283],{"class":184},[174,9496,9497],{"class":199}," findAll",[174,9499,326],{"class":192},[174,9501,7643],{"class":329},[174,9503,203],{"class":192},[174,9505,7210],{"class":188},[174,9507,337],{"class":192},[174,9509,9510],{"class":188}," Promise",[174,9512,8234],{"class":192},[174,9514,8634],{"class":188},[174,9516,8761],{"class":350},[174,9518,9519],{"class":192},">",[174,9521,193],{"class":192},[174,9523,9524,9526,9528,9530,9532,9534,9536,9538,9540,9542,9544,9546],{"class":176,"line":2335},[174,9525,5886],{"class":184},[174,9527,7595],{"class":350},[174,9529,354],{"class":192},[174,9531,5350],{"class":180},[174,9533,5154],{"class":192},[174,9535,9389],{"class":350},[174,9537,370],{"class":192},[174,9539,8449],{"class":322},[174,9541,326],{"class":199},[174,9543,7643],{"class":350},[174,9545,512],{"class":199},[174,9547,3128],{"class":192},[174,9549,9550,9552,9554,9556,9558,9560,9563,9565],{"class":176,"line":2390},[174,9551,5259],{"class":180},[174,9553,5350],{"class":180},[174,9555,5154],{"class":192},[174,9557,9470],{"class":350},[174,9559,370],{"class":192},[174,9561,9562],{"class":322},"find",[174,9564,326],{"class":199},[174,9566,469],{"class":192},[174,9568,9569,9571,9573,9575,9577],{"class":176,"line":2401},[174,9570,8518],{"class":199},[174,9572,203],{"class":192},[174,9574,5051],{"class":192},[174,9576,7595],{"class":350},[174,9578,3215],{"class":192},[174,9580,9581,9583,9585],{"class":176,"line":2412},[174,9582,8553],{"class":192},[174,9584,512],{"class":199},[174,9586,3128],{"class":192},[174,9588,9589],{"class":176,"line":2423},[174,9590,551],{"class":192},[174,9592,9593],{"class":176,"line":2434},[174,9594,1097],{"emptyLinePlaceholder":1096},[174,9596,9597,9599,9601,9603,9605,9607,9609,9611,9613,9615,9617,9620,9623,9625],{"class":176,"line":2442},[174,9598,8283],{"class":184},[174,9600,8479],{"class":199},[174,9602,326],{"class":192},[174,9604,7647],{"class":329},[174,9606,203],{"class":192},[174,9608,7210],{"class":188},[174,9610,337],{"class":192},[174,9612,9510],{"class":188},[174,9614,8234],{"class":192},[174,9616,8634],{"class":188},[174,9618,9619],{"class":192}," |",[174,9621,9622],{"class":188}," null",[174,9624,9519],{"class":192},[174,9626,193],{"class":192},[174,9628,9629,9631,9633,9635,9637,9639,9642,9644,9646,9648,9650,9652],{"class":176,"line":2452},[174,9630,5259],{"class":180},[174,9632,5350],{"class":180},[174,9634,5154],{"class":192},[174,9636,9470],{"class":350},[174,9638,370],{"class":192},[174,9640,9641],{"class":322},"findOneBy",[174,9643,326],{"class":199},[174,9645,1155],{"class":192},[174,9647,8525],{"class":350},[174,9649,1172],{"class":192},[174,9651,512],{"class":199},[174,9653,3128],{"class":192},[174,9655,9656],{"class":176,"line":2465},[174,9657,551],{"class":192},[174,9659,9660],{"class":176,"line":2474},[174,9661,302],{"class":192},[12,9663,9665],{"id":9664},"relations-many-to-many","Relations Many-To-Many",[17,9667,9668,9669,2019,9672,9674],{},"Many-to-many est une relation dans laquelle l'entité A contient plusieurs instances de\nl'entité B, et inversement. Par exemple, considérons les entités ",[580,9670,9671],{},"Product",[580,9673,8634],{},". Une\ncommande peut inclure plusieurs produits, et chaque produit peut appartenir à plusieurs\ncommandes.",[17,9676,9677,9678,2899],{},"Explorons le contenu de ",[24,9679,7378],{},[65,9681,9683],{"className":7070,"code":9682,"filename":8771,"language":7072,"meta":73,"style":73},"import { Product } from 'src\u002Fproducts\u002Fentities\u002Fproduct.entity';\nimport { ManyToMany, JoinTable } from 'typeorm';\n\n@Entity('orders')\nexport class Order {\n  \u002F\u002F ..... other properties\n\n  \u002F\u002F our new property\n  @ManyToMany(() => Product)\n  products: Product[];\n}\n",[24,9684,9685,9707,9733,9737,9753,9763,9768,9772,9777,9793,9806],{"__ignoreMap":73},[174,9686,9687,9689,9691,9694,9696,9698,9700,9703,9705],{"class":176,"line":177},[174,9688,5749],{"class":180},[174,9690,5051],{"class":192},[174,9692,9693],{"class":350}," Product",[174,9695,1172],{"class":192},[174,9697,7088],{"class":180},[174,9699,2158],{"class":192},[174,9701,9702],{"class":1412},"src\u002Fproducts\u002Fentities\u002Fproduct.entity",[174,9704,3118],{"class":192},[174,9706,3128],{"class":192},[174,9708,9709,9711,9713,9716,9718,9721,9723,9725,9727,9729,9731],{"class":176,"line":196},[174,9710,5749],{"class":180},[174,9712,5051],{"class":192},[174,9714,9715],{"class":350}," ManyToMany",[174,9717,1050],{"class":192},[174,9719,9720],{"class":350}," JoinTable",[174,9722,1172],{"class":192},[174,9724,7088],{"class":180},[174,9726,2158],{"class":192},[174,9728,7152],{"class":1412},[174,9730,3118],{"class":192},[174,9732,3128],{"class":192},[174,9734,9735],{"class":176,"line":209},[174,9736,1097],{"emptyLinePlaceholder":1096},[174,9738,9739,9741,9743,9745,9747,9749,9751],{"class":176,"line":220},[174,9740,5035],{"class":192},[174,9742,7167],{"class":322},[174,9744,326],{"class":350},[174,9746,3118],{"class":192},[174,9748,8870],{"class":1412},[174,9750,3118],{"class":192},[174,9752,495],{"class":350},[174,9754,9755,9757,9759,9761],{"class":176,"line":230},[174,9756,181],{"class":180},[174,9758,7185],{"class":184},[174,9760,8668],{"class":188},[174,9762,193],{"class":192},[174,9764,9765],{"class":176,"line":240},[174,9766,9767],{"class":1419},"  \u002F\u002F ..... other properties\n",[174,9769,9770],{"class":176,"line":250},[174,9771,1097],{"emptyLinePlaceholder":1096},[174,9773,9774],{"class":176,"line":260},[174,9775,9776],{"class":1419},"  \u002F\u002F our new property\n",[174,9778,9779,9781,9784,9786,9788,9790],{"class":176,"line":270},[174,9780,7195],{"class":192},[174,9782,9783],{"class":322},"ManyToMany",[174,9785,326],{"class":350},[174,9787,3327],{"class":192},[174,9789,515],{"class":184},[174,9791,9792],{"class":350}," Product)\n",[174,9794,9795,9798,9800,9802,9804],{"class":176,"line":280},[174,9796,9797],{"class":199},"  products",[174,9799,203],{"class":192},[174,9801,9693],{"class":188},[174,9803,8761],{"class":350},[174,9805,3128],{"class":192},[174,9807,9808],{"class":176,"line":290},[174,9809,302],{"class":192},[1593,9811,9812],{},[17,9813,9814,9817,9818,9821,9822,9825],{},[24,9815,9816],{},"@JoinTable()"," est requis pour les relations ",[24,9819,9820],{},"@ManyToMany",". Tu dois mettre ",[24,9823,9824],{},"@JoinTable"," sur\nun seul côté (le côté propriétaire) de la relation.",[151,9827,9829],{"id":9828},"sauvegarder-des-relations-many-to-many","Sauvegarder des relations many-to-many",[17,9831,9832],{},"Mettons à jour le fichier de service order et ajoutons quelques lignes à la méthode create :",[65,9834,9836],{"className":7070,"code":9835,"filename":9111,"language":7072,"meta":73,"style":73},"export class OrdersService {\n  \u002F\u002F ....previous stuffs \n\n  async create(userId: number, createOrderDto: CreateOrderDto) {\n    \u002F\u002F ..... previous stuffs\n\n    const products = [];\n\n    for (const product of createOrderDto.products) {\n      try {\n        const dbProduct = await this.productService.findOne(product);\n        products.push(dbProduct);\n      } catch (error) {\n        \u002F\u002F We'll update this later\n        console.log('failed to find product with id ' + product);\n      }\n    }\n\n    order.products = products;\n\n    \u002F\u002F .... previous stuffs\n  }\n}\n",[24,9837,9838,9848,9853,9857,9883,9888,9892,9906,9910,9937,9943,9973,9991,10005,10010,10036,10040,10044,10048,10062,10066,10071,10075],{"__ignoreMap":73},[174,9839,9840,9842,9844,9846],{"class":176,"line":177},[174,9841,181],{"class":180},[174,9843,7185],{"class":184},[174,9845,9280],{"class":188},[174,9847,193],{"class":192},[174,9849,9850],{"class":176,"line":196},[174,9851,9852],{"class":1419},"  \u002F\u002F ....previous stuffs \n",[174,9854,9855],{"class":176,"line":209},[174,9856,1097],{"emptyLinePlaceholder":1096},[174,9858,9859,9861,9863,9865,9867,9869,9871,9873,9875,9877,9879,9881],{"class":176,"line":220},[174,9860,8283],{"class":184},[174,9862,1430],{"class":199},[174,9864,326],{"class":192},[174,9866,7643],{"class":329},[174,9868,203],{"class":192},[174,9870,7210],{"class":188},[174,9872,1050],{"class":192},[174,9874,9366],{"class":329},[174,9876,203],{"class":192},[174,9878,9142],{"class":188},[174,9880,512],{"class":192},[174,9882,193],{"class":192},[174,9884,9885],{"class":176,"line":230},[174,9886,9887],{"class":1419},"    \u002F\u002F ..... previous stuffs\n",[174,9889,9890],{"class":176,"line":240},[174,9891,1097],{"emptyLinePlaceholder":1096},[174,9893,9894,9896,9899,9901,9904],{"class":176,"line":250},[174,9895,5886],{"class":184},[174,9897,9898],{"class":350}," products",[174,9900,354],{"class":192},[174,9902,9903],{"class":199}," []",[174,9905,3128],{"class":192},[174,9907,9908],{"class":176,"line":260},[174,9909,1097],{"emptyLinePlaceholder":1096},[174,9911,9912,9915,9917,9920,9923,9926,9928,9930,9933,9935],{"class":176,"line":270},[174,9913,9914],{"class":180},"    for",[174,9916,365],{"class":199},[174,9918,9919],{"class":184},"const",[174,9921,9922],{"class":350}," product",[174,9924,9925],{"class":192}," of",[174,9927,9366],{"class":350},[174,9929,370],{"class":192},[174,9931,9932],{"class":350},"products",[174,9934,376],{"class":199},[174,9936,469],{"class":192},[174,9938,9939,9941],{"class":176,"line":280},[174,9940,5097],{"class":180},[174,9942,193],{"class":192},[174,9944,9945,9948,9951,9953,9955,9957,9960,9962,9964,9966,9969,9971],{"class":176,"line":290},[174,9946,9947],{"class":184},"        const",[174,9949,9950],{"class":350}," dbProduct",[174,9952,354],{"class":192},[174,9954,5350],{"class":180},[174,9956,5154],{"class":192},[174,9958,9959],{"class":350},"productService",[174,9961,370],{"class":192},[174,9963,8449],{"class":322},[174,9965,326],{"class":199},[174,9967,9968],{"class":350},"product",[174,9970,512],{"class":199},[174,9972,3128],{"class":192},[174,9974,9975,9978,9980,9982,9984,9987,9989],{"class":176,"line":3},[174,9976,9977],{"class":350},"        products",[174,9979,370],{"class":192},[174,9981,1146],{"class":322},[174,9983,326],{"class":199},[174,9985,9986],{"class":350},"dbProduct",[174,9988,512],{"class":199},[174,9990,3128],{"class":192},[174,9992,9993,9995,9997,9999,10001,10003],{"class":176,"line":554},[174,9994,5196],{"class":192},[174,9996,3394],{"class":180},[174,9998,365],{"class":199},[174,10000,5203],{"class":350},[174,10002,376],{"class":199},[174,10004,469],{"class":192},[174,10006,10007],{"class":176,"line":562},[174,10008,10009],{"class":1419},"        \u002F\u002F We'll update this later\n",[174,10011,10012,10014,10016,10019,10021,10023,10026,10028,10030,10032,10034],{"class":176,"line":2167},[174,10013,5170],{"class":350},[174,10015,370],{"class":192},[174,10017,10018],{"class":322},"log",[174,10020,326],{"class":199},[174,10022,3118],{"class":192},[174,10024,10025],{"class":1412},"failed to find product with id ",[174,10027,3118],{"class":192},[174,10029,825],{"class":192},[174,10031,9922],{"class":350},[174,10033,512],{"class":199},[174,10035,3128],{"class":192},[174,10037,10038],{"class":176,"line":2177},[174,10039,5250],{"class":192},[174,10041,10042],{"class":176,"line":2209},[174,10043,3386],{"class":192},[174,10045,10046],{"class":176,"line":2217},[174,10047,1097],{"emptyLinePlaceholder":1096},[174,10049,10050,10052,10054,10056,10058,10060],{"class":176,"line":2226},[174,10051,9426],{"class":350},[174,10053,370],{"class":192},[174,10055,9932],{"class":350},[174,10057,354],{"class":192},[174,10059,9898],{"class":350},[174,10061,3128],{"class":192},[174,10063,10064],{"class":176,"line":2234},[174,10065,1097],{"emptyLinePlaceholder":1096},[174,10067,10068],{"class":176,"line":2246},[174,10069,10070],{"class":1419},"    \u002F\u002F .... previous stuffs\n",[174,10072,10073],{"class":176,"line":2258},[174,10074,551],{"class":192},[174,10076,10077],{"class":176,"line":2266},[174,10078,302],{"class":192},[151,10080,10082],{"id":10081},"charger-des-relations-many-to-many","Charger des relations many-to-many",[17,10084,10085],{},"Pour charger des commandes avec leurs produits, tu dois spécifier la relation dans les\nFindOptions :",[65,10087,10089],{"className":7070,"code":10088,"filename":9111,"language":7072,"meta":73,"style":73},"export class OrdersService {\n  \u002F\u002F ....previous stuffs \n\n  async findOne(id: number): Promise\u003COrder | null> {\n    return await this.orderRepository.findOne({\n      relations: {\n        products: true,\n      },\n      where: { id },\n    });\n  }\n}\n",[24,10090,10091,10101,10105,10109,10139,10157,10165,10175,10180,10192,10200,10204],{"__ignoreMap":73},[174,10092,10093,10095,10097,10099],{"class":176,"line":177},[174,10094,181],{"class":180},[174,10096,7185],{"class":184},[174,10098,9280],{"class":188},[174,10100,193],{"class":192},[174,10102,10103],{"class":176,"line":196},[174,10104,9852],{"class":1419},[174,10106,10107],{"class":176,"line":209},[174,10108,1097],{"emptyLinePlaceholder":1096},[174,10110,10111,10113,10115,10117,10119,10121,10123,10125,10127,10129,10131,10133,10135,10137],{"class":176,"line":220},[174,10112,8283],{"class":184},[174,10114,8479],{"class":199},[174,10116,326],{"class":192},[174,10118,7647],{"class":329},[174,10120,203],{"class":192},[174,10122,7210],{"class":188},[174,10124,337],{"class":192},[174,10126,9510],{"class":188},[174,10128,8234],{"class":192},[174,10130,8634],{"class":188},[174,10132,9619],{"class":192},[174,10134,9622],{"class":188},[174,10136,9519],{"class":192},[174,10138,193],{"class":192},[174,10140,10141,10143,10145,10147,10149,10151,10153,10155],{"class":176,"line":230},[174,10142,5259],{"class":180},[174,10144,5350],{"class":180},[174,10146,5154],{"class":192},[174,10148,9470],{"class":350},[174,10150,370],{"class":192},[174,10152,8449],{"class":322},[174,10154,326],{"class":199},[174,10156,469],{"class":192},[174,10158,10159,10161,10163],{"class":176,"line":240},[174,10160,8532],{"class":199},[174,10162,203],{"class":192},[174,10164,193],{"class":192},[174,10166,10167,10169,10171,10173],{"class":176,"line":250},[174,10168,9977],{"class":199},[174,10170,203],{"class":192},[174,10172,3361],{"class":930},[174,10174,2910],{"class":192},[174,10176,10177],{"class":176,"line":260},[174,10178,10179],{"class":192},"      },\n",[174,10181,10182,10184,10186,10188,10190],{"class":176,"line":270},[174,10183,8518],{"class":199},[174,10185,203],{"class":192},[174,10187,5051],{"class":192},[174,10189,8525],{"class":350},[174,10191,3215],{"class":192},[174,10193,10194,10196,10198],{"class":176,"line":280},[174,10195,8553],{"class":192},[174,10197,512],{"class":199},[174,10199,3128],{"class":192},[174,10201,10202],{"class":176,"line":290},[174,10203,551],{"class":192},[174,10205,10206],{"class":176,"line":3},[174,10207,302],{"class":192},[12,10209,10211],{"id":10210},"résumé","Résumé",[17,10213,10214],{},"On a plongé en profondeur dans le monde des relations de données avec NestJS, TypeORM et les\nbases SQL dans cet article étendu. Ce cours t'aidera, que tu sois un développeur NestJS\nexpérimenté qui veut élargir son expertise ou un débutant désireux d'apprendre les nuances des\nrelations de données. Reste à l'écoute pour le prochain article, où on plongera dans la\nvalidation des données et la gestion d'erreurs, en t'équipant d'encore plus d'outils pour\ndevenir un expert NestJS. Rejoins-moi dans cette aventure, et maîtrisons ensemble l'art des\nrelations de données dans NestJS !",[1255,10216,10217],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":73,"searchDepth":196,"depth":196,"links":10219},[10220,10221,10222,10226,10229,10233],{"id":14,"depth":196,"text":15},{"id":6964,"depth":196,"text":6965},{"id":7010,"depth":196,"text":7011,"children":10223},[10224,10225],{"id":7651,"depth":209,"text":7652},{"id":7989,"depth":209,"text":7990},{"id":8625,"depth":196,"text":8626,"children":10227},[10228],{"id":9103,"depth":209,"text":9104},{"id":9664,"depth":196,"text":9665,"children":10230},[10231,10232],{"id":9828,"depth":209,"text":9829},{"id":10081,"depth":209,"text":10082},{"id":10210,"depth":196,"text":10211},"2023-10-18","Libère la puissance des relations de données avec NestJS, TypeORM et les bases SQL. Maîtrise l'art de construire des structures de données complexes et des interactions fluides. Idéal pour les développeurs NestJS expérimentés comme pour les débutants qui veulent créer des applications à la pointe.","https:\u002F\u002Fres.cloudinary.com\u002Fdpdwhd6ka\u002Fimage\u002Fupload\u002Fv1692780505\u002FBlog\u002Farticles\u002Fnest-js\u002Fazgcjzm8pwgdckdybir9.svg",{},"\u002Fbackend\u002Fnest-js\u002Funlocking-the-power-of-relationships-with-typeorm.fr",{"title":6940,"description":10235},"unlocking-the-power-of-relationships-with-typeorm","2.backend\u002F1.nest-js\u002F4.unlocking-the-power-of-relationships-with-typeorm.fr",[10243,10244,6668],"Nest-JS","Mirco-service",[10246,6673],"nest-js","eKO_TFItzD7OmMuYjsW6XzBxsj_GjsCjqGu4YOOyloc",1780074484743]