[{"data":1,"prerenderedAt":297},["ShallowReactive",2],{"blog-\u002Fblog\u002F2024\u002Fpokedex-kilkadziesiat-zapytan-zamiast-tysiaca":3,"blog-project-\u002Fblog\u002F2024\u002Fpokedex-kilkadziesiat-zapytan-zamiast-tysiaca":184},{"id":4,"title":5,"art":6,"body":7,"date":166,"description":167,"draft":168,"extension":169,"image":170,"kind":171,"meta":172,"navigation":173,"ogImage":174,"path":175,"project":176,"seo":177,"slug":170,"stem":178,"tags":179,"__hash__":183},"blog_en\u002Fblog\u002F2024\u002Fpokedex-kilkadziesiat-zapytan-zamiast-tysiaca.md","Pokedex – a few dozen requests instead of more than a thousand","\u002Fart\u002Fblog\u002Fpokedex.svg",{"type":8,"value":9,"toc":157},"minimark",[10,14,17,22,25,28,41,53,57,75,78,85,89,92,107,110,114,117,128,132,146],[11,12,13],"p",{},"Pokedex was my first bigger project in JavaScript. I wrote it while I was doing a course – no framework, just my own classes. I wanted to build something harder than another to-do list, and something I couldn't find ready-made.",[11,15,16],{},"I chose Pokémon for a simple reason: they have been around for years, so someone had surely built an API for them. I was right – PokéAPI was the first search result. Someone else keeps the data up to date, and I could focus on the code.",[18,19,21],"h2",{"id":20},"the-problem-the-data-is-in-many-places","The problem: the data is in many places",[11,23,24],{},"In PokéAPI every Pokémon has its own address with all its data. The easiest way is one request per Pokémon – that's what most tutorials do with the first generation. With more than 1,000 species, that means more than 1,000 requests every time someone opens the page. I was worried it would put too much load on the server and the page would load slowly.",[11,26,27],{},"So I turned it around. Instead of asking every Pokémon about its type, I ask every type about its Pokémon:",[29,30,31,35,38],"ol",{},[32,33,34],"li",{},"the species list – one request gives the numbers and names of all Pokémon,",[32,36,37],{},"types – each type returns the list of Pokémon that have it,",[32,39,40],{},"generations – each generation returns the list of its species.",[11,42,43,44,48,49,52],{},"The type and generation requests run in parallel with ",[45,46,47],"code",{},"Promise.all",". There are also separate requests for names with a hyphen – the API returns e.g. ",[45,50,51],{},"mr-mime",", and I wanted \"Mr. Mime\" on the list. In total, it's a few dozen requests instead of more than a thousand.",[18,54,56],{"id":55},"a-small-database-in-the-browser","A small database in the browser",[11,58,59,60,63,64,63,67,70,71,74],{},"From these responses I build one object: Pokémon number → name, types and generation. The keys are one letter long (",[45,61,62],{},"n",", ",[45,65,66],{},"t",[45,68,69],{},"g","), because everything goes to ",[45,72,73],{},"localStorage",", where space is limited.",[11,76,77],{},"On the next visit, the page only asks the API how many species there are now. If the number matches the one in memory, the list loads from the browser – that's two requests instead of a few dozen. If new Pokémon appear in the API, the database rebuilds itself. That was my goal from the start: code that keeps working without me.",[11,79,80,81,84],{},"Height, weight and stats are loaded only when someone opens a Pokémon card. They go to ",[45,82,83],{},"sessionStorage",", so opening the same card again doesn't send another request.",[18,86,88],{"id":87},"always-one-more-idea","Always one more idea",[11,90,91],{},"A simple list wasn't enough for long. While I was writing the code, I kept wanting to add something new:",[93,94,95,98,101,104],"ul",{},[32,96,97],{},"marking caught Pokémon – the list is saved in the browser,",[32,99,100],{},"import and export of that list, so you can move it to another device or browser,",[32,102,103],{},"a light and a dark theme,",[32,105,106],{},"pagination with the page number in the address, so a link opens the same page.",[11,108,109],{},"I had more ideas than time. I often started the next feature before the previous one worked the way it should – and then I had to go back to several places at once.",[18,111,113],{"id":112},"the-mistakes-that-taught-me-the-most","The mistakes that taught me the most",[11,115,116],{},"At the beginning I got stuck in a loop that overloaded my computer more than once. Then came async code – I had to check where I was sending a request, what I got back and whether I needed that data at all. The hardest part was keeping it in sync with the DOM: the data is ready but the view isn't, or the other way round.",[11,118,119,120,123,124,127],{},"My main tools were ",[45,121,122],{},"console.log"," and ",[45,125,126],{},"console.trace",". Today I would more often pause the code in the debugger at the point where something happens – I didn't do that back then, and it would probably have saved me a lot of time.",[18,129,131],{"id":130},"what-i-would-do-differently-today","What I would do differently today",[93,133,134,137,140,143],{},[32,135,136],{},"Finish one feature before starting the next. I learned to work in small steps only after some time.",[32,138,139],{},"First look at how the API returns data and what I really need, and only then write code.",[32,141,142],{},"Maybe build the look first and the features later – I changed the design many times along the way.",[32,144,145],{},"Not everything has to be a class. Today I would write some parts as plain functions.",[11,147,148,149,156],{},"The project doesn't look big, but it was an important step for me. One day I want to rewrite it in Vue – same topic, new lesson. For now, ",[150,151,155],"a",{"href":152,"rel":153},"https:\u002F\u002Flukaszdzielo.github.io\u002Fpokedex\u002F",[154],"nofollow","Pokedex is live here",".",{"title":158,"searchDepth":159,"depth":159,"links":160},"",2,[161,162,163,164,165],{"id":20,"depth":159,"text":21},{"id":55,"depth":159,"text":56},{"id":87,"depth":159,"text":88},{"id":112,"depth":159,"text":113},{"id":130,"depth":159,"text":131},"2024-03-20","How I built a Pokedex for more than 1,000 Pokémon in plain JavaScript – with a planned order of PokéAPI requests and a small database in the browser.",false,"md",null,"post",{},true,"\u002Fog\u002Fblog\u002Fpokedex-en.jpg","\u002Fblog\u002F2024\u002Fpokedex-kilkadziesiat-zapytan-zamiast-tysiaca","\u002Fprojects\u002F2024\u002Fpokedex",{"title":5,"description":167},"blog\u002F2024\u002Fpokedex-kilkadziesiat-zapytan-zamiast-tysiaca",[180,181,182],"javascript","pokeapi","projekt","GmOcBAE-6QqJhApxHdJzPfVBOkb69KE1DAiwQukzlG8",{"id":185,"title":186,"agency":170,"body":187,"compare":170,"cover":242,"date":248,"description":249,"draft":168,"extension":169,"facts":250,"featured":168,"gallery":264,"galleryLead":170,"highlights":170,"image":280,"links":281,"meta":283,"navigation":173,"note":170,"ogImage":284,"path":176,"seo":285,"stack":286,"stem":292,"tags":293,"__hash__":296},"projects_en\u002Fprojects\u002F2024\u002Fpokedex\u002Findex.md","Pokedex",{"type":8,"value":188,"toc":239},[189,192,196],[11,190,191],{},"An interactive Pokémon encyclopedia covering more than 1,000 creatures – written in vanilla JavaScript (ES6+, OOP patterns), with no frameworks, using the public PokéAPI.",[18,193,195],{"id":194},"key-features","Key features",[93,197,198,203,214,230,233,236],{},[32,199,200,201],{},"A planned loading order for PokéAPI data – parallel requests with ",[45,202,47],{},[32,204,205,206,209,210,213],{},"A local database in the browser (",[45,207,208],{},"LocalStorage"," + ",[45,211,212],{},"SessionStorage",") – data is fetched from the API only once",[32,215,216,217,63,220,63,223,63,226,229],{},"A modular class architecture (",[45,218,219],{},"DataManager",[45,221,222],{},"DataBuilder",[45,224,225],{},"StorageManager",[45,227,228],{},"PokemonDialog",") ready for a migration to Vue 3",[32,231,232],{},"Responsive pagination synced in both directions, with its state reflected in the URL",[32,234,235],{},"Light and dark theme (also automatic, following the system setting)",[32,237,238],{},"Caught Pokémon – you can mark them in the Pokémon card, and import or export the list in the settings",{"title":158,"searchDepth":159,"depth":159,"links":240},[241],{"id":194,"depth":159,"text":195},{"desktop":243,"phone":244,"width":245,"height":246,"alt":247},"\u002Fprojects\u002F2024\u002Fpokedex\u002Flist.webp","\u002Fprojects\u002F2024\u002Fpokedex\u002Fphone.webp",1920,1105,"Pokedex – the Pokémon list with pagination","2024-01-28","An interactive Pokedex covering more than 1,000 Pokémon, written in vanilla JavaScript (OOP) on top of PokéAPI – with a planned data loading order and a local database in the browser.",[251,254,257,261],{"value":252,"text":253},"2024","year built",{"value":255,"text":256},"Solo","a personal project – from the idea to the release",{"value":258,"suffix":259,"text":260},"1000","+","Pokémon from PokéAPI – a few dozen requests instead of more than 1,000",{"value":262,"text":263},"Vanilla JS","no Vue, React or other frameworks – my own classes in plain JavaScript",[265,268,272,276],{"src":243,"width":245,"height":246,"alt":266,"caption":267},"The Pokémon list with pagination","The list – more than 1,000 Pokémon, with the page number saved in the URL (?p=)",{"src":244,"device":269,"alt":270,"caption":271},"phone","Pokedex on a phone – a Pokémon card","Phone – a Pokémon card open over the two-column list",{"src":273,"width":245,"height":246,"alt":274,"caption":275},"\u002Fprojects\u002F2024\u002Fpokedex\u002Fpokemon-card.webp","The Pokémon details window","Pokémon card – name, types and generation come straight from memory, stats are loaded from the API",{"src":277,"width":245,"height":246,"alt":278,"caption":279},"\u002Fprojects\u002F2024\u002Fpokedex\u002Fsettings.webp","App settings","Settings – theme, import and export of caught Pokémon, clearing the data","\u002Fprojects\u002F2024\u002Fpokedex\u002Fthumbnail.webp",{"live":152,"github":282},"https:\u002F\u002Fgithub.com\u002Flukaszdzielo\u002Fpokedex",{},"\u002Fog\u002Fprojects\u002Fpokedex-en.jpg",{"title":186,"description":249},[287,288,289,208,290,291],"JavaScript (ES6+)","OOP","PokéAPI","SCSS","no frameworks","projects\u002F2024\u002Fpokedex\u002Findex",[180,294,295,181],"vanilla-js","oop","KPcGRP16upHcMky2GZKwhzNMX12SKwpZNRU4EIegoUA",1791981881353]