Obtener datos de una API
Usa el hook useFetch siempre que tu widget necesite datos de una 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>;
};
Tu widget espera los datos antes de dibujarse, así que data siempre está disponible. No hay ningún estado de carga que comprobar.
Usa useFetch en lugar del fetch del navegador. Las peticiones pasan por los servidores de Blotch, así que las API que bloquean las peticiones hechas desde un navegador siguen funcionando.
Tipar la respuesta
Pasa la forma de la respuesta como parámetro de tipo y data tendrá autocompletado completo:
type Quote = {
readonly text: string;
readonly author: string;
};
const { data } = useFetch<Quote[]>("https://api.example.com/quotes");
data[0].author; // string
No tienes que escribir el tipo a mano. En la pestaña Red del Estudio de widgets, elige Crear definiciones de tipos junto a una respuesta para copiar un tipo de TypeScript que la describa.
Usar entradas
Lee tus entradas con useInputs e inclúyelas en la URL. Envuelve en encodeURIComponent todo lo que escriba un usuario, para que los espacios y los símbolos lleguen intactos:
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>;
};
Claves de API
Nunca escribas una clave de API en el código de tu widget, porque el código de los widgets es público. Guárdala como entrada privada y envíala en una cabecera:
const { API_KEY } = useInputs();
const { data } = useFetch<Weather>("https://api.example.com/weather", {
headers: { "X-Api-Key": API_KEY },
});
Para los servicios en los que la gente inicia sesión con OAuth, como Google o Spotify, usa en su lugar una integración.
Enviar datos
El segundo argumento acepta las mismas opciones que el fetch del navegador, como method, headers y body:
const { data } = useFetch<SearchResult>("https://api.example.com/search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "weather" }),
});
body puede ser texto, como JSON, o bien URLSearchParams o FormData para enviar un formulario. No se pueden enviar archivos.
Mantener cada petición igual
El código de tu widget puede ejecutarse varias veces mientras espera los datos, y cada respuesta solo vuelve a la petición exacta que la pidió. Así que construye la misma URL, las mismas cabeceras y el mismo cuerpo cada vez.
Un ID aleatorio o la hora actual salen distintos en cada ejecución, así que su respuesta nunca se usa. Tras unos cuantos intentos, Blotch deja de pedirla y muestra un error en lugar de tu widget. Genera ese tipo de valor una sola vez, fuera de tu 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 tampoco lo conserva, porque React vuelve a empezar el estado de tu widget mientras espera sus primeros datos.
Una petición tras otra
Como data siempre está disponible, una segunda petición puede usar el resultado de la primera:
const { data: user } = useFetch<User>("https://api.example.com/me");
const { data: posts } = useFetch<Post[]>(
`https://api.example.com/users/${user.id}/posts`,
);
Las peticiones se ejecutan una tras otra, así que cada una alarga lo que tarda tu widget en prepararse. Obtén solo lo que muestras.
Caché
Pasa proxyCache con un ttl en milisegundos y Blotch guardará la respuesta durante ese tiempo:
const { data } = useFetch<Weather>("https://api.example.com/weather", {
proxyCache: { ttl: 30 * 60 * 1000 }, // 30 minutos
});
Hasta que se agote el ttl, toda petición idéntica, venga del marco que venga, recibe la respuesta guardada en lugar de volver a consultar la API. Una petición es idéntica cuando coinciden su URL, su método, sus cabeceras y su cuerpo, así que todos los marcos que muestran el tiempo de Londres comparten una respuesta y París tiene la suya.
Úsalo cuando los datos cambien despacio, como el tiempo o los titulares, o cuando la API sea lenta, limite el número de peticiones o cobre por cada una. No lo uses para datos que siempre deban estar al día.
- Las peticiones privadas nunca se guardan en caché. Una petición con una cabecera
Authorization,Proxy-Authorization,CookieoX-Api-Keysiempre va a la API, porque su respuesta puede pertenecer a una sola persona. Una clave en la URL no se puede detectar, así que guarda en caché solo las respuestas que sean iguales para todo el mundo. - Los errores también se guardan en caché. Con un
ttlmás corto te recuperas antes de una mala respuesta. - La caché no se puede vaciar antes de tiempo, y un
ttlmuy largo se acorta al máximo que permite Blotch.
Errores
data contiene lo que haya devuelto la API, incluso un error como 404 Not Found. Cuando la API tenga un formato de error, compruébalo:
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 petición ni siquiera se puede hacer, por ejemplo porque la API no funciona, Blotch muestra el error en lugar de tu widget. No tienes que gestionarlo tú.
Las peticiones salen de los servidores de Blotch, no del marco, así que no pueden llegar a direcciones de una red doméstica, como 192.168.1.10.
Reintentos
Algunas API responden «todavía estoy en ello» y esperan que vuelvas a preguntar. Dale a retry una condition que devuelva true para esas respuestas, y useFetch esperará y volverá a preguntar, hasta count veces:
const { data } = useFetch<Report>("https://api.example.com/report", {
retry: {
condition: (report) => report.status === "pending",
count: 5,
},
});
Formatos de respuesta
- JSON: se analiza automáticamente.
- XML, RSS y Atom: las respuestas que empiezan por
<?xmlse convierten en objetos sencillos. Los elementos pasan a ser claves, y los atributos, claves que empiezan por@_, así que<rss version="2.0">se convierte endata.rss["@_version"]. - Cualquier otra cosa, como texto sin formato o HTML, llega como
string.
Si las respuestas de una API XML no empiezan por <?xml, define output: "force" para convertirlas de todos modos. Define output: "pass-through" para conservar el XML como texto.
Consulta useFetch en la referencia para ver todas las opciones.