je fais du json relationnel pour gérer une bd : 
{
  "personnages": [
    {
      "id": 1,
      "nom": "Ahri",
      "image": "https://ddragon.leagueoflegends.com/cdn/img/champion/splash/Ahri_0.jpg",
      "role": "Mage / Assassin",
      "niveau" : 1
    },
    {
      "id": 2,
      "nom": "Yasuo",
      "image" : "https://ddragon.leagueoflegends.com/cdn/img/champion/splash/Yasuo_0.jpg",
      "role": "Combattant / Assassin",
      "niveau" : 2
    },
    {
      "id": 3,
      "nom": "Lux",
      "image" : "https://ddragon.leagueoflegends.com/cdn/img/champion/splash/Lux_0.jpg",
      "role": "Mage / Support",
      "niveau" : 0
    },
    {
      "id": 4,
      "nom": "Garen",
      "image" : "https://ddragon.leagueoflegends.com/cdn/img/champion/splash/Garen_0.jpg",
      "role": "Combattant / Tank",
      "niveau" : 0
    },
    {
      "id": 5,
      "nom": "Jinx",
      "image" : "https://ddragon.leagueoflegends.com/cdn/img/champion/splash/Jinx_0.jpg",
      "role": "Tireur / Assassin",
      "niveau" : 0
    }
  ],
  "skins": [
    { "id": 1, "image" : "https://i.ytimg.com/vi/Cwwt0YlZoWw/maxresdefault.jpg", "nom": "Ahri Dynastique", "rarete": "Rare", "id_personnage": 1, "niveau": 1 },
    { "id": 2, "image" : "https://fbi.cults3d.com/uploaders/25822624/illustration-file/bc3e3dc9-3f59-4012-b99f-0a3d17737c15/kda_ahr.webp", "nom": "Ahri K/DA", "rarete": "Épique", "id_personnage": 1, "niveau": 2 },
    { "id": 3, "image":"https://prod.api.assets.riotgames.com/public/v1/asset/lol/14.5.1/CHAMPION_SKIN/157001/SPLASH?width=1920&height=1080&format=auto&auto=webp", "nom": "Yasuo Épéiste du Vent", "rarete": "Rare", "id_personnage": 2,  "niveau": 1},
    { "id": 4, "image" : "https://prod.api.assets.riotgames.com/public/v1/asset/lol/14.5.1/CHAMPION_SKIN/157010/SPLASH", "nom": "Yasuo Odysée", "rarete": "Légendaire", "id_personnage": 2, "niveau": 2},
    { "id": 5, "image": "https://images.contentstack.io/v3/assets/blt187521ff0727be24/blt4deb96de5ea056c8/60ee1256fdc6795ca58484fe/lux-splash.jpg", "nom": "Lux Gardienne des Etoiles", "rarete": "Épique", "id_personnage": 3, "niveau": 1 },
    { "id": 6, "image" : "https://loltracker.com/images/easyblog_shared/articles_philidia/CyclePBE623/Lux_Splash_7.jpg", "nom": "Lux Élémentaliste", "rarete": "Ultime", "id_personnage": 3, "niveau": 2},
    { "id": 7, "image" : "https://static1.millenium.org/entity_articles/9/17/19/@/1464097-garen-4-article_m-1.jpg", "nom": "Garen Chevalier du Royaume", "rarete": "Rare", "id_personnage": 4, "niveau": 1 },
    { "id": 8, "image" :"https://static1.millenium.org/articles/3/30/12/43/@/550739-dfv-q7-wkaewzf1-article_m-1.jpg", "nom": "Garen Dieu Roi", "rarete": "Légendaire", "id_personnage": 4, "niveau": 2 },
    { "id": 9, "image" : "https://static1.millenium.org/entity_articles/9/18/99/@/1470839-jinx-1-article_m-1.jpg", "nom": "Jinx Mafieuse", "rarete": "Rare", "id_personnage": 5, "niveau": 1 },
    { "id": 10, "image" :"https://static1.millenium.org/entity_articles/9/18/99/@/1470840-jinx-2-article_m-1.jpg", "nom": "Jinx Monstre", "rarete": "Épique", "id_personnage": 5, "niveau": 2 }
  ],
  "equipements": [
    { "id": 1, "image":"https://i.ytimg.com/vi/gjBNapuG_XM/sddefault.jpg",  "nom": "Bâton du Vide", "id_personnage": 1, "niveau": 1},
    { "id": 2, "image":"https://static.wikia.nocookie.net/leagueoflegends/images/7/7c/Coiffe_de_Rabadon_Obj.png/revision/latest?cb=20210518190042&path-prefix=fr", "nom": "Coiffe de Rabadon", "id_personnage": 1, "niveau": 2 },
    { "id": 3, "image":"https://leagueofitems.com/images/items/256/3153.webp", "nom": "Lame du roi déchu", "id_personnage": 2, "niveau": 1 },
    { "id": 4, "image" :"https://images.contentstack.io/v3/assets/blt731acb42bb3d1659/blt3124604fd85a70b9/5fa1f073fe49b57a83a17190/6333_Fighter_T3_DeathsDance.png", "nom": "Danse de la mort", "id_personnage": 2, "niveau": 2 },
    { "id": 5, "image":"https://cdna.artstation.com/p/assets/images/images/022/992/816/medium/martin-hutter-ludens-echo-fa1-1to1quare.jpg?1577634734", "nom": "Echo de Luden", "id_personnage": 3, "niveau": 1 },
    { "id": 6, "image": "https://images.contentstack.io/v3/assets/blt731acb42bb3d1659/bltdaf9f139508e3caa/5fa1f0791a81c1644e998cf1/4637_Mage_T3_DemonicEmbrace.png", "nom": "Etreinte démoniaque", "id_personnage": 3, "niveau": 2},
    { "id": 7, "image":"https://images.contentstack.io/v3/assets/blt731acb42bb3d1659/blt5147c41f2d6ac31a/604aeff45aedc043351b81dd/3052_Tank_T3_SteraksGage.png?disposition=inline", "nom": "Gage de Sterak", "id_personnage": 4, "niveau": 1 },
    { "id": 8, "image":"https://images.contentstack.io/v3/assets/blt731acb42bb3d1659/bltacf13441476402a6/636c6feb173c53758cc79113/111522_Patch_12_22_Notes_Heartsteel.png", "nom": "Cœur d'acier", "id_personnage": 4, "niveau": 2 },
    { "id": 9, "image":"https://images.contentstack.io/v3/assets/blt731acb42bb3d1659/blt172a9e76ab952d52/64812a23359ef027fe60c644/Rapid_Firecannon_v2.png", "nom": "Canon ultrarapide", "id_personnage": 5, "niveau": 1 },
    { "id": 10, "image":"https://static.wikia.nocookie.net/leagueoflegends/images/4/44/Soif-de-sang_Obj.png/revision/latest?cb=20210518190756&path-prefix=fr", "nom": "Soif de Sang", "id_personnage": 5, "niveau": 2 }
  ]
}
en gros, j'ai des personnages avec des niveaux. quand ils évoluent, ils changent de nom, de photo par rapport à leur nom  de skin adapté au niveau en question, et pareil pour les équipements. j'ai ces pages : 
app.js : 
import AllCharacters from "./views/pages/AllCharacters.js";
import Error404 from './views/pages/Error404.js';
import Home from './views/pages/Home.js';
import CharacterShow from './views/pages/CharacterShow.js';

