Uso de fetch() para recibir y enviar desde el lado del servidor

fetch() es una función nativa de JavaScript que permite hacer peticiones HTTP de forma sencilla y trabajar con sus respuestas usando promesas. Es muy usada para obtener o enviar datos a un servidor, APIs, archivos, etc. Es una función disponible en el navegador (y también en Node.js con librerías o versiones recientes) que permite realizar solicitudes a una URL. Retorna una Promise que se resuelve con un objeto Response.

¿Cómo funciona?

Podemos entender la función fetch() como un método para establecer una conexión con un origen de datos externo al propio ámbito del script en el que se encuentra esa función. El mismo método de conexión sirve para realizar peticiones (Request) en la que podemos enviar datos al endpoint al que nos estamos conectando, y para obtener una respuesta (Response) con datos enviados que consumiremos en nuestra aplicación.

La función tiene dos parámetros: El primero es obligatorio y sirve para establecer la conexión con el origen de datos, este parámetro puede ser definido con un string, o con un objeto de tipo URL o Request. El segundo parámetro es opcional, y es un objeto que contiene las opciones que puedan configurar la petición que estamos creando. Este segundo parámetro es necesario cuando queremos enviar datos desde el script al endpoint.

Asincronía

Hay que tener en cuenta que los métodos con los que vamos a trabajar para hacer peticiones de datos son asíncronos, esto es que no se procesan de forma inmediata en el flujo de datos del script, porque dependen de que hayan recibido toda la información de la petición para poder realizar las operaciones solicitadas con esos datos, y ese proceso está condicionado por varios factores (tamaño de datos enviados, tipo de conexión, saturación de la red...), por lo tanto tenemos que integrar esa asincronía en nuestro flujo de trabajo.

De forma más técnica, los métodos asíncronos devuelven un objeto de tipo Promise, en lugar de devolver datos directamente. Siguiendo el esquema de trabajo de las Promises:


                fetch(url)
                    .then(response => response.json())
                    .then(data => console.log(data))
                    .catch(error => console.error(error));
            

Este sería el proceso realizado:

  1. Se llama a fetch(url) => Realiza la solicitud HTTP.
  2. fetch() devuelve una Promise => Cuando llega la respuesta del servidor, la promesa se resuelve con un objeto Response.
  3. Transforma la respuesta => Por ejemplo, si esperamos JSON: response.json(). Esto también devuelve una Promise.
  4. Usamos los datos => En el .then siguiente manejamos la información

Usando async/await

La sintaxis al modo promesas con .then() es perfectamente válida y funcional, pero en la actualidad es más habitual usarlo con una función asíncrona, que realiza lo mismo pero de una manera más limpia desde el lado de la lectura del código. Además permite un manejo de errores más preciso y sencillo, puesto que en el modo asíncrono el bloque de catch() puede incluirse dentro de la misma función de conexión, mientras que con la promesa debe ir al final de todo el proceso de encadenado.

Hay que tener en cuenta que una función asíncrona devuelve siempre una promesa, y que normalmente lo que queremos es acceder a la información que contiene esa promesa cuando se resuelve de forma positiva. Por eso, para acceder a esos datos que forman parte del contenido de la promesa debemos usar la palabra await, que le indica al script que debe esperar a que se resuelva la promesa para acceder a su información. Si llamamos directamente a una función asíncrona sin usar await antes de la llamada lo que obtenemos es el objeto promesa que retorna la función, pero no su contenido.

Por lo tanto, en el caso de querer acceder a los datos de un archivo json del servidor necesitmos acceder a los datos que se encuentran dentro de dos objetos Promise, que nos devuelven respectivamente fetch() y Response.json(). Para eso necesitamos llamar a esas funciones con la expresión await, y hay que recordar que await, por definición de javaScript, debe ir siempre dentro de una función async, de lo contrario obtendremos un error.

Con todas estas premisas en mente, este puede ser un ejemplo de conexión a datos externos usando una función asíncrona:


        async function cargarUsuarios() {
            try {
                const res = await fetch('https://api.github.com/users');

                if (!res.ok) {
                    throw new Error('Error en la petición');
                }

                const data = await res.json();
                console.log('datos', data);

            } catch (err) {
                console.error(err);
            }
        }
            

Manejo de los datos recibidos

Una vez recibidos los datos y parseados a formato json ya disponemos en nuestro código de un objeto con el que podemos realizar las acciones habituales que hemos visto en otros casos: recorrerlo con bucles, acceder y modificar sus propiedades y usar su información para modificar el DOM.

Probando la conexión del ejemplo anterior para que nos devuelva el listado de datos de usuarios

Envío de datos al servidor

El método fetch también permite enviar datos al servidor, además de recibirlos. Para ello, además, necesitamos tener en el servidor algún tipo de script que se encargue de procesar los datos.Un caso habitual es enviar los datos a un archivo php que comprobará en primer lugar que tienen un formato válido y a continuación los guardará en un archivo con formato .json en el servidor.

Y mediante un formulario, por ejemplo, el usuario rellena unos datos que mandamos al servidor para que se almacenen en ese archivo json y a continuación realizamos una petición al servidor para que nos devuelva el archivo completo, con los datos que acabamos de añadir, y los podamos procesar como sea neceario.


        async function enviaDatos(datos) {
            try {
                const res = await fetch('https:/misitiodestino.com/guardarformatojson.php', {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                        "Authorization": "Tipo de acceso"
                    },
                    body: JSON.stringify(datos_en_formato_json_a_enviar)
                })
                if (!res.ok) {
                    throw new Error('Error en la conexión');
                }
                const data = await res.json()
                await leerDatos()
            } catch (err) {
                console.error("Error:", err)
            }
        }
        async function leerDatos() {
            try {
                const res = await fetch('https:/misitiodestino.com/leerformatojson.php');
                if (!res.ok) {
                    throw new Error('Error en la petición');
                }
                const data = await res.json();
                // Procesar los datos recibidos
            } catch (err) {
                console.error(err);
            }       
        }