Passa al contenuto principale

Recuperare dati da un’API

Usa l’hook useFetch ogni volta che il tuo widget ha bisogno di dati da un’API.

import { useFetch } from "~lib/use-fetch";

type Weather = {
readonly temperature: number;
};

export const Widget = () => {
const { data } = useFetch<Weather>("https://api.example.com/weather");

return <div className="text-6xl">{data.temperature}°C</div>;
};

Il tuo widget attende i dati prima di essere disegnato, quindi data c’è sempre. Non c’è nessuno stato di caricamento da controllare.

suggerimento

Usa useFetch invece del fetch del browser. Le richieste passano dai server di Blotch, quindi funzionano anche le API che bloccano le richieste provenienti da un browser.

Tipizzare la risposta​

Passa la forma della risposta come parametro di tipo, e avrai il completamento automatico su tutto data:

type Quote = {
readonly text: string;
readonly author: string;
};

const { data } = useFetch<Quote[]>("https://api.example.com/quotes");

data[0].author; // string
suggerimento

Non devi scrivere il tipo a mano. Nella scheda Rete dello Studio dei widget, scegli Crea definizioni di tipo accanto a una risposta per copiarne il tipo TypeScript.

Usare gli input​

Leggi i tuoi input con useInputs e inseriscili nell’URL. Racchiudi in encodeURIComponent tutto ciò che digita un utente, così spazi e simboli arrivano intatti:

import { useFetch } from "~lib/use-fetch";
import { useInputs } from "~lib/use-inputs";

export const Widget = () => {
const { CITY } = useInputs();

const { data } = useFetch<Weather>(
`https://api.example.com/weather?city=${encodeURIComponent(CITY)}`,
);

return <div>{data.temperature}°C</div>;
};

Chiavi API​

Non scrivere mai una chiave API nel codice del widget, perché il codice dei widget è pubblico. Salvala come input privato e inviala in un’intestazione:

const { API_KEY } = useInputs();

const { data } = useFetch<Weather>("https://api.example.com/weather", {
headers: { "X-Api-Key": API_KEY },
});

Per i servizi a cui si accede con OAuth, come Google o Spotify, usa invece un’integrazione.

Inviare dati​

Il secondo argomento accetta le stesse opzioni del fetch del browser, come method, headers e body:

const { data } = useFetch<SearchResult>("https://api.example.com/search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "weather" }),
});

body può essere testo, come JSON, oppure URLSearchParams o FormData per inviare un modulo. Non si possono inviare file.

Mantenere ogni richiesta identica​

Il codice del tuo widget può essere eseguito più volte mentre attende i dati, e ogni risposta torna solo alla richiesta esatta che l’ha chiesta. Quindi costruisci ogni volta lo stesso URL, le stesse intestazioni e lo stesso corpo.

Un ID casuale o l’ora attuale cambiano a ogni esecuzione, quindi la risposta a una richiesta che li contiene non viene mai usata. Dopo alcuni tentativi Blotch smette di chiedere e mostra un errore al posto del tuo widget. Crea un valore del genere una sola volta, fuori dal widget:

const DEVICE_ID = Math.random().toString(36).slice(2);

export const Widget = () => {
const { data } = useFetch<Session>("https://api.example.com/sessions", {
method: "POST",
body: JSON.stringify({ deviceId: DEVICE_ID }),
});

return <div>Signed in as {data.name}</div>;
};

Nemmeno useState lo conserva, perché React fa ripartire da zero lo stato del widget mentre attende i primi dati.

Una richiesta dopo l’altra​

Poiché data c’è sempre, una seconda richiesta può usare il risultato della prima:

const { data: user } = useFetch<User>("https://api.example.com/me");
const { data: posts } = useFetch<Post[]>(
`https://api.example.com/users/${user.id}/posts`,
);

Le richieste vengono eseguite una dopo l’altra, quindi ognuna allunga il tempo che il tuo widget impiega a essere preparato. Recupera solo ciò che mostri.

Cache​

Passa proxyCache con un ttl in millisecondi, e Blotch conserva la risposta per quel tempo:

const { data } = useFetch<Weather>("https://api.example.com/weather", {
proxyCache: { ttl: 30 * 60 * 1000 }, // 30 minuti
});

Finché il ttl non scade, ogni richiesta identica, da qualsiasi cornice, riceve la risposta salvata invece di interrogare di nuovo l’API. Una richiesta è identica quando URL, metodo, intestazioni e corpo coincidono tutti, quindi tutte le cornici che mostrano il meteo di Londra condividono una sola risposta, e Parigi ha la sua.

Usala quando i dati cambiano lentamente, come il meteo o i titoli delle notizie, o quando l’API è lenta, limita il numero di richieste o si fa pagare per ogni richiesta. Non usarla per i dati che devono essere sempre aggiornati.

  • Le richieste private non vengono mai memorizzate nella cache. Una richiesta con un’intestazione Authorization, Proxy-Authorization, Cookie o X-Api-Key va sempre all’API, perché la sua risposta potrebbe appartenere a una sola persona. Una chiave nell’URL non può essere riconosciuta, quindi memorizza nella cache solo le risposte che sono uguali per tutti.
  • Anche gli errori vengono memorizzati. Con un ttl più breve ti riprendi prima da una risposta sbagliata.
  • La cache non si può svuotare in anticipo, e un ttl molto lungo viene ridotto al massimo consentito da Blotch.

Errori​

data contiene tutto ciò che l’API ha restituito, anche un errore come 404 Not Found. Se l’API ha un formato per gli errori, controllalo:

type WeatherResponse =
| { readonly temperature: number }
| { readonly error: { readonly message: string } };

export const Widget = () => {
const { data } = useFetch<WeatherResponse>("https://api.example.com/weather");

if ("error" in data) {
return <div>Weather unavailable: {data.error.message}</div>;
}

return <div>{data.temperature}°C</div>;
};

Se la richiesta non può proprio essere eseguita, ad esempio perché l’API non è raggiungibile, Blotch mostra l’errore al posto del tuo widget. Non devi gestirlo tu.

Le richieste partono dai server di Blotch, non dalla cornice, quindi non possono raggiungere gli indirizzi di una rete domestica, come 192.168.1.10.

Riprovare​

Alcune API rispondono «ci sto ancora lavorando» e si aspettano che tu chieda di nuovo. Dai a retry una condition che restituisce true per quelle risposte, e useFetch attende e chiede di nuovo, fino a count volte:

const { data } = useFetch<Report>("https://api.example.com/report", {
retry: {
condition: (report) => report.status === "pending",
count: 5,
},
});

Formati di risposta​

  • Il JSON viene analizzato per te.
  • Le risposte XML, RSS e Atom che iniziano con <?xml diventano oggetti semplici. Gli elementi diventano chiavi, e gli attributi diventano chiavi che iniziano con @_, quindi <rss version="2.0"> diventa data.rss["@_version"].
  • Tutto il resto, come testo semplice o HTML, arriva come string.

Se le risposte di un’API XML non iniziano con <?xml, imposta output: "force" per convertirle comunque. Imposta output: "pass-through" per mantenere l’XML come testo.

Consulta useFetch nel riferimento per tutte le opzioni.