import Utils from './services/Utils.js';

const routes = {
    '/': Home,
    '/characters': AllCharacters,
    '/characters/:id': CharacterShow
};

const router = async () => {
    const content = null || document.querySelector('#content');

    let request = Utils.parseRequestURL()

    let parsedURL = (request.resource ? '/' + request.resource : '/') + (request.id ? '/:id' : '') + (request.verb ? '/' + request.verb : '');

    let page = routes[parsedURL] ? new routes[parsedURL] : Error404
    
    content.innerHTML = await page.render();
    await page.afterRender();
}


window.addEventListener('hashchange', router);

window.addEventListener('load', router);
allcharacters.js : 
import GameDataProvider from "../../services/GameDataProvider.js";

export default class AllCharacters {
    constructor() {
        this.upgradeCounter = 0;
    }

    async render () {
        let characters = await GameDataProvider.fetchCharacters();
        let skins = await Promise.all(characters.map(character => character.niveau > 0 ? GameDataProvider.findSkinByIdAndLevel(character.id, character.niveau) : null));
        let equipments = await Promise.all(characters.map(character => character.niveau > 0 ? GameDataProvider.findEquipmentsByCharacterIdAndLevel(character.id, character.niveau) : []));
        let view =  /*html*/`
            <h2>Tous les personnages</h2>
            <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
                ${ characters.map((character, index) => 
                    /*html*/`
                    <div class="col">
                        <div class="card shadow-sm">
                            <img class="bd-placeholder-img card-img-top" src="${character.niveau > 0 && skins[index] ? skins[index].image : character.image}" alt="${character.nom}" />
                            <div class="card-body">
                                <p class="card-text">${character.niveau > 0 && skins[index] ? skins[index].nom : character.nom}</p>
                                <p class="card-text">${character.role}</p>
                                <p class="card-text">Niveau : ${character.niveau}</p>
                                <div class="d-flex justify-content-between align-items-center">
                                    <div class="btn-group">
                                    <a href="#/characters/${character.id}" class="btn btn-sm btn-outline-secondary">Voir ${character.nom}</a>
                                    <button class="btn btn-sm btn-outline-secondary upgrade-button" data-id="${character.id}">Améliorer</button>
                                    </div>
                                    <small class="text-body-secondary">${character.id}</small>
                                </div>
                            </div>
                            <div class="equipments d-flex flex-wrap justify-content-around">
                                ${equipments[index].map(equipment => 
                                    /*html*/`
                                    <div class="equipment">
                                        <img style="width: 10vw; height: 15vh;" src="${equipment.image}" alt="${equipment.nom}" />
                                        <p>${equipment.nom}</p>
                                    </div>
                                    `
                                ).join('\n ')}
                            </div>
                        </div>
                    </div>
                    `
                    ).join('\n ')
                }
            </div>
        `
        return view
    }

