Aller au contenu principal

Récupérer des données depuis une API

Utilisez le hook useFetch chaque fois que votre widget a besoin de données provenant d’une 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>;
};

Votre widget attend les données avant d’être dessiné : data est donc toujours là. Il n’y a aucun état de chargement à vérifier.

astuce

Utilisez useFetch plutôt que le fetch du navigateur. Les requêtes passent par les serveurs de Blotch : les API qui bloquent les requêtes venant d’un navigateur fonctionnent donc quand même.

Typer la réponse​

Passez la forme de la réponse en paramètre de type, et data bénéficie d’une autocomplétion complète :

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

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

data[0].author; // string
astuce

Inutile d’écrire le type à la main. Dans l’onglet Réseau du Studio de widgets, choisissez Créer les définitions de types à côté d’une réponse pour copier le type TypeScript qui lui correspond.

Utiliser les entrées​

Lisez vos entrées avec useInputs et intégrez-les à l’URL. Passez tout ce que saisit un utilisateur dans encodeURIComponent, pour que les espaces et les symboles arrivent intacts :

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>;
};

Clés d’API​

N’écrivez jamais de clé d’API dans le code de votre widget, car le code des widgets est public. Enregistrez-la dans une entrée privée et envoyez-la dans un en-tête :

const { API_KEY } = useInputs();

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

Pour les services auxquels on se connecte avec OAuth, comme Google ou Spotify, utilisez plutôt une intégration.

Envoyer des données​

Le second argument accepte les mêmes options que le fetch du navigateur, comme method, headers et body :

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

body peut être du texte, comme du JSON, ou bien un URLSearchParams ou un FormData pour envoyer un formulaire. Il n’est pas possible d’envoyer des fichiers.

Garder chaque requête identique​

Le code de votre widget peut s’exécuter plusieurs fois pendant qu’il attend des données, et chaque réponse ne revient qu’à la requête exacte qui l’a demandée. Construisez donc la même URL, les mêmes en-têtes et le même corps à chaque fois.

Un identifiant aléatoire ou l’heure actuelle change à chaque exécution : sa réponse n’est donc jamais utilisée. Après quelques essais, Blotch cesse de demander et affiche une erreur à la place de votre widget. Créez une telle valeur une seule fois, en dehors de votre 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>;
};

useState ne la conservera pas non plus, car React réinitialise l’état de votre widget pendant qu’il attend ses premières données.

Une requête après l’autre​

Comme data est toujours là, une seconde requête peut utiliser le résultat de la première :

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

Les requêtes s’exécutent l’une après l’autre : chacune allonge donc le temps de préparation de votre widget. Ne récupérez que ce que vous affichez.

Mise en cache​

Passez proxyCache avec un ttl en millisecondes, et Blotch conserve la réponse pendant cette durée :

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

Tant que le ttl n’est pas écoulé, chaque requête identique, quel que soit le cadre qui l’envoie, reçoit la réponse enregistrée au lieu d’interroger à nouveau l’API. Une requête est identique lorsque son URL, sa méthode, ses en-têtes et son corps correspondent tous : tous les cadres qui affichent la météo de Londres partagent donc une même réponse, et Paris a la sienne.

Utilisez-la lorsque les données changent lentement, comme la météo ou les gros titres, ou lorsque l’API est lente, limitée en nombre de requêtes ou facturée à la requête. Ne l’utilisez pas pour des données qui doivent toujours être à jour.

  • Les requêtes privées ne sont jamais mises en cache. Une requête avec un en-tête Authorization, Proxy-Authorization, Cookie ou X-Api-Key va toujours jusqu’à l’API, car sa réponse peut n’appartenir qu’à une seule personne. Une clé placée dans l’URL ne peut pas être repérée : ne mettez donc en cache que des réponses identiques pour tout le monde.
  • Les erreurs sont aussi mises en cache. Avec un ttl plus court, votre widget se remet plus vite d’une mauvaise réponse.
  • Le cache ne peut pas être vidé avant l’heure, et un ttl très long est ramené au maximum autorisé par Blotch.

Erreurs​

data contient ce que l’API a renvoyé, quoi que ce soit, même une erreur comme 404 Not Found. Lorsque l’API a un format d’erreur, vérifiez sa présence :

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>;
};

Si la requête ne peut pas du tout être effectuée, par exemple parce que l’API est hors service, Blotch affiche l’erreur à la place de votre widget. Vous n’avez pas à gérer ce cas vous-même.

Les requêtes partent des serveurs de Blotch, pas du cadre : elles ne peuvent donc pas atteindre les adresses d’un réseau domestique, comme 192.168.1.10.

Nouvelles tentatives​

Certaines API répondent « toujours en cours » et attendent que vous redemandiez. Donnez à retry une condition qui renvoie true pour ces réponses : useFetch attend alors et redemande, jusqu’à count fois :

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

Formats de réponse​

  • Le JSON est analysé pour vous.
  • Les réponses XML, RSS et Atom qui commencent par <?xml deviennent de simples objets. Les éléments deviennent des clés, et les attributs des clés qui commencent par @_ : <rss version="2.0"> devient donc data.rss["@_version"].
  • Tout le reste, comme du texte brut ou du HTML, arrive sous forme de string.

Si les réponses d’une API XML ne commencent pas par <?xml, réglez output: "force" pour les convertir quand même. Réglez output: "pass-through" pour garder le XML sous forme de texte.

Consultez useFetch dans la référence pour connaître toutes les options.