El consumo de información de las APIs (habitualmente REST aunque hay opciones como GraphQL) es una de las partes más importantes de cualquier aplicación Frontend.
Los conceptos sobre esta manera de trabajar son comunes para todas las tecnologías donde trabajamos con aplicaciones Frontend.
Por lo tanto, si entendemos bien los conceptos en una tecnología, no vamos a tener muchos problemas en aplicarlo en otros frameworks, librerías,…, sea en Qwik, Javascript puro o en tecnologías como Angular, React,…
Cuando trabajamos con la acción de consumir información de APIs externas estaremos obteniendo información de los recursos que nos proporciona una web para poder realizar las acciones como leer, crear, modificar y eliminar, lo que solemos conocer como CRUD.
No todas las APIs nos dan opción de modificar la información, muchas de ellas podríamos decir que son básicas, en las que reciben los datos de consulta devolviendo un resultado acorde a esos datos enviados. Esto es lo básico de una API y sin entender esto, no podremos avanzar.
El objetivo principal es que aprendamos a hacer uso de este recurso como es consumir una API en Qwik y por esa razón, os voy a proporcionar todas las claves para entender como podemos consumir información de APIs externas mediante casos prácticos.
Lo que aprenderemos en este capítulo se engloba en estos puntos importantes.
Seguramente haya referencias donde encontremos muchas más APIs (si las tenéis compartirlas por favor), pero estas referencias son super completas y se van actualizando periódicamente.
Las voy a clasificar en dos puntos principales:
En cada una de estas referencias, podremos ver que las APIs se clasifican en categorías como Animales, Anime, Conversores de divisas,… Os invito a que veáis la referencia con calma para ver que opciones encontráis, seguramente haya algo de un tema que os interese verdaderamente y siendo así, os invito a que practiquéis con ello ajeno a lo que vamos a trabajar en este apartado, que puede que el tema usado no os apasione.
Acceso a la lista de APIs
: https://shorten-up.vercel.app/9RVuGmiHhHEn cada una de estas referencias, podremos ver que las APIs se clasifican en categorías como APIs oficiales y APIs de solo demostración, entre otras opciones.
Como os he mencionado anteriormente, visualizar el documento con calma para ver si encontráis algo que os resulte interesante.
Acceso a la lista de APIs
: https://shorten-up.vercel.app/sls75oNuRBUna vez disponibles estos recursos, vamos a comenzar a trabajar consumiendo una API REST desde el navegador.
En este apartado realizamos los preparativos necesarios para poner en marcha la API que usaremos como fuente de datos, para consumirlo mediante REST y GraphQL.
La API que usaremos es la siguiente:
https://shorten-up.vercel.app/aSmT1qtBjC
Dentro de esta API tenemos las instrucciones para arrancar el proyecto de la API que consiste en estos 3 pasos sencillos, hasta tener la API en marcha:
git clone https://github.com/qwik-book/qwik-book-api.git
nvm use && npm install
npm start
Una vez que arranquemos el proyecto, se nos habilita la URL principal con las instrucciones para acceder a los dos endpoints de la API REST y el endpoint correspondiente a la API GraphQL:
Llegados a este punto, ya tenemos la API que usaremos lista para consumir.
Pasamos al siguiente apartado donde veremos como consumir la API desde el navegador.
La API que acabamos de arrancar nos va a proporcionar información sobre los datos al detalle que corresponden a cada uno de los personajes. Tenemos las siguientes opciones:
Para acceder a esta API lo hacemos mediante este enlace:
Si tenéis conocimientos de como se consume una API REST os animo a pasar al siguiente punto donde ya se puede ver como consumir las APIs REST desde Qwik
Esta será el apartado de la documentación:
Donde tenemos dos endpoints correspondientes a este tipo de API:
/characters
: Lista de todos los personajes/character/<ID>
: Información del caracter seleccionado mediante el ID.Para ver la lista de personajes, lo que tenemos que hacer es introducir la siguiente URL:
http://localhost:3000/characters
Reflejándose en el acceso a los datos dentro de nuestro proyecto, que lo aplicaremos en el siguiente paso:
const dataCharacters = await fetch(`http://localhost:3000/characters`).json();
Esto sería correspondiente a la lista de todos los personajes.
Si hacemos uso de las peticiones con un personaje concreto, usaremos la siguiente estructura para obtener esa información:
http://localhost:3000/character/<ID>
Y los ids existentes, los tenemos desde el 1 hasta el 21 (incluido). Si seleccionamos 22 o algún valor que no esté en ese rango, nos dará un mensaje de aviso, de que no existe ese personaje seleccionado.
Existe
: http://localhost:3000/character/2
No existe
Asumo que ya tenéis el proyecto creado y disponible para trabajar con ello. El nombre asignado el proyecto (recomendado) 11-apis-rest.
Comenzamos con este apartado, donde vamos a obtener la lista de personajes de Breaking Bad con la siguiente URL:
http://localhost:3000/characters
Creamos primero la función para consumir los datos de la URL que acabamos de ver y esto lo hacemos dentro de la carpeta api
(la creamos dentro de src
) en el fichero characters.ts
añadiendo el siguiente código
// 0 - Fichero alojado en: src/api/characters.ts
// 1.- URL principal de la API - BASE API URL
const API_URL = 'http://localhost:3000';
// 2.- Reniendo en cuenta la URL http://localhost:3000/characters
export const charactersAPI = async (
controller?: AbortController
): Promise<Array<any>> => {
// 3.- CREAMOS el endpoint
const endPoint = `/characters`;
// 4.- Formamos la URL completa del recurso para la lista de personajes
const url = `${API_URL}${endPoint}`;
console.log('Vamos a obtener datos desde =====>', url);
// 5.- Pedimos los datos
const data = await fetch(url, {
method: 'GET',
signal: controller?.signal,
// 8.- Uso de Abort Controller (controller.signal).
// Gestionamos las posibles cancelaciones en ejecución
});
console.log('FETCH resolved');
// 6.- Lista de personajes
const json: Array<any> = (await data.json());
// 7.- Devolvemos resultado
return Array.isArray(json) ? json : Promise.reject(json);
};
En el capítulo anterior hemos trabajado con la gestión de estado, que son los encargados de gestionar valores de estado común y también hemos trabajado con un valor de estado computacional, el useComputed$
que observa y devuelve inmediatamente los datos, con opción a modificarlo.
En este caso, trabajaremos con useResource$()
, que tendrá un funcionamiento parecido a useComputed$
pero este hook se usará específicamente para observar los cambios de estado a la hora de obtener información remota, como es una API.
useResource$()
Si queréis profundizar con más información acerca de este hook, siempre podéis acceder al enlace de la documentación que os proporciono a continuación:
https://shorten-up.vercel.app/KKXL0zR6Ex
Al tener disponible la función para obtener los datos REST de la URL seleccionada, vamos al componente correspondiente a la ruta inicial (src/routes/index.tsx
) y añadimos la función useResource$()
de la siguiente manera:
// 1.- Importar
import { component$, useResource$ } from '@builder.io/qwik';
export default component$(() => {
// 2 Para cargar el contenido de la función que nos trae la información de la API
const charactersListResponse = useResource$<any>(() => {
});
return <>
<h1>Lista de Personajes principales de Breaking Bad</h1>
</>
});
El resultado actual es el siguiente:
Analizándolo con más detalle, ¿En qué consiste la función useResource$()
?
Este método nos va a permitir obtener los valores de una función de forma asincrónica.
Se llamará a la función asíncrona pasada como su primer argumento cuando se monte el componente y cuando cambien los valores rastreados.
Ahora vamos a trabajar dentro de useResource$()
, aplicando la llamada a la API obteniendo los datos necesarios con el año actual:
// 1.- Imports necesarios
import { component$, useResource$, useStore} from '@builder.io/qwik';
import { charactersAPI } from '~/api/characters';
export default component$(() => {
// 2
// 2 Para cargar el contenido de la función que nos trae la información de la API
const charactersListResponse = useResource$<any>(({ cleanup }) => {
// 3.- Es buena práctica usar `AbortController` para abortar (cancelar) la obtención de datos si
// llega una nueva solicitud. Creamos un nuevo `AbortController` y registramos una `limpieza` (cleanup)
// función que se llama cuando se vuelve a ejecutar esta función.
const controller = new AbortController();
cleanup(() => controller.abort());
// 4.- Llamar a la función y obtener el resultado
return charactersAPI(controller);
});
return (
<>
<h1>Lista de Personajes principales de Breaking Bad</h1>
<p>AQUÍ DEBEMOS DE PINTAR LA LISTA DE PERSONAJES</p>
</>
);
});
Y esto es lo que tenemos ahora:
El valor charactersListResponse que es el valor que se asigna desde useResource$()
y tiene estos tres posibles resultados:
pending: los datos aún no están disponibles, es decir, mientras está haciendo el proceso de obtener los datos, en estado de carga.
resolved: los datos están disponibles.
reject: los datos no están disponibles debido a un error o tiempo de espera superado.
Ahora ya después de usar la función useResource$()
y conociendo los posibles estados de las respuesta, debemos de incrustar el componente <Resource />
que será lo que obtenemos en la vista.
Resource
Lo que vamos a aplicar en este componente que tiene relación directa con el hook useResource$()
lo hacemos basándonos en este punto de la documentación:
https://shorten-up.vercel.app/UnGxxlF27X
Lo que vamos a añadir es la siguiente estructura:
<Resource
value={
/* VALOR ASIGNADO a la constante desde useResource$
/* En este caso: charactersListResponse*/
}
onPending={() => <div>Cargando...</div>} // Mientras está ejecutando la carga
onRejected={() =>
<div>Fallo a la hora de cargar la lista de personajes de Breaking Bad</div>} // Rechazado
onResolved={(result) => {
// Resuelto, donde renderizamos los resultados
return <div><CONTENIDO></div>;
}}
/>
Aplicándolo a nuestro proyecto:
// Añadir "Resource" en el import
import { component$, useResource$, useStore, Resource } from '@builder.io/qwik';
...
export default component$(() => {
....
return (
<>
<h1>Lista de Personajes principales de Breaking Bad</h1>
<Resource
value={charactersListResponse}
onPending={() => <div>Cargando...</div>} // Mientras está ejecutando la carga
onRejected={() => (
<div>Fallo a la hora de cargar la lista de personajes</div>
)} // Rechazado
onResolved={(result) => {
return <div>{JSON.stringify(result)}</div>;
}}
/>
</>
);
});
Quedando de la siguiente forma, como se puede apreciar ya coge todos los datos de las carreras y lo incrusta como una cadena de texto (que por cierto, queda feo, pero podemos comprobar de una manera rápida que obtiene bien la información):
Lo que tenemos que hacer es ponerlo más organizado y más vistoso y esto lo haremos en formato lista:
<Resource
value={charactersListResponse}
onPending={() => <div>Cargando...</div>} // Mientras está ejecutando la carga
onRejected={() => (
<div>Fallo a la hora de cargar la lista de carreras</div>
)} // Rechazado
onResolved={(result) => {
return result.length ? (
<ul class="list">
{result.map((character: any, index: number) => (
<li key={String(character.id).concat(character.name.toLowerCase())}>
<a href={character.url} target="_blank">
{character.name}
</a>{' - '}
({character.description}) - <Link href={`/details/${index + 1}`}>Más detalles</Link>
</li>
))}
</ul>
) : (
<p>
Sin resultados - Comprueba que la API está en funcionamiento
</p>
);
}}
/>
El resultado será el siguiente:
Vamos a darle un mínimo de estilo, para que se pueda visualizar bien el formato lista y muestre el contenido de una manera más limpia en src/global.css
:
ul {
margin: 0% 5%;
}
h1 {
margin: 1rem 0rem 1rem 2rem;
}
.list li::before {
/*https://cloford.com/resources/charcodes/utf-8_misc-symbols.htm coger el valor HEX para content*/
content: "\26B9";
color: rgb(10, 95, 34);
display: inline-block;
width: 1em;
font-weight: 800;
margin-right: 1em;
}
ul li {
list-style: none;
}
Quedando de la siguiente forma:
Si por lo que sea la API no responde, por ejemplo la API no está operativa por estar parada, debe de mostrarnos el siguiente estado:
Una vez que hemos realizado el primer apartado, donde recibimos la información de todos los personajes principales de Breaking Bad, llegará el momento que nos interesa filtrar estos datos, para obtener únicamente los correspondientes al seleccionado.
Para poder trabajar con lo siguiente, debemos de recordar lo aprendido en el apartado Obtención del parámetro ruta desde una URL del capítulo Routing.
Teniendo esto claro, vamos a src/routes
y dentro del directorio creamos un directorio details
y dentro de este otro llamado [id]
que se encargará de obtener el parámetro de manera dinámica junto con su fichero index.tsx
.
Quedará de la siguiente manera la estructura de directorios y ficheros:
src/
└── routes/
|── index.tsx
└── details/
└── [id] # details/1, details/2,...
└── index.tsx
Definimos la función que se encargará de llamar a la API, para obtener los datos del personaje seleccionado. Vamos al fichero creado anteriormente como src/api/characters.ts
y añadimos lo siguiente con lo que actualmente existe:
export const getCharacterAPI = async (
id: string,
controller?: AbortController
): Promise<object> => {
// 1.- CREAMOS el endpoint en base al id seleccionado
const endPoint = `/character/${id}`;
// 2.- Formamos la URL completa del recurso para la lista de carreras
const url = `${API_URL}${endPoint}`;
console.log('Vamos a obtener datos desde =====>', url);
// 3.- Pedimos los datos del año seleccionado
const data = await fetch(url, {
method: 'GET',
signal: controller?.signal,
// 6.- Uso de Abort Controller (controller.signal).
// Gestionamos las posibles cancelaciones en ejecución
});
console.log('FETCH resolved');
// 4.- Información del personaje seleccionado
const json: object = (await data.json());
// 5.- Devolvemos resultado
return ('id' in json) ? json : Promise.reject(json);
};
Ahora en src/routes/details/[id]/index.tsx
añadimos lo siguiente:
import { Resource, component$, useResource$ } from '@builder.io/qwik';
import { useLocation, Link } from '@builder.io/qwik-city';
import { getCharacterAPI } from '~/api/characters';
export default component$(() => {
const location = useLocation();
const getCharacterResponse = useResource$<any>(({ track, cleanup }) => {
track(() => location.params.id);
const controller = new AbortController();
cleanup(() => controller.abort());
return getCharacterAPI(location.params.id, controller);
});
return <>
<h1>Personaje seleccionado: ID {location.params.id}</h1>
<Resource
value={getCharacterResponse}
onPending={() => <div>Cargando...</div>} // Mientras está ejecutando la carga
onRejected={() => (
<div>Personaje no encontrado</div>
)} // Rechazado
onResolved={(result) => {
return <div>
<h2>{result.name}</h2>
<p>{result.description}</p>
<p>Número de episodios en los que participa: {result.episodes}</p>
<p><a href={result.url}>Más información</a></p>
<Link href={'/'}>Volver</Link>
</div>
}}
/>
</>;
});
Lo que se añade como novedad respecto a la lista de personajes (/
) es el uso de useLocation
, hook que hemos usado anteriormente en el capítulo de Routing, por lo que no es algo nuevo.
Añadimos para observar los cambios con track
del valor location.params.id
para que pida a la API los datos cada vez que seleccionemos un nuevo ID del personaje.
Guardamos y ejecutamos diferentes opciones.
Información de personajes con id "1" y "2"
Información que no existe con id que no está entre 1 y 21
Si volvemos a la ruta principal (/
) podemos ir accediendo a la información individual de cada personaje si hacemos click sobre Más detalles
(1) sobre cada uno de los elementos existentes digiriéndose a la ruta especificada (2)
Una vez realizado click:
Llegados a este punto ya hemos trabajado con la forma de consumir APIs REST con los dos endpoints disponibles, usando el elemento Resource
con el hook useResource$()
.
Ahora vamos a comenzar con el apartado de la API de GraphQL, desde el navegador mediante el uso del playground.
Abriendo el proyecto 11-01-apis-rest podemos encontrar todo el código trabajado hasta ahora:
El enlace lo tenéis a continuación:
https://shorten-up.vercel.app/t4RtWYewKR
Después de haber completado la manera de consumir una API REST paso a paso, vamos a realizar el mismo proceso con las APIs GraphQL, que prácticamente es lo mismo pero a la hora de hacer la llamada usaremos SIEMPRE el verbo POST, ya que así está establecido en este estándar.
Antes de entrar en detalles, vamos a usar el endpoint /graphql
de la API que estamos consumiento en estos momentos, donde podremos obtener la lista de los personajes en conjunto y los detalles de manera individual.
Si no habéis trabajado nunca con GraphQL, os animo que si tenéis interés, toméis este curso GRATUITO impartido por mi en el que para obtener unos conocimientos básicos de como se trabaja en el Playground, lo haremos con la sección Trabajando en la Interfaz GrahQL Playground — Un GraphiQL mejorado:
https://shorten-up.vercel.app/1qQ2OmSPac
Una vez que accedamos, tenemos la siguiente apariencia (puede cambiar algo), el Playground
donde vamos a realizar las diferentes consultas para obtener la información exacta que necesitemos.
Teniendo como referencia la documentación Docs
(1) para expandir los puntos de dicha documentación:
Una vez realizado el click tenemos (2) se muestra la lista de elementos que pertenecen a nuestra API.
Estos elementos son los llamadas definiciones que serán como los endpoints, pero todo dentro de una consulta, pudiendo usar uno, dos y reusar el mismo con diferentes alias.
Creamos dos consultas, por un lado la lista de los personajes principales de Breaking Bad (characters
) que es una consulta simple y por otro, una consulta un poco más compleja donde trabajamos con los Query Variables
(Apartado Variables
) para aplicar filtros y obtener los detalles de un personaje concreto mediante id (character
).
Esto como se hace no lo voy a explicar, ya está explicado en el curso que os he adjuntado. Nos centramos en hacer la consulta y coger esa información para consumir desde Qwik.
Consulta simple en el que usamos el resolver characters
para obtener la lista de todos los personajes principales existentes, donde nos interesa obtener el id de cada uno de ellos para la segunda consulta.
Seleccionamos Query
(1) para ver los resolvers disponibles, escribimos la consulta en el apartado de la mitad (2) y a la derecha su resultado (3).
Se ha especificado con lo mínimo posible para obtener el id que usaremos en la siguiente consulta junto con el nombre del personaje y votos recibidos (esto podemos prescindir si queremos).
Lo que tendríamos que tener en cuenta es lo siguiente:
Endpoint
: http://localhost:3000/graphql
body
: Dentro de la propiedad query
enviamos el contenido de la consulta que sería lo que corresponde a la siguiente estructura.
{
characters {
id
name
votes
}
}
Ahora que ya sabemos que podemos filtrar el personaje con el id
que apliquemos (de 1 a 21 incluido) vamos a aplicar la siguiente consulta en el que ya estamos metiendo otro elemento, el uso de Query Variables
(Variables
), que son las variables que usaremos para poder hacer consultas dinámicas:
1
: Consulta donde ya hacemos uso del filtro añadiendo los argumentos con los Query Variables
(apartado Variables
, depende del playground aparece de una manera u otra). Se añadirá con este contenido de la consulta:query getSelectCharacter($id: Int!) {
character(id: $id) {
name
description
episodes
url
votes
}
}
2
: El filtro para obtener los datos mediante el uso de Query Variables
. Y aquí, a continuación lo que corresponde al filtro que estamos aplicando:{
"id": 1
}
3
: Resultado aplicando correctamente el filtro para obtener solo los datos cuyo id es 1
.Llegados a este punto, vemos dos maneras de realizar diferentes tipos de consultas:
Es importante que sepáis trabajar con la documentación de las APIs, ahí está toda la clave. Si os desenvolvéis bien, podréis trabajar consumiendo cualquier API que os encontréis por el camino y en el caso de las APIs de GraphQL, las encontraréis documentadas, por lo menos con la estructura del schema.
Con esto, terminamos este apartado y usando estos ejemplos, vamos a consumirlo con Qwik, tal y como lo hemos hecho con los endpoints correspondientes a la API REST.
Para consumir una API de GraphQL siempre
necesitamos hacer las llamadas de tipo POST
. Lo que necesitamos mínimo es lo siguiente:
Endpoint
.query
— NECESARIO) y sus filtros, las Query Variables
(variables — opcional)Teniendo en cuenta lo siguiente, montamos la función para poder realizar las operaciones con una API de GraphQL.
Creamos un nuevo proyecto para tenerlo todo bien separado con el nombre asignado (recomendado) 11-02-apis-graphql.
Basándonos en lo visto con la API REST, usando su base, dejamos la función de la siguiente forma creando un nuevo fichero dentro del directorio api
llamando al fichero characters.ts
:
// 0 - Fichero alojado en: src/api/characters.ts
// 1.- Endpoint principal
const API_URL_GRAPHQL = 'http://localhost:3000/graphql';
// 2.- Función donde se realizan la consultas
export const breakingBapCharactersGraphQLAPI = async (
body: { query: string; variables?: object }, // 3.- Consultas obligatorias + filtros
controller?: AbortController
): Promise<Array<any>> => {
// 4.- Realizamos la llamada fetch con POST pasándole siempre el body con mínimo la "query" (consulta)
const resp = await fetch(API_URL_GRAPHQL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
signal: controller?.signal,
});
console.log('FETCH resolved');
// 5.- Extraemos la propiedad data, que siempre es el valor principal que tenemos en común
const { data } = await resp.json();
console.log(data);
return data || Promise.reject(data);
};
Teniendo la función definida, lo que nos hace falta es consumirla desde la parte frontal, donde se visualizarán los datos.
Basándonos en los anterior, vamos a src/routes/index.ts
y añadimos el contenido para obtener la lista de personajes:
1.- Importamos lo necesario
import { Resource, component$, useResource$, useSignal } from '@builder.io/qwik';
import { breakingBapCharactersGraphQLAPI } from '~/api/characters';
export default component$(() => {
const selectCharacter = useSignal(1);
// 2.- Accedemos a la API
const charactersListResource = useResource$<any>(({ cleanup }) => {
const controller = new AbortController();
cleanup(() => controller.abort());
// Llamada a la función pasando únicamente la consulta en "query"
return breakingBapCharactersGraphQLAPI(
{
query: `{
characters {
id
name
votes
}
}
`,
},
controller,
);
});
// 3.- Pintamos el resultado
return (
<>
<h1>Personajes Breaking Bad</h1>
<Resource
value={charactersListResource}
onPending={() => <div>Cargando...</div>}
onRejected={() => <div>Error al cargar la lista de personajes</div>}
onResolved={({ characters }) => {
return characters.length ? (
<ul class="list">
{characters.map((character: any) => (
<li key={character.id}>
ID: {character.id} / Name: {character.name}
</li>
))}
</ul>
) : (
<p>Sin resultados</p>
);
}}
/>
</>
);
});
Al ser un nuevo proyecto, añadiremos los estilos que se han establecido antes en 11-01-apis-rest en global.css para darle una apariencia más agradable
Y su resultado será el siguiente:
Ahora ya tenemos los personajes a mano, solo nos queda generar dinámicamente los botones asociados a los personajes, para que cuando hagamos click, podamos cargar los detalles que corresponden a ese personaje, algo como lo que hemos realizado en el apartado anterior, consumiendo la API GraphQL en el navegador.
Cambiamos el código a lo siguiente:
1.- Importamos lo necesario
import { Resource, component$, useResource$, useSignal} from '@builder.io/qwik';
import { breakingBapCharactersGraphQLAPI } from '~/api/characters';
export default component$(() => {
// 2.- Seleccionamos 'Walter White' por defecto
const selectCharacter = useSignal(1);
// 3.- Accedemos a la API
const charactersListResource = useResource$<any>(({ cleanup }) => {
...(Sin cambios)
});
// 4.- Pintamos el resultado
return (
<>
<h1>Personajes Breaking Bad</h1>
<p>Personaje seleccionado: {selectCharacter.value}</p>
<Resource
value={charactersListResource}
onPending={() => <div>Cargando...</div>}
onRejected={() => <div>Error al cargar la lista de personajes</div>}
onResolved={({ characters }) => {
return characters.length ? (
<div>
{characters.map((character: any) => (
<button key={character.name.toLowerCase()}>{character.name}</button>
))}
</div>
) : (
<p>Sin resultados</p>
);
}}
/>
</>
);
});
Y su resultado será el siguiente:
Añadimos la acción de click en los botones (dentro de <Resource />
), para que actualice la información del continente seleccionado:
<div>
{characters.map((character: any) => (
<button key={character.name.toLowerCase()} onClick$={() => selectCharacter.value = character.id}>{character.name}</button>
))}
</div>
Por defecto, estará seleccionado el id 1
que corresponde a Walter White
.
Si hacemos click en cualquiera de los otros personajes, irá cogiendo el id que le corresponde y con eso ya tendríamos lo necesario para poder cargar los detalles de cada selección.
Jesse Pinkman
Saul Goodman
Llegados a este punto ya tenemos el primer apartado realizado, lo que nos queda es añadir otro <Resource/>
para mostrar los detalles del personaje seleccionado.
Añadimos el siguiente código para obtener la información basándonos en lo visto en la segunda consulta que hemos hecho antes que era lo siguiente (ahora está mostrando los datos de Jesse Pinkman
):
Lo aplicamos al código, para que coja el valor del continente en base a nuestra selección:
import {
component$,
Resource,
useResource$,
useSignal,
} from '@builder.io/qwik';
import { breakingBapCharactersGraphQLAPI } from '~/api/characters';
export default component$(() => {
...
const characterSelectResource = useResource$<any>(
({ track, cleanup }) => {
// 1.- Observamos los cambios del continente seleccionado
track(() => selectCharacter.value);
const controller = new AbortController();
cleanup(() => controller.abort());
// 2.- Consulta para obtener los detalles del personaheje seleccionado
return breakingBapCharactersGraphQLAPI(
{
query: `
query getSelectCharacter($id: Int!) {
character(id: $id) {
name
description
episodes
url
votes
}
}
`,
variables: {
id: selectCharacter.value
}
},
controller
);
}
);
return (
<>
...
<hr />
<Resource
value={characterSelectResource}
onPending={() => <div>Cargando...</div>}
onRejected={() => <div>Falla al cargar los detalles del personaje seleccionado {selectCharacter.value}</div>}
onResolved={() => <p>Pendiente para mostrar detalles del personaje seleccionado</p>}
/>
</>
});
Y ahora vamos a probarlo. Al cargar hace lo siguiente (en la consola de ejecución de los eventos del servidor), siendo character
los detalles correspondientes al id
relacionado a Walter White
:
Ahora si selecciono Jesse Pinkman
, esto es lo que pasaría (en la consola del navegador).
Ahora recordad, que con cualquier cambio se visualizará el log dentro de la consola del navegador. Este momento estará todo listo en el navegador y cualquier evento de cambio en los valores del estado hará que se ejecute en el navegador y no en el servidor como en la primera carga.
Probad con otros personajes para ver resultados, a ver si funciona con coherencia.
Aquí los diferentes casos haciendo diferentes opciones:
Saul Goodman
Gustavo Fring
(Probad todos vosotros si tenéis curiosidad, el funcionamiento es correcto)
Lo más difícil está hecho, lo que falta es reflejar esos resultados en una forma de lista (o como queramos, tampoco nos vamos a parar mucho en ello).
Añadimos dentro del código JSX debajo de los botones de selección lo siguiente:
import {
component$,
Resource,
useResource$,
useSignal,
} from '@builder.io/qwik';
import { breakingBapCharactersGraphQLAPI } from '~/api/characters';
export default component$(() => {
...
return (
<>
...
<hr/>
<Resource
value={characterSelectResource}
onPending={() => <div>Cargando...</div>}
onRejected={() => <div>Falla al cargar los detalles del personaje seleccionado {selectCharacter.value}</div>}
onResolved={({character}) => {
return (character ?
<ul class="list">
{Object.keys(character).map((key: any) => (
<li key={key.toLowerCase()}>
{character[key]}
</li>
))}
</ul> : <p>Sin resultados</p>
);
}}
/>
</>
);
});
Y el resultado se refleja de la siguiente forma, en este caso de manera inicial:
Seleccionando Skyler White
:
Abriendo el proyecto 11-02-apis-graphql podemos encontrar todo el código trabajado hasta ahora:
El enlace lo tenéis a continuación:
https://shorten-up.vercel.app/ZoDukiaOeF
Al finalizar este capítulo, deberíamos de ser capaces de:
useResource$()
para manejar el estado de la llamada a la API en proyectos de Qwik.Resource
junto con useResource$()
para plasmar la información en los diferentes estado que puede estar cuando nos comunicamos con una API.useResource$()
consumir las APIs REST y GraphQL propuestas.Llegados a este punto hemos aprendido como consumir APIs REST y GraphQL con Qwik.
Este capítulo debemos de considerarlo muy importante, ya que a día de hoy prácticamente todas las aplicaciones hacen uso de la comunicación con APIs REST / GraphQL y entendiendo bien los conceptos de este capítulo, podremos realizarlo con cualquier API que deseemos usar.