[{"data":1,"prerenderedAt":1207},["ShallowReactive",2],{"navigation":3,"/blog/fetch-api":50},[4],{"title":5,"path":6,"stem":7,"children":8,"page":49},"Blog","/blog","blog",[9,13,17,21,25,29,33,37,41,45],{"title":10,"path":11,"stem":12},"Unlocking the Power of Django: A Developer-Friendly Architecture Guide","/blog/django-arhitecture","blog/01.django-arhitecture",{"title":14,"path":15,"stem":16},"Getting Started with Go: Why You Should Learn Golang","/blog/intro-go","blog/02.intro-go",{"title":18,"path":19,"stem":20},"Recursion in JavaScript: The Art of Functions Calling Themselves","/blog/recursion-js","blog/03.recursion-js",{"title":22,"path":23,"stem":24},"What Go Got Right and Wrong: A Thoughtful Look Back","/blog/golang-pro-and-cons","blog/04.golang-pro-and-cons",{"title":26,"path":27,"stem":28},"JavaScript Performance Optimization: A Practical Guide for Better Web Applications","/blog/js-perf","blog/05.js-perf",{"title":30,"path":31,"stem":32},"Understanding Go Slices: A Powerful Data Structure","/blog/go-slices-article","blog/06.go-slices-article",{"title":34,"path":35,"stem":36},"The Model Context Protocol (MCP): Giving AI \"Hands\"","/blog/mcp-article-complete","blog/07.mcp-article-complete",{"title":38,"path":39,"stem":40},"CSS Grid Mastery: Advanced Layout Techniques for Intermediate Developers","/blog/css-grid","blog/08.css-grid",{"title":42,"path":43,"stem":44},"Modern JavaScript: Mastering the Fetch API with Async/Await","/blog/fetch-api","blog/09.fetch-api",{"title":46,"path":47,"stem":48},"How to Use AWS for Cloud-Based Web Development","/blog/aws-webdev","blog/10.aws-webdev",false,[51,1202],{"id":52,"title":42,"author":53,"badge":59,"body":61,"date":1189,"description":1190,"extension":1191,"image":1192,"meta":1193,"navigation":571,"path":43,"readingTime":1194,"seo":1199,"stem":44,"tags":1200,"__hash__":1201},"blog/blog/09.fetch-api.md",[54],{"name":55,"to":56,"avatar":57},"Norbert Br3tt","https://www.linkedin.com/in/norbert-brett/",{"src":58},"https://res.cloudinary.com/nbrett/image/upload/v1725625689/IMG_0698_d0nhun.jpg",{"label":60},"JavaScript",{"type":62,"value":63,"toc":1179},"minimark",[64,69,78,90,93,98,101,114,118,121,152,161,165,168,289,295,434,440,453,457,479,482,497,507,511,514,640,649,653,870,883,887,890,1143,1150,1154,1165,1172,1175],[65,66,68],"h1",{"id":67},"understanding-asyncawait-without-the-headache","Understanding Async/Await (Without the Headache)",[70,71,72,73,77],"p",{},"Here's a thing that happened to me: I spent six months writing ",[74,75,76],"code",{},".then().catch()"," chains that I didn't fully trust. They worked. Mostly. But every time I wrote one, I held my breath a little.",[70,79,80,81,84,85,89],{},"Then I rewrote one of those chains using ",[74,82,83],{},"async/await",", read it back, and it was just... ",[86,87,88],"em",{},"sentences",". It read like English. I've personally never gone back.",[70,91,92],{},"Let me show you what's happening under the hood — and more importantly, how to write it in a way that feels natural rather than like a puzzle you're solving.",[94,95,97],"h2",{"id":96},"why-this-matters","Why This Matters",[70,99,100],{},"JavaScript is single-threaded, which means it can only do one thing at a time. But fetching data from an API, reading a file, or waiting for a timer shouldn't freeze your whole app. That's what asynchronous code is for — it says \"start this, and come back when it's done.\"",[70,102,103,105,106,109,110,113],{},[74,104,83],{}," is syntax built on top of Promises. It doesn't change ",[86,107,108],{},"what"," JavaScript does; it changes ",[86,111,112],{},"how you read and write it",".",[94,115,117],{"id":116},"before-you-do-anything","Before You Do Anything",[70,119,120],{},"A quick vocabulary check:",[122,123,124,132,140],"ul",{},[125,126,127,131],"li",{},[128,129,130],"strong",{},"Promise"," — an object representing a value that isn't available yet",[125,133,134,139],{},[128,135,136],{},[74,137,138],{},"async"," — marks a function as asynchronous; it will always return a Promise",[125,141,142,147,148,151],{},[128,143,144],{},[74,145,146],{},"await"," — pauses execution ",[86,149,150],{},"inside"," an async function until the Promise resolves",[70,153,154,155,157,158,160],{},"You can only use ",[74,156,146],{}," inside an ",[74,159,138],{}," function. That's the one rule to internalize.",[94,162,164],{"id":163},"lets-fetch-some-data","Let's Fetch Some Data",[70,166,167],{},"Here's the old Promise chain way:",[169,170,175],"pre",{"className":171,"code":172,"language":173,"meta":174,"style":174},"language-javascript shiki shiki-themes vitesse-dark","fetch('https://api.example.com/users')\n  .then(response => response.json())\n  .then(data => console.log(data))\n  .catch(error => console.error(error));\n","javascript","",[74,176,177,203,232,261],{"__ignoreMap":174},[178,179,182,186,190,194,198,200],"span",{"class":180,"line":181},"line",1,[178,183,185],{"class":184},"sCK9x","fetch",[178,187,189],{"class":188},"s_pn2","(",[178,191,193],{"class":192},"sNJcY","'",[178,195,197],{"class":196},"s7rlk","https://api.example.com/users",[178,199,193],{"class":192},[178,201,202],{"class":188},")\n",[178,204,206,209,212,214,218,221,224,226,229],{"class":180,"line":205},2,[178,207,208],{"class":188},"  .",[178,210,211],{"class":184},"then",[178,213,189],{"class":188},[178,215,217],{"class":216},"st-jp","response",[178,219,220],{"class":188}," =>",[178,222,223],{"class":216}," response",[178,225,113],{"class":188},[178,227,228],{"class":184},"json",[178,230,231],{"class":188},"())\n",[178,233,235,237,239,241,244,246,249,251,254,256,258],{"class":180,"line":234},3,[178,236,208],{"class":188},[178,238,211],{"class":184},[178,240,189],{"class":188},[178,242,243],{"class":216},"data",[178,245,220],{"class":188},[178,247,248],{"class":216}," console",[178,250,113],{"class":188},[178,252,253],{"class":184},"log",[178,255,189],{"class":188},[178,257,243],{"class":216},[178,259,260],{"class":188},"))\n",[178,262,264,266,269,271,274,276,278,280,282,284,286],{"class":180,"line":263},4,[178,265,208],{"class":188},[178,267,268],{"class":184},"catch",[178,270,189],{"class":188},[178,272,273],{"class":216},"error",[178,275,220],{"class":188},[178,277,248],{"class":216},[178,279,113],{"class":188},[178,281,273],{"class":184},[178,283,189],{"class":188},[178,285,273],{"class":216},[178,287,288],{"class":188},"));\n",[70,290,291,292,294],{},"It works. But nesting gets messy fast. Here's the same thing with ",[74,293,83],{},":",[169,296,298],{"className":171,"code":297,"language":173,"meta":174,"style":174},"async function getUsers() {\n  try {\n    const response = await fetch('https://api.example.com/users');\n    const data = await response.json();\n    console.log(data);\n  } catch (error) {\n    console.error(error);\n  }\n}\n",[74,299,300,317,325,352,372,388,407,422,428],{"__ignoreMap":174},[178,301,302,305,308,311,314],{"class":180,"line":181},[178,303,138],{"class":304},"s_wWq",[178,306,307],{"class":304}," function",[178,309,310],{"class":184}," getUsers",[178,312,313],{"class":188},"()",[178,315,316],{"class":188}," {\n",[178,318,319,323],{"class":180,"line":205},[178,320,322],{"class":321},"s3QIE","  try",[178,324,316],{"class":188},[178,326,327,330,332,335,338,341,343,345,347,349],{"class":180,"line":234},[178,328,329],{"class":304},"    const",[178,331,223],{"class":216},[178,333,334],{"class":188}," =",[178,336,337],{"class":321}," await",[178,339,340],{"class":184}," fetch",[178,342,189],{"class":188},[178,344,193],{"class":192},[178,346,197],{"class":196},[178,348,193],{"class":192},[178,350,351],{"class":188},");\n",[178,353,354,356,359,361,363,365,367,369],{"class":180,"line":263},[178,355,329],{"class":304},[178,357,358],{"class":216}," data",[178,360,334],{"class":188},[178,362,337],{"class":321},[178,364,223],{"class":216},[178,366,113],{"class":188},[178,368,228],{"class":184},[178,370,371],{"class":188},"();\n",[178,373,375,378,380,382,384,386],{"class":180,"line":374},5,[178,376,377],{"class":216},"    console",[178,379,113],{"class":188},[178,381,253],{"class":184},[178,383,189],{"class":188},[178,385,243],{"class":216},[178,387,351],{"class":188},[178,389,391,394,397,400,402,405],{"class":180,"line":390},6,[178,392,393],{"class":188},"  }",[178,395,396],{"class":321}," catch",[178,398,399],{"class":188}," (",[178,401,273],{"class":216},[178,403,404],{"class":188},")",[178,406,316],{"class":188},[178,408,410,412,414,416,418,420],{"class":180,"line":409},7,[178,411,377],{"class":216},[178,413,113],{"class":188},[178,415,273],{"class":184},[178,417,189],{"class":188},[178,419,273],{"class":216},[178,421,351],{"class":188},[178,423,425],{"class":180,"line":424},8,[178,426,427],{"class":188},"  }\n",[178,429,431],{"class":180,"line":430},9,[178,432,433],{"class":188},"}\n",[70,435,436,437,439],{},"Notice how it reads top-to-bottom, like synchronous code. The ",[74,438,146],{}," keyword tells JavaScript: \"hold here until this Promise resolves, then give me the value.\"",[70,441,442,443,446,447,449,450,113],{},"That ",[74,444,445],{},"try/catch"," block is handling errors — the ",[74,448,83],{}," equivalent of ",[74,451,452],{},".catch()",[94,454,456],{"id":455},"the-part-that-trips-people-up-two-awaits","The Part That Trips People Up: Two Awaits",[70,458,459,460,462,463,466,467,470,471,474,475,478],{},"See those two ",[74,461,146],{}," calls? ",[74,464,465],{},"fetch()"," returns a Promise for the ",[86,468,469],{},"response object"," — not the data. You have to call ",[74,472,473],{},".json()"," on it, which is ",[86,476,477],{},"also"," asynchronous.",[70,480,481],{},"So:",[483,484,485,491],"ol",{},[125,486,487,490],{},[74,488,489],{},"await fetch(url)"," → gives you the Response",[125,492,493,496],{},[74,494,495],{},"await response.json()"," → gives you the actual parsed data",[70,498,499,500,502,503,506],{},"I personally got burned by this the first time. You'll forget the second ",[74,501,146],{}," once, see ",[74,504,505],{},"[object Promise]"," in your console, and never forget again. That's fine — it's a rite of passage. 😄",[94,508,510],{"id":509},"running-multiple-requests-in-parallel","Running Multiple Requests in Parallel",[70,512,513],{},"One common mistake: awaiting requests one at a time when they don't depend on each other.",[169,515,517],{"className":171,"code":516,"language":173,"meta":174,"style":174},"// Slow — sequential, each waits for the previous\nconst user = await fetchUser(id);\nconst posts = await fetchPosts(id);\n\n// Fast — parallel, both start at once\nconst [user, posts] = await Promise.all([\n  fetchUser(id),\n  fetchPosts(id)\n]);\n",[74,518,519,525,547,567,573,578,612,624,635],{"__ignoreMap":174},[178,520,521],{"class":180,"line":181},[178,522,524],{"class":523},"sux-A","// Slow — sequential, each waits for the previous\n",[178,526,527,530,533,535,537,540,542,545],{"class":180,"line":205},[178,528,529],{"class":304},"const",[178,531,532],{"class":216}," user",[178,534,334],{"class":188},[178,536,337],{"class":321},[178,538,539],{"class":184}," fetchUser",[178,541,189],{"class":188},[178,543,544],{"class":216},"id",[178,546,351],{"class":188},[178,548,549,551,554,556,558,561,563,565],{"class":180,"line":234},[178,550,529],{"class":304},[178,552,553],{"class":216}," posts",[178,555,334],{"class":188},[178,557,337],{"class":321},[178,559,560],{"class":184}," fetchPosts",[178,562,189],{"class":188},[178,564,544],{"class":216},[178,566,351],{"class":188},[178,568,569],{"class":180,"line":263},[178,570,572],{"emptyLinePlaceholder":571},true,"\n",[178,574,575],{"class":180,"line":374},[178,576,577],{"class":523},"// Fast — parallel, both start at once\n",[178,579,580,582,585,588,591,593,596,598,600,604,606,609],{"class":180,"line":390},[178,581,529],{"class":304},[178,583,584],{"class":188}," [",[178,586,587],{"class":216},"user",[178,589,590],{"class":188},",",[178,592,553],{"class":216},[178,594,595],{"class":188},"]",[178,597,334],{"class":188},[178,599,337],{"class":321},[178,601,603],{"class":602},"sm68I"," Promise",[178,605,113],{"class":188},[178,607,608],{"class":184},"all",[178,610,611],{"class":188},"([\n",[178,613,614,617,619,621],{"class":180,"line":409},[178,615,616],{"class":184},"  fetchUser",[178,618,189],{"class":188},[178,620,544],{"class":216},[178,622,623],{"class":188},"),\n",[178,625,626,629,631,633],{"class":180,"line":424},[178,627,628],{"class":184},"  fetchPosts",[178,630,189],{"class":188},[178,632,544],{"class":216},[178,634,202],{"class":188},[178,636,637],{"class":180,"line":430},[178,638,639],{"class":188},"]);\n",[70,641,642,645,646,648],{},[74,643,644],{},"Promise.all"," takes an array of Promises and resolves when ",[86,647,608],{}," of them finish. If the requests are independent, this is almost always what you want. I use it constantly.",[94,650,652],{"id":651},"error-handling-dont-skip-this","Error Handling: Don't Skip This",[169,654,656],{"className":171,"code":655,"language":173,"meta":174,"style":174},"async function getUser(id) {\n  try {\n    const response = await fetch(`/api/users/${id}`);\n    \n    // fetch() only rejects on network failure — not HTTP errors!\n    if (!response.ok) {\n      throw new Error(`HTTP error: ${response.status}`);\n    }\n    \n    return await response.json();\n  } catch (error) {\n    // Handle gracefully — don't just swallow the error\n    console.error('Failed to fetch user:', error);\n    return null;\n  }\n}\n",[74,657,658,675,681,713,719,724,745,778,783,787,803,818,824,849,860,865],{"__ignoreMap":174},[178,659,660,662,664,667,669,671,673],{"class":180,"line":181},[178,661,138],{"class":304},[178,663,307],{"class":304},[178,665,666],{"class":184}," getUser",[178,668,189],{"class":188},[178,670,544],{"class":216},[178,672,404],{"class":188},[178,674,316],{"class":188},[178,676,677,679],{"class":180,"line":205},[178,678,322],{"class":321},[178,680,316],{"class":188},[178,682,683,685,687,689,691,693,695,698,701,704,706,709,711],{"class":180,"line":234},[178,684,329],{"class":304},[178,686,223],{"class":216},[178,688,334],{"class":188},[178,690,337],{"class":321},[178,692,340],{"class":184},[178,694,189],{"class":188},[178,696,697],{"class":192},"`",[178,699,700],{"class":196},"/api/users/",[178,702,703],{"class":321},"${",[178,705,544],{"class":196},[178,707,708],{"class":321},"}",[178,710,697],{"class":192},[178,712,351],{"class":188},[178,714,715],{"class":180,"line":263},[178,716,718],{"class":717},"sNpkn","    \n",[178,720,721],{"class":180,"line":374},[178,722,723],{"class":523},"    // fetch() only rejects on network failure — not HTTP errors!\n",[178,725,726,729,731,734,736,738,741,743],{"class":180,"line":390},[178,727,728],{"class":321},"    if",[178,730,399],{"class":188},[178,732,733],{"class":304},"!",[178,735,217],{"class":216},[178,737,113],{"class":188},[178,739,740],{"class":216},"ok",[178,742,404],{"class":188},[178,744,316],{"class":188},[178,746,747,750,753,756,758,760,763,765,767,769,772,774,776],{"class":180,"line":409},[178,748,749],{"class":321},"      throw",[178,751,752],{"class":304}," new",[178,754,755],{"class":184}," Error",[178,757,189],{"class":188},[178,759,697],{"class":192},[178,761,762],{"class":196},"HTTP error: ",[178,764,703],{"class":321},[178,766,217],{"class":196},[178,768,113],{"class":188},[178,770,771],{"class":196},"status",[178,773,708],{"class":321},[178,775,697],{"class":192},[178,777,351],{"class":188},[178,779,780],{"class":180,"line":424},[178,781,782],{"class":188},"    }\n",[178,784,785],{"class":180,"line":430},[178,786,718],{"class":717},[178,788,790,793,795,797,799,801],{"class":180,"line":789},10,[178,791,792],{"class":321},"    return",[178,794,337],{"class":321},[178,796,223],{"class":216},[178,798,113],{"class":188},[178,800,228],{"class":184},[178,802,371],{"class":188},[178,804,806,808,810,812,814,816],{"class":180,"line":805},11,[178,807,393],{"class":188},[178,809,396],{"class":321},[178,811,399],{"class":188},[178,813,273],{"class":216},[178,815,404],{"class":188},[178,817,316],{"class":188},[178,819,821],{"class":180,"line":820},12,[178,822,823],{"class":523},"    // Handle gracefully — don't just swallow the error\n",[178,825,827,829,831,833,835,837,840,842,844,847],{"class":180,"line":826},13,[178,828,377],{"class":216},[178,830,113],{"class":188},[178,832,273],{"class":184},[178,834,189],{"class":188},[178,836,193],{"class":192},[178,838,839],{"class":196},"Failed to fetch user:",[178,841,193],{"class":192},[178,843,590],{"class":188},[178,845,846],{"class":216}," error",[178,848,351],{"class":188},[178,850,852,854,857],{"class":180,"line":851},14,[178,853,792],{"class":321},[178,855,856],{"class":304}," null",[178,858,859],{"class":188},";\n",[178,861,863],{"class":180,"line":862},15,[178,864,427],{"class":188},[178,866,868],{"class":180,"line":867},16,[178,869,433],{"class":188},[70,871,872,873,875,876,879,880,113],{},"Watch out for this: ",[74,874,465],{}," only throws on ",[86,877,878],{},"network"," failures (no connection, DNS error). A 404 or 500 response is still a \"successful\" fetch as far as JavaScript is concerned. Always check ",[74,881,882],{},"response.ok",[94,884,886],{"id":885},"a-real-world-pattern","A Real-World Pattern",[70,888,889],{},"Here's a pattern I reach for all the time when building UI components:",[169,891,893],{"className":171,"code":892,"language":173,"meta":174,"style":174},"async function loadDashboard() {\n  const loadingEl = document.getElementById('loading');\n  const contentEl = document.getElementById('content');\n  \n  loadingEl.hidden = false;\n  \n  try {\n    const [user, stats] = await Promise.all([\n      fetchUser(),\n      fetchStats()\n    ]);\n    \n    renderDashboard(user, stats);\n    contentEl.hidden = false;\n  } catch (error) {\n    showErrorState(error);\n  } finally {\n    loadingEl.hidden = true; // always hide the loader\n  }\n}\n",[74,894,895,908,937,963,968,985,989,995,1022,1030,1038,1043,1047,1062,1077,1091,1102,1112,1133,1138],{"__ignoreMap":174},[178,896,897,899,901,904,906],{"class":180,"line":181},[178,898,138],{"class":304},[178,900,307],{"class":304},[178,902,903],{"class":184}," loadDashboard",[178,905,313],{"class":188},[178,907,316],{"class":188},[178,909,910,913,916,918,921,923,926,928,930,933,935],{"class":180,"line":205},[178,911,912],{"class":304},"  const",[178,914,915],{"class":216}," loadingEl",[178,917,334],{"class":188},[178,919,920],{"class":216}," document",[178,922,113],{"class":188},[178,924,925],{"class":184},"getElementById",[178,927,189],{"class":188},[178,929,193],{"class":192},[178,931,932],{"class":196},"loading",[178,934,193],{"class":192},[178,936,351],{"class":188},[178,938,939,941,944,946,948,950,952,954,956,959,961],{"class":180,"line":234},[178,940,912],{"class":304},[178,942,943],{"class":216}," contentEl",[178,945,334],{"class":188},[178,947,920],{"class":216},[178,949,113],{"class":188},[178,951,925],{"class":184},[178,953,189],{"class":188},[178,955,193],{"class":192},[178,957,958],{"class":196},"content",[178,960,193],{"class":192},[178,962,351],{"class":188},[178,964,965],{"class":180,"line":263},[178,966,967],{"class":717},"  \n",[178,969,970,973,975,978,980,983],{"class":180,"line":374},[178,971,972],{"class":216},"  loadingEl",[178,974,113],{"class":188},[178,976,977],{"class":216},"hidden",[178,979,334],{"class":188},[178,981,982],{"class":321}," false",[178,984,859],{"class":188},[178,986,987],{"class":180,"line":390},[178,988,967],{"class":717},[178,990,991,993],{"class":180,"line":409},[178,992,322],{"class":321},[178,994,316],{"class":188},[178,996,997,999,1001,1003,1005,1008,1010,1012,1014,1016,1018,1020],{"class":180,"line":424},[178,998,329],{"class":304},[178,1000,584],{"class":188},[178,1002,587],{"class":216},[178,1004,590],{"class":188},[178,1006,1007],{"class":216}," stats",[178,1009,595],{"class":188},[178,1011,334],{"class":188},[178,1013,337],{"class":321},[178,1015,603],{"class":602},[178,1017,113],{"class":188},[178,1019,608],{"class":184},[178,1021,611],{"class":188},[178,1023,1024,1027],{"class":180,"line":430},[178,1025,1026],{"class":184},"      fetchUser",[178,1028,1029],{"class":188},"(),\n",[178,1031,1032,1035],{"class":180,"line":789},[178,1033,1034],{"class":184},"      fetchStats",[178,1036,1037],{"class":188},"()\n",[178,1039,1040],{"class":180,"line":805},[178,1041,1042],{"class":188},"    ]);\n",[178,1044,1045],{"class":180,"line":820},[178,1046,718],{"class":717},[178,1048,1049,1052,1054,1056,1058,1060],{"class":180,"line":826},[178,1050,1051],{"class":184},"    renderDashboard",[178,1053,189],{"class":188},[178,1055,587],{"class":216},[178,1057,590],{"class":188},[178,1059,1007],{"class":216},[178,1061,351],{"class":188},[178,1063,1064,1067,1069,1071,1073,1075],{"class":180,"line":851},[178,1065,1066],{"class":216},"    contentEl",[178,1068,113],{"class":188},[178,1070,977],{"class":216},[178,1072,334],{"class":188},[178,1074,982],{"class":321},[178,1076,859],{"class":188},[178,1078,1079,1081,1083,1085,1087,1089],{"class":180,"line":862},[178,1080,393],{"class":188},[178,1082,396],{"class":321},[178,1084,399],{"class":188},[178,1086,273],{"class":216},[178,1088,404],{"class":188},[178,1090,316],{"class":188},[178,1092,1093,1096,1098,1100],{"class":180,"line":867},[178,1094,1095],{"class":184},"    showErrorState",[178,1097,189],{"class":188},[178,1099,273],{"class":216},[178,1101,351],{"class":188},[178,1103,1105,1107,1110],{"class":180,"line":1104},17,[178,1106,393],{"class":188},[178,1108,1109],{"class":321}," finally",[178,1111,316],{"class":188},[178,1113,1115,1118,1120,1122,1124,1127,1130],{"class":180,"line":1114},18,[178,1116,1117],{"class":216},"    loadingEl",[178,1119,113],{"class":188},[178,1121,977],{"class":216},[178,1123,334],{"class":188},[178,1125,1126],{"class":321}," true",[178,1128,1129],{"class":188},";",[178,1131,1132],{"class":523}," // always hide the loader\n",[178,1134,1136],{"class":180,"line":1135},19,[178,1137,427],{"class":188},[178,1139,1141],{"class":180,"line":1140},20,[178,1142,433],{"class":188},[70,1144,1145,1146,1149],{},"The ",[74,1147,1148],{},"finally"," block runs regardless of success or failure — perfect for hiding loading states. Find what works for your project, but I've landed on this shape for most data-fetching scenarios.",[94,1151,1153],{"id":1152},"go-make-something-that-talks-to-an-api","Go Make Something That Talks to an API",[70,1155,1156,1157,1164],{},"Start small: find a free public API (the ",[1158,1159,1163],"a",{"href":1160,"rel":1161},"https://jsonplaceholder.typicode.com/",[1162],"nofollow","JSONPlaceholder"," mock API is great for practice), fetch some data, render it to the page.",[70,1166,1167,1168,1171],{},"Once async/await clicks, you'll wonder how you managed without it. It's one of those tools that doesn't just make code shorter — it makes it ",[86,1169,1170],{},"thinkable",". And that's worth a lot.",[70,1173,1174],{},"You've got this. Go fetch something. 🎉",[1176,1177,1178],"style",{},"html pre.shiki code .sCK9x, html code.shiki .sCK9x{--shiki-default:#80A665}html pre.shiki code .s_pn2, html code.shiki .s_pn2{--shiki-default:#666666}html pre.shiki code .sNJcY, html code.shiki .sNJcY{--shiki-default:#C98A7D77}html pre.shiki code .s7rlk, html code.shiki .s7rlk{--shiki-default:#C98A7D}html pre.shiki code .st-jp, html code.shiki .st-jp{--shiki-default:#BD976A}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 pre.shiki code .s_wWq, html code.shiki .s_wWq{--shiki-default:#CB7676}html pre.shiki code .s3QIE, html code.shiki .s3QIE{--shiki-default:#4D9375}html pre.shiki code .sux-A, html code.shiki .sux-A{--shiki-default:#758575DD}html pre.shiki code .sm68I, html code.shiki .sm68I{--shiki-default:#B8A965}html pre.shiki code .sNpkn, html code.shiki .sNpkn{--shiki-default:#DBD7CAEE}",{"title":174,"searchDepth":205,"depth":205,"links":1180},[1181,1182,1183,1184,1185,1186,1187,1188],{"id":96,"depth":205,"text":97},{"id":116,"depth":205,"text":117},{"id":163,"depth":205,"text":164},{"id":455,"depth":205,"text":456},{"id":509,"depth":205,"text":510},{"id":651,"depth":205,"text":652},{"id":885,"depth":205,"text":886},{"id":1152,"depth":205,"text":1153},"2026-01-10T00:00:00.000Z","Learn how to make clean, readable HTTP requests in JavaScript using the Fetch API and async/await. From basic GET requests to error handling, authentication, and real-world patterns.","md","https://res.cloudinary.com/nbrett/image/upload/v1772835045/portfolio/fetch_api_hero_zz3ed8.svg",{},{"text":1195,"minutes":1196,"time":1197,"words":1198},"4 min read",3.49,209400,698,{"title":42,"description":1190},null,"Xkn0Q9Iil6czTOpg9A0VIfO6dl7HAp_nwdO5XN2mVGk",[1203,1205],{"title":38,"path":39,"stem":40,"description":1204,"children":-1},"Take your CSS Grid skills to the next level. Learn advanced techniques like grid-template-areas, auto-fit, minmax, and responsive patterns that make complex layouts simple and maintainable.",{"title":46,"path":47,"stem":48,"description":1206,"children":-1},"A practical, no-fluff guide to deploying real web apps on AWS — from S3 static hosting to EC2, RDS, and beyond. Learn the services that actually matter and how they fit together.",1774167726533]