Usare le API REST con le Fetch API e la paginazione in JavaScript

In questo articolo vedremo come reperire i dati da un'API REST con le Fetch API gestendo anche la paginazione dei risultati.

Le Fetch API hanno una caratteristica peculiare: la Promise restituita invocando fetch() è sempre posta nello stato resolved. Ciò significa che per capire se la richiesta HTTP ha avuto successo dobbiamo interrogare la proprietà booleana ok della risposta e sollevare un'eccezione nel caso in cui il suo valore fosse false.

async function getData(url) {
        try {
            const request = await fetch(url);
            if(!request.ok) {
                throw new Error('Request error.');
            }
            return await request.json();
        } catch(err) {
            return null;
        }
}

In questo caso avremo un valore effettivo in JSON solo se la richiesta iniziale ha avuto esito positivo.

Deleghiamo il reperimento dei dati con paginazione ad una funzione specifica:

async function getFacts(page = 1) {
        const list = document.getElementById('facts-list');
        const preloader = document.getElementById('preloader');
        preloader.classList.remove('loaded');
        try {
            const url = `${API_ENDPOINT}?page=${page}`;
            const facts = await getData(url);
            if(!facts) {
                throw new Error('Request error.');
            }
            const data = facts.data;
            let contents = '';

            for(const fact of data) {
                contents += `<li>${fact.fact}</li>`;
            }
            list.innerHTML = contents;
            createPaginationLinks(facts.prev_page_url, facts.next_page_url);
            preloader.classList.add('loaded');
        } catch(err) {
            preloader.classList.add('loaded');
            list.innerHTML = '<li class="error">No data to show.</li>';
        }
}

I dati relativi alla paginazione di solito vengono restituiti come URL completo di query string contenente il numero di pagina. In questo caso prev_page_url e next_page_url conterranno gli URL della pagina precedente e successiva o null se tali pagine non esistono.

Aggiungiamo quindi i pulsanti di navigazione delle pagine:

function createPaginationLinks(previous, next) {
        const pagination = document.getElementById('pagination');
        pagination.innerHTML = '';
        if(previous) {
            const previousBtn = document.createElement('button');
            previousBtn.type = 'button';
            previousBtn.dataset.page = previous;
            previousBtn.innerText = 'Previous';
            previousBtn.id = 'previous';
            previousBtn.className = 'pagination-link';
            pagination.appendChild(previousBtn);
        }
        if(next) {
            const nextBtn = document.createElement('button');
            nextBtn.type = 'button';
            nextBtn.dataset.page = next;
            nextBtn.innerText = 'Next';
            nextBtn.id = 'next';
            nextBtn.className = 'pagination-link';
            pagination.appendChild(nextBtn);
        }
}

Ciascun pulsante ha l'attributo di dati page in cui viene salvato l'URL della paginazione. Per gestire il click su questi pulsanti dobbiamo far ricorso alla event delegation in quanto i pulsanti vengono aggiornati dinamicamente nel DOM ad ogni richiesta.

function handlePagination() {
        document.addEventListener('click', evt => {
            if(evt.target.classList.contains('pagination-link')) {
                const button = evt.target;
                const link = button.dataset.page;
                const page = parseInt(link.split('?')[1].replace('page=', ''), 10);
                getFacts(page);
            }
        });
}

Infine, inizializziamo il nostro codice:

document.addEventListener('DOMContentLoaded', () => {
        handlePagination();
        getFacts();
});

Demo

JavaScript Fetch API

Conclusione

A livello teorico l'unico dettaglio tecnico potenzialmente problematico nell'uso delle Fetch API riguarda la gestione delle eccezioni HTTP. Il resto delle feature è sicuramente più intuitivo per l'uso che ne vogliamo fare.

Torna su