[{"data":1,"prerenderedAt":931},["ShallowReactive",2],{"page-\u002Fnuxt\u002Froutes-api":3,"pieces-nuxt":884,"autour-\u002Fnuxt\u002Froutes-api":913,"prerequis-\u002Fnuxt\u002Froutes-api":918,"plan-nuxt":920},{"id":4,"title":5,"body":6,"description":849,"extension":850,"ligne":851,"meta":852,"motsCles":853,"navigation":228,"niveau":857,"path":858,"prerequis":859,"ressources":861,"seo":872,"sources":873,"stem":882,"__hash__":883},"docs\u002Fnuxt\u002F06.routes-api.md","Les routes API",{"type":7,"value":8,"toc":842},"minimark",[9,14,30,34,55,111,118,271,286,290,297,308,452,464,476,503,516,614,620,624,634,701,715,732,738,782,795,799,838],[10,11,13],"h2",{"id":12},"ce-que-tu-vas-apprendre","Ce que tu vas apprendre",[15,16,17,21,24,27],"ul",{},[18,19,20],"li",{},"Créer une route d'API qui renvoie la liste des recettes.",[18,22,23],{},"Lire un paramètre d'adresse, une requête et le corps d'un envoi.",[18,25,26],{},"Renvoyer une erreur propre, comme une 404.",[18,28,29],{},"Ranger une clé secrète pour qu'elle n'arrive jamais dans le navigateur.",[10,31,33],{"id":32},"une-route-un-fichier","Une route, un fichier",[35,36,37,38,42,43,46,47,50,51,54],"p",{},"Les fichiers de ",[39,40,41],"code",{},"server\u002Fapi\u002F"," deviennent des adresses préfixées par ",[39,44,45],{},"\u002Fapi",". La fin du nom de fichier choisit la méthode HTTP : ",[39,48,49],{},".get.ts"," pour lire, ",[39,52,53],{},".post.ts"," pour créer.",[56,57,58,71],"table",{},[59,60,61],"thead",{},[62,63,64,68],"tr",{},[65,66,67],"th",{},"Fichier",[65,69,70],{},"Requête",[72,73,74,87,99],"tbody",{},[62,75,76,82],{},[77,78,79],"td",{},[39,80,81],{},"server\u002Fapi\u002Frecettes.get.ts",[77,83,84],{},[39,85,86],{},"GET \u002Fapi\u002Frecettes",[62,88,89,94],{},[77,90,91],{},[39,92,93],{},"server\u002Fapi\u002Frecettes.post.ts",[77,95,96],{},[39,97,98],{},"POST \u002Fapi\u002Frecettes",[62,100,101,106],{},[77,102,103],{},[39,104,105],{},"server\u002Fapi\u002Frecettes\u002F[id].get.ts",[77,107,108],{},[39,109,110],{},"GET \u002Fapi\u002Frecettes\u002F12",[35,112,113,114,117],{},"Chaque fichier exporte un gestionnaire, ",[39,115,116],{},"defineEventHandler",". Ce qu'il renvoie part en JSON :",[119,120,125],"pre",{"className":121,"code":122,"filename":81,"language":123,"meta":124,"style":124},"language-ts shiki shiki-themes github-light-default github-dark-default github-dark-default","\u002F\u002F Pour l'exemple, les recettes sont dans un tableau.\n\u002F\u002F Dans un vrai projet, elles viendraient d'une base (Supabase par exemple).\nconst recettes = [\n  { id: 1, titre: 'Crêpes', duree: 20, categorie: 'dessert' },\n  { id: 2, titre: 'Ratatouille', duree: 60, categorie: 'plat' }\n]\n\nexport default defineEventHandler(() => {\n  return recettes\n})\n","ts","",[39,126,127,136,142,160,191,217,223,230,253,262],{"__ignoreMap":124},[128,129,132],"span",{"class":130,"line":131},"line",1,[128,133,135],{"class":134},"sd6ml","\u002F\u002F Pour l'exemple, les recettes sont dans un tableau.\n",[128,137,139],{"class":130,"line":138},2,[128,140,141],{"class":134},"\u002F\u002F Dans un vrai projet, elles viendraient d'une base (Supabase par exemple).\n",[128,143,145,149,153,156],{"class":130,"line":144},3,[128,146,148],{"class":147},"sOnC1","const",[128,150,152],{"class":151},"s5iOY"," recettes",[128,154,155],{"class":147}," =",[128,157,159],{"class":158},"scfrF"," [\n",[128,161,163,166,169,172,176,179,182,185,188],{"class":130,"line":162},4,[128,164,165],{"class":158},"  { id: ",[128,167,168],{"class":151},"1",[128,170,171],{"class":158},", titre: ",[128,173,175],{"class":174},"shWOn","'Crêpes'",[128,177,178],{"class":158},", duree: ",[128,180,181],{"class":151},"20",[128,183,184],{"class":158},", categorie: ",[128,186,187],{"class":174},"'dessert'",[128,189,190],{"class":158}," },\n",[128,192,194,196,199,201,204,206,209,211,214],{"class":130,"line":193},5,[128,195,165],{"class":158},[128,197,198],{"class":151},"2",[128,200,171],{"class":158},[128,202,203],{"class":174},"'Ratatouille'",[128,205,178],{"class":158},[128,207,208],{"class":151},"60",[128,210,184],{"class":158},[128,212,213],{"class":174},"'plat'",[128,215,216],{"class":158}," }\n",[128,218,220],{"class":130,"line":219},6,[128,221,222],{"class":158},"]\n",[128,224,226],{"class":130,"line":225},7,[128,227,229],{"emptyLinePlaceholder":228},true,"\n",[128,231,233,236,239,243,247,250],{"class":130,"line":232},8,[128,234,235],{"class":147},"export",[128,237,238],{"class":147}," default",[128,240,242],{"class":241},"syCW7"," defineEventHandler",[128,244,246],{"class":245},"sIXEE","(() ",[128,248,249],{"class":147},"=>",[128,251,252],{"class":158}," {\n",[128,254,256,259],{"class":130,"line":255},9,[128,257,258],{"class":147},"  return",[128,260,261],{"class":158}," recettes\n",[128,263,265,268],{"class":130,"line":264},10,[128,266,267],{"class":158},"}",[128,269,270],{"class":245},")\n",[35,272,273,274,277,278,281,282,285],{},"Lance ",[39,275,276],{},"npm run dev"," et ouvre ",[39,279,280],{},"http:\u002F\u002Flocalhost:3000\u002Fapi\u002Frecettes"," : le JSON s'affiche. C'est l'adresse que ta page appelle avec ",[39,283,284],{},"useFetch",".",[10,287,289],{"id":288},"lire-ce-que-la-requête-contient","Lire ce que la requête contient",[35,291,292,293,296],{},"Le gestionnaire reçoit un ",[39,294,295],{},"event",", qui décrit la requête. Des fonctions toutes prêtes en extraient les morceaux.",[35,298,299,303,304,307],{},[300,301,302],"strong",{},"Un paramètre d'adresse",", avec ",[39,305,306],{},"getRouterParam"," :",[119,309,311],{"className":121,"code":310,"filename":105,"language":123,"meta":124,"style":124},"\u002F\u002F recettes : le même tableau. Range-le dans server\u002Futils\u002Frecettes.ts\n\u002F\u002F avec export const : Nuxt l'importe automatiquement dans tout server\u002F.\nexport default defineEventHandler((event) => {\n  const id = Number(getRouterParam(event, 'id'))\n  const recette = recettes.find(r => r.id === id)\n  if (!recette) {\n    throw createError({ statusCode: 404, statusMessage: 'Recette introuvable' })\n  }\n  return recette\n})\n",[39,312,313,318,323,338,365,397,411,434,439,446],{"__ignoreMap":124},[128,314,315],{"class":130,"line":131},[128,316,317],{"class":134},"\u002F\u002F recettes : le même tableau. Range-le dans server\u002Futils\u002Frecettes.ts\n",[128,319,320],{"class":130,"line":138},[128,321,322],{"class":134},"\u002F\u002F avec export const : Nuxt l'importe automatiquement dans tout server\u002F.\n",[128,324,325,327,329,331,334,336],{"class":130,"line":144},[128,326,235],{"class":147},[128,328,238],{"class":147},[128,330,242],{"class":241},[128,332,333],{"class":245},"((event) ",[128,335,249],{"class":147},[128,337,252],{"class":158},[128,339,340,343,346,348,351,354,356,359,362],{"class":130,"line":162},[128,341,342],{"class":147},"  const",[128,344,345],{"class":151}," id",[128,347,155],{"class":147},[128,349,350],{"class":241}," Number",[128,352,353],{"class":158},"(",[128,355,306],{"class":241},[128,357,358],{"class":158},"(event, ",[128,360,361],{"class":174},"'id'",[128,363,364],{"class":158},"))\n",[128,366,367,369,372,374,377,380,382,385,388,391,394],{"class":130,"line":193},[128,368,342],{"class":147},[128,370,371],{"class":151}," recette",[128,373,155],{"class":147},[128,375,376],{"class":158}," recettes.",[128,378,379],{"class":241},"find",[128,381,353],{"class":158},[128,383,384],{"class":245},"r",[128,386,387],{"class":147}," =>",[128,389,390],{"class":158}," r.id ",[128,392,393],{"class":147},"===",[128,395,396],{"class":158}," id)\n",[128,398,399,402,405,408],{"class":130,"line":219},[128,400,401],{"class":147},"  if",[128,403,404],{"class":158}," (",[128,406,407],{"class":147},"!",[128,409,410],{"class":158},"recette) {\n",[128,412,413,416,419,422,425,428,431],{"class":130,"line":225},[128,414,415],{"class":147},"    throw",[128,417,418],{"class":241}," createError",[128,420,421],{"class":158},"({ statusCode: ",[128,423,424],{"class":151},"404",[128,426,427],{"class":158},", statusMessage: ",[128,429,430],{"class":174},"'Recette introuvable'",[128,432,433],{"class":158}," })\n",[128,435,436],{"class":130,"line":232},[128,437,438],{"class":158},"  }\n",[128,440,441,443],{"class":130,"line":255},[128,442,258],{"class":147},[128,444,445],{"class":158}," recette\n",[128,447,448,450],{"class":130,"line":264},[128,449,267],{"class":158},[128,451,270],{"class":245},[35,453,454,457,458,460,461,285],{},[39,455,456],{},"createError"," renvoie un vrai code HTTP. Côté page, ",[39,459,284],{}," le récupère dans ",[39,462,463],{},"error",[35,465,466,404,469,472,473,307],{},[300,467,468],{},"Une requête",[39,470,471],{},"\u002Fapi\u002Frecettes?categorie=dessert","), avec ",[39,474,475],{},"getQuery",[119,477,479],{"className":121,"code":478,"language":123,"meta":124,"style":124},"const { categorie } = getQuery(event)\n",[39,480,481],{"__ignoreMap":124},[128,482,483,485,488,491,494,497,500],{"class":130,"line":131},[128,484,148],{"class":147},[128,486,487],{"class":158}," { ",[128,489,490],{"class":151},"categorie",[128,492,493],{"class":158}," } ",[128,495,496],{"class":147},"=",[128,498,499],{"class":241}," getQuery",[128,501,502],{"class":158},"(event)\n",[35,504,505,303,508,511,512,515],{},[300,506,507],{},"Le corps d'un envoi",[39,509,510],{},"readBody"," (la fonction est asynchrone, d'où le ",[39,513,514],{},"await",") :",[119,517,519],{"className":121,"code":518,"filename":93,"language":123,"meta":124,"style":124},"export default defineEventHandler(async (event) => {\n  const corps = await readBody(event)\n  if (!corps?.titre) {\n    throw createError({ statusCode: 400, statusMessage: 'Le titre est obligatoire' })\n  }\n  \u002F\u002F ... enregistrer la recette\n  return { ok: true }\n})\n",[39,520,521,541,558,569,587,591,596,608],{"__ignoreMap":124},[128,522,523,525,527,529,531,534,537,539],{"class":130,"line":131},[128,524,235],{"class":147},[128,526,238],{"class":147},[128,528,242],{"class":241},[128,530,353],{"class":245},[128,532,533],{"class":147},"async",[128,535,536],{"class":245}," (event) ",[128,538,249],{"class":147},[128,540,252],{"class":158},[128,542,543,545,548,550,553,556],{"class":130,"line":138},[128,544,342],{"class":147},[128,546,547],{"class":151}," corps",[128,549,155],{"class":147},[128,551,552],{"class":147}," await",[128,554,555],{"class":241}," readBody",[128,557,502],{"class":158},[128,559,560,562,564,566],{"class":130,"line":144},[128,561,401],{"class":147},[128,563,404],{"class":158},[128,565,407],{"class":147},[128,567,568],{"class":158},"corps?.titre) {\n",[128,570,571,573,575,577,580,582,585],{"class":130,"line":162},[128,572,415],{"class":147},[128,574,418],{"class":241},[128,576,421],{"class":158},[128,578,579],{"class":151},"400",[128,581,427],{"class":158},[128,583,584],{"class":174},"'Le titre est obligatoire'",[128,586,433],{"class":158},[128,588,589],{"class":130,"line":193},[128,590,438],{"class":158},[128,592,593],{"class":130,"line":219},[128,594,595],{"class":134},"  \u002F\u002F ... enregistrer la recette\n",[128,597,598,600,603,606],{"class":130,"line":225},[128,599,258],{"class":147},[128,601,602],{"class":158}," { ok: ",[128,604,605],{"class":151},"true",[128,607,216],{"class":158},[128,609,610,612],{"class":130,"line":232},[128,611,267],{"class":158},[128,613,270],{"class":245},[615,616,617],"caution",{},[35,618,619],{},"Ne fais jamais confiance aux données reçues : n'importe qui peut appeler ton API avec n'importe quel contenu. Vérifie chaque champ avant de l'enregistrer.",[10,621,623],{"id":622},"les-secrets-restent-sur-le-serveur","Les secrets restent sur le serveur",[35,625,626,627,630,631,307],{},"Le code de ",[39,628,629],{},"server\u002F"," n'est jamais envoyé au navigateur. C'est le seul endroit où utiliser une clé secrète (clé de base de données, clé d'un service payant). Déclare-la dans ",[39,632,633],{},"runtimeConfig",[119,635,638],{"className":121,"code":636,"filename":637,"language":123,"meta":124,"style":124},"export default defineNuxtConfig({\n  runtimeConfig: {\n    cleApi: '', \u002F\u002F secrète : lisible seulement côté serveur\n    public: {\n      nomSite: 'Mes recettes' \u002F\u002F publique : lisible partout\n    }\n  }\n})\n","nuxt.config.ts",[39,639,640,652,657,671,676,687,692,696],{"__ignoreMap":124},[128,641,642,644,646,649],{"class":130,"line":131},[128,643,235],{"class":147},[128,645,238],{"class":147},[128,647,648],{"class":241}," defineNuxtConfig",[128,650,651],{"class":245},"({\n",[128,653,654],{"class":130,"line":138},[128,655,656],{"class":158},"  runtimeConfig: {\n",[128,658,659,662,665,668],{"class":130,"line":144},[128,660,661],{"class":158},"    cleApi: ",[128,663,664],{"class":174},"''",[128,666,667],{"class":158},", ",[128,669,670],{"class":134},"\u002F\u002F secrète : lisible seulement côté serveur\n",[128,672,673],{"class":130,"line":162},[128,674,675],{"class":158},"    public: {\n",[128,677,678,681,684],{"class":130,"line":193},[128,679,680],{"class":158},"      nomSite: ",[128,682,683],{"class":174},"'Mes recettes'",[128,685,686],{"class":134}," \u002F\u002F publique : lisible partout\n",[128,688,689],{"class":130,"line":219},[128,690,691],{"class":158},"    }\n",[128,693,694],{"class":130,"line":225},[128,695,438],{"class":158},[128,697,698],{"class":130,"line":232},[128,699,700],{"class":245},"})\n",[35,702,703,704,707,708,711,712,285],{},"La vraie valeur se met dans le fichier ",[39,705,706],{},".env",", jamais dans le code. Le nom de la variable suit une règle : ",[39,709,710],{},"NUXT_"," puis le nom en majuscules, mots séparés par ",[39,713,714],{},"_",[119,716,720],{"className":717,"code":718,"filename":706,"language":719,"meta":124,"style":124},"language-bash shiki shiki-themes github-light-default github-dark-default github-dark-default","NUXT_CLE_API=ta-cle-secrete\n","bash",[39,721,722],{"__ignoreMap":124},[128,723,724,727,729],{"class":130,"line":131},[128,725,726],{"class":158},"NUXT_CLE_API",[128,728,496],{"class":147},[128,730,731],{"class":174},"ta-cle-secrete\n",[35,733,734,735,307],{},"Dans une route d'API, tu la lis avec ",[39,736,737],{},"useRuntimeConfig(event)",[119,739,741],{"className":121,"code":740,"language":123,"meta":124,"style":124},"export default defineEventHandler((event) => {\n  const config = useRuntimeConfig(event)\n  \u002F\u002F config.cleApi est disponible ici, jamais dans le navigateur\n})\n",[39,742,743,757,771,776],{"__ignoreMap":124},[128,744,745,747,749,751,753,755],{"class":130,"line":131},[128,746,235],{"class":147},[128,748,238],{"class":147},[128,750,242],{"class":241},[128,752,333],{"class":245},[128,754,249],{"class":147},[128,756,252],{"class":158},[128,758,759,761,764,766,769],{"class":130,"line":138},[128,760,342],{"class":147},[128,762,763],{"class":151}," config",[128,765,155],{"class":147},[128,767,768],{"class":241}," useRuntimeConfig",[128,770,502],{"class":158},[128,772,773],{"class":130,"line":144},[128,774,775],{"class":134},"  \u002F\u002F config.cleApi est disponible ici, jamais dans le navigateur\n",[128,777,778,780],{"class":130,"line":162},[128,779,267],{"class":158},[128,781,270],{"class":245},[783,784,785],"warning",{},[35,786,787,788,790,791,794],{},"Ajoute ",[39,789,706],{}," à ton ",[39,792,793],{},".gitignore"," : un secret poussé sur GitHub doit être considéré comme volé, il faut le changer.",[10,796,798],{"id":797},"à-retenir","À retenir",[15,800,801,814,827,830],{},[18,802,803,806,807,810,811,813],{},[39,804,805],{},"server\u002Fapi\u002Fnom.get.ts"," répond à ",[39,808,809],{},"GET \u002Fapi\u002Fnom"," ; ce que renvoie ",[39,812,116],{}," part en JSON.",[18,815,816,667,818,820,821,823,824,826],{},[39,817,306],{},[39,819,475],{}," et ",[39,822,510],{}," lisent la requête ; ",[39,825,456],{}," renvoie un code d'erreur.",[18,828,829],{},"Vérifie toujours les données reçues.",[18,831,832,833,820,835,837],{},"Les secrets vont dans ",[39,834,633],{},[39,836,706],{},", et se lisent seulement côté serveur.",[839,840,841],"style",{},"html pre.shiki code .sd6ml, html code.shiki .sd6ml{--shiki-light:#6E7781;--shiki-default:#8B949E;--shiki-dark:#8B949E}html pre.shiki code .sOnC1, html code.shiki .sOnC1{--shiki-light:#CF222E;--shiki-default:#FF7B72;--shiki-dark:#FF7B72}html pre.shiki code .s5iOY, html code.shiki .s5iOY{--shiki-light:#0550AE;--shiki-default:#79C0FF;--shiki-dark:#79C0FF}html pre.shiki code .scfrF, html code.shiki .scfrF{--shiki-light:#1F2328;--shiki-default:#E6EDF3;--shiki-dark:#E6EDF3}html pre.shiki code .shWOn, html code.shiki .shWOn{--shiki-light:#0A3069;--shiki-default:#A5D6FF;--shiki-dark:#A5D6FF}html pre.shiki code .syCW7, html code.shiki .syCW7{--shiki-light:#8250DF;--shiki-default:#D2A8FF;--shiki-dark:#D2A8FF}html pre.shiki code .sIXEE, html code.shiki .sIXEE{--shiki-light:#953800;--shiki-default:#FFA657;--shiki-dark:#FFA657}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":124,"searchDepth":138,"depth":144,"links":843},[844,845,846,847,848],{"id":12,"depth":138,"text":13},{"id":32,"depth":138,"text":33},{"id":288,"depth":138,"text":289},{"id":622,"depth":138,"text":623},{"id":797,"depth":138,"text":798},"Écrire une petite API dans server\u002Fapi : lire, créer, renvoyer des erreurs, et garder les secrets côté serveur.","md","nuxt",{},[854,855,116,306,510,475,456,633,856],"server\u002Fapi","API","Nitro","intermediaire","\u002Fnuxt\u002Froutes-api",[860],"\u002Fnuxt\u002Frecuperer-des-donnees",[862,866,869],{"nom":863,"url":864,"type":865,"anglais":228},"Documentation officielle de Nuxt","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fgetting-started\u002Fserver","doc",{"nom":867,"url":868,"type":865},"Méthodes de requête HTTP sur MDN","https:\u002F\u002Fdeveloper.mozilla.org\u002Ffr\u002Fdocs\u002FWeb\u002FHTTP\u002FReference\u002FMethods",{"nom":870,"url":871,"type":865},"Codes de réponse HTTP sur MDN","https:\u002F\u002Fdeveloper.mozilla.org\u002Ffr\u002Fdocs\u002FWeb\u002FHTTP\u002FReference\u002FStatus",{"title":5,"description":849},[874,876,879],{"nom":875,"url":864},"Le serveur dans Nuxt (en anglais)",{"nom":877,"url":878},"Dossier server (en anglais)","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fdirectory-structure\u002Fserver",{"nom":880,"url":881},"Configuration d'exécution (en anglais)","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fguide\u002Fgoing-further\u002Fruntime-config","nuxt\u002F06.routes-api","DLhn3-dbKA0zMBeQI6Y2TZYegwFKFR_hQ-GFF4Q1ksQ",[885,889,893,897,901,904,905,909],{"path":886,"title":887,"stem":888},"\u002Fnuxt\u002Fintroduction","Introduction à Nuxt","nuxt\u002F01.introduction",{"path":890,"title":891,"stem":892},"\u002Fnuxt\u002Fstructure","La structure d'un projet","nuxt\u002F02.structure",{"path":894,"title":895,"stem":896},"\u002Fnuxt\u002Fpages-et-routage","Pages et routage","nuxt\u002F03.pages-et-routage",{"path":898,"title":899,"stem":900},"\u002Fnuxt\u002Fcomposants","Les composants","nuxt\u002F04.composants",{"path":860,"title":902,"stem":903},"Récupérer des données","nuxt\u002F05.recuperer-des-donnees",{"path":858,"title":5,"stem":882},{"path":906,"title":907,"stem":908},"\u002Fnuxt\u002Fmodes-de-rendu","Les modes de rendu","nuxt\u002F07.modes-de-rendu",{"path":910,"title":911,"stem":912},"\u002Fnuxt","Nuxt","nuxt\u002Findex",[914,916],{"title":902,"path":860,"stem":903,"description":915,"children":-1},"Charger des données avec useFetch, gérer le chargement et les erreurs, et savoir quand utiliser $fetch.",{"title":907,"path":906,"stem":908,"description":917,"children":-1},"Universel, client, statique ou hybride : où la page est fabriquée, et comment choisir pour chaque partie du site.",[919],{"path":860,"title":902,"ligne":851},[921,923,924,925,926,927,928,929],{"path":886,"title":887,"niveau":922,"stem":888},"debutant",{"path":890,"title":891,"niveau":922,"stem":892},{"path":894,"title":895,"niveau":922,"stem":896},{"path":898,"title":899,"niveau":922,"stem":900},{"path":860,"title":902,"niveau":857,"stem":903},{"path":858,"title":5,"niveau":857,"stem":882},{"path":906,"title":907,"niveau":857,"stem":908},{"path":910,"title":911,"niveau":930,"stem":912},"memo",1794583440515]