    async afterRender() {
        const upgradeButtons = document.querySelectorAll('.upgrade-button');
        upgradeButtons.forEach(button => {
            button.addEventListener('click', async (event) => {
                const id = event.target.dataset.id;
                const character = await GameDataProvider.fetchCharacter(id);
                if (character && character.niveau < 2) {
                    character.niveau++;
                    await GameDataProvider.updateCharacter(character);
                    location.reload();
                }
            });
        });
    }
} 

gamedataprovider.js : 
import { ENDPOINT } from '../config.js'

export default class GameDataProvider {

    static fetchCharacters = async () => {
        const options = {
           method: 'GET',
           headers: {
               'Content-Type': 'application/json'
           }
       };
       try {
           const response = await fetch(`${ENDPOINT}/personnages`, options)
           const json = await response.json();
           return json
       } catch (err) {
           console.log('Error getting characters', err)
       }
    }

    static fetchCharacter = async (id) => {
        const options = {
           method: 'GET',
           headers: {
               'Content-Type': 'application/json'
           }
       };
       try {
           const response = await fetch(`${ENDPOINT}/personnages/${id}`, options)
           const json = await response.json();
           return json
       } catch (err) {
           console.log('Error getting character', err)
       }
    }

    static fetchSkins = async () => {
        const options = {
           method: 'GET',
           headers: {
               'Content-Type': 'application/json'
           }
       };
       try {
           const response = await fetch(`${ENDPOINT}/skins`, options)
           const json = await response.json();
           return json
       } catch (err) {
           console.log('Error getting skins', err)
       }
    }

    static fetchEquipments = async () => {
        const options = {
           method: 'GET',
           headers: {
               'Content-Type': 'application/json'
           }
       };
       try {
           const response = await fetch(`${ENDPOINT}/equipements`, options)
           const json = await response.json();
           return json
       } catch (err) {
           console.log('Error getting equipments', err)
       }
    }

    static findSkinByIdAndLevel = async (id, level) => {
        const options = {
           method: 'GET',
           headers: {
               'Content-Type': 'application/json'
           }
       };
       try {
            const response = await fetch(`${ENDPOINT}/skins?id_personnage=${id}&niveau=${level}`, options)
           if (!response.ok) {
               throw new Error(`HTTP error! status: ${response.status}`);
           }
           const json = await response.json();
           return json[0];
       } catch (err) {
           console.log('Error getting skin', err)
           return null;
       }
    }

    static findEquipmentsByCharacterIdAndLevel = async (id, level) => {
        const options = {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        };
        try {
            const response = await fetch(`${ENDPOINT}/equipements`, options)
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            const allEquipments = await response.json();
            const equipments = allEquipments.filter(equipment => equipment.id_personnage === id && equipment.niveau <= level);
            return equipments;
        } catch (err) {
            console.log('Error getting equipments', err)
            return null;
        }
    }

    static updateCharacter = async (character) => {
        const options = {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(character)
        };
        try {
            const response = await fetch(`${ENDPOINT}/personnages/${character.id}`, options)
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return response.json();
        } catch (err) {
            console.log('Error updating character', err)
            return null;
        }
    }
}
et quand je clique sur le bouton "améliorer" pour augmenter le niveau du personnage et changer graphiquement toutes les informations qui sont donc modifiées, ça me met cette erreur dans la console : GameDataProvider.js:29 
        
        
       GET http://localhost:3000/personnages/2 404 (Not Found)
fetchCharacter @ GameDataProvider.js:29
(anonymous) @ AllCharacters.js:57
GameDataProvider.js:33 Error getting character SyntaxError: Unexpected token 'N', "Not Found" is not valid JSON
GameDataProvider.js:29 
        
        
       GET http://localhost:3000/personnages/1 404 (Not Found)
fetchCharacter @ GameDataProvider.js:29
(anonymous) @ AllCharacters.js:57
GameDataProvider.js:33 Error getting character SyntaxError: Unexpected token 'N', "Not Found" is not valid JSON