Original article: How to Consume REST APIs in React – a Beginner's Guide

React es una librería popular de Front-end que los desarrolladores usan para crear aplicaciones. Si quieres crear aplicaciones listas para poner en producción seguramente vas a necesitar saber integrar APIs a tu aplicación de React en algún momento de tu carrera.

Cada desarrollador que quiera construir aplicaciones web robustas y modernas con React tiene que saber cómo consumir APIs para traer información a su aplicación de React.

En esta guía para principiantes, vamos a aprender como consumir una API RESTful en React trayendo, borrando o agregando información.  También veremos dos formas de consumir APIs RESTful y cómo usarla con React Hooks.

Aquí tienes un scrim interactivo sobre cómo consumir APIs REST en React:

¿Qué es una API REST?

Si alguna vez haz pasado tiempo programando o investigando sobre programación, seguramente te haz cruzado con el término "API".

API significa "Interfaz de Programación de Aplicación", por sus siglas en inglés. Es un medio que permite la comunicación programática entre diferentes aplicaciones y que devuelve una respuesta en tiempo real.

En los 2000, Roy Fielding definió el concepto REST como un estilo de arquitectura y metodología usado frecuentemente en el desarrollo de servicios de internet, como, por ejemplo, los sistemas de hipermedia distribuidos. Es un acrónimo que significa, en inglés, Transferencia de Estado Representacional.

Cuando se hace una petición a través de una API REST, ésta envía, al peticionante o a un endpoint, una representación del estado actual del recurso. Esta representación del estado puede tomar la forma de archivo JSON (JavaScript Object Notation), XML, o HTML.

JSON es el formato de archivo más usado porque no depende de ningún lenguaje de programación y puede ser leído tanto por personas como por computadoras.

Por ejemplo:

[
   {
      "userId": 1,
      "id": 1,
      "title": "sunt excepturi",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur "
   },
   {
      "userId": 1,
      "id": 2,
      "title": "qui est esse",
      "body": "est rerum tempore vitae\nsequi sint nihil"
   }
]

Cómo consumir APIs REST en React

Es posible consumir APIs REST dentro de una aplicación React de distintas maneras, pero en esta guía veremos dos de las formas más populares: Axios (un cliente HTTP basado en promesas) y Fetch API (una API web ya incluida en el navegador).

Nota: Para poder comprender esta guía en su totalidad, deberías saber JavaScript, React y React Hooks, ya que son conocimientos que vamos a usar aquí.

Antes de explicar cómo consumir APIs, es importante entender que consumir APIs en React es muy diferente de como se lo hace en JavaScript. Esto es porque estas peticiones ahora se hacen en un componente de React.

En nuestro caso, vamos a usar componentes funcionales, lo que significa que vamos a necesitar 2 Hooks de React importantes:

  • Hook useEffect: En React, las peticiones a las API se hacen dentro del hook useEffect(). El hook muestra la información cuando la aplicación es montada o después de se alcanza un estado específico. Esta es la sintaxis general que vamos a usar:
useEffect(() => {
    // data fetching here
}, []);
  • Hook useState: Al pedir información, debemos crear un estado en el cual se va a guardar el resultado de la petición. También lo podemos guardar en un herramienta de administración de estado como Redux o en un objeto de contexto. Para hacerlo más simple, vamos a guardar el resultado de la petición en el estado local de React.
const [posts, setPosts] = useState([]);

Vamos a meternos en la parte jugosa de esta guía, donde aprenderemos como obtener, agregar, y borrar información usando la API JSONPlaceholder. Este conocimiento es aplicable a cualquier tipo de API, ya que esta guía está orientada a principiantes.

Cómo consumir APIs con la Fetch API

La Fetch API es un método JavaScript para obtener recursos de un servidor o de un endpoint de una API. Como ya viene incluida en el navegador, no es necesario instalar dependencias o paquetes.

El método fetch() requiere de un argumento obligatorio que es la URL o dirección al recurso que queremos obtener. Luego, este se convierte en una promesa para que podamos manejar su éxito o error a través de los métodos then() y catch().

Una petición fetch básica es muy simple de escribir y se parece al código de abajo. Lo que estamos haciendo es simplemente obtener información desde una URL que devuelve esa información como un JSON y luego mostrándola en la consola:

fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
   .then(response => response.json())
   .then(data => console.log(data));

La respuesta por defecto es una respuesta HTTP estándar más que un JSON, pero podemos ver la información como un objeto JSON al usar el método json() incluida en la respuesta.

Cómo realizar una petición GET en React con Fetch API

Es posible usar el método HTTP GET para obtener información desde un endpoint.

Como dijimos antes, Fetch API solo acepta un argumento obligatorio, lo cual es cierto. También acepta un argumento option, el cual es opcional, específicamente cuando se usa el método GET, el cual es el método predeterminado. Pero para los otros métodos como POST y DELETE, es necesario usar el método con un arreglo:

fetch(url, {
    method: "GET" // default, so we can ignore
})

Hasta el momento aprendimos cómo funcionan las cosas, ahora vamos a poner todo lo que aprendimos junto y hacer una petición get para obtener información de nuestra API.

De nuevo, volvemos a usar la API gratuita JSONPlaceholder para obtener una lista de posts dentro de nuestra aplicación:

import React, { useState, useEffect } from 'react';

const App = () => {
   const [posts, setPosts] = useState([]);
   useEffect(() => {
      fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
         .then((response) => response.json())
         .then((data) => {
            console.log(data);
            setPosts(data);
         })
         .catch((err) => {
            console.log(err.message);
         });
   }, []);

return (
   // ... consume here
);
};

Creamos un estado en el código anterior para guardar la información que obtuvimos de la API así la podemos usar después en nuestra aplicación. También podemos establecer el valor predeterminado a un arreglo vacío.

const [posts, setPosts] = useState([]);

Como la parte más importante de then() ocurrió en el hook useState, la información/los posteos se obtienen apenas carga la aplicación. La petición fetch produce una promesa que puede ser aceptada o rechazada:

useEffect(() => {
   fetch('https://jsonplaceholder.typicode.com/posts?_limit=10').then(
      (response) => console.log(response)
   );
}, []);

Esta respuesta contiene una gran cantidad de información, como el código de estado, texto, y otra información que vamos a necesitar después para manejar los errores.

Hasta ahora, hemos manejado la resolución de una promesa con .then(), pero nos devolvió un objeto como respuesta que no es lo que queremos. Por esos, necesitamos transformar el objeto respuesta al formato JSON con el método .then(). Esto también nos devuelve una promesa para que podamos obtener la información usando el segundo .then().

useEffect(() => {
   fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
      .then((response) => response.json())
      .then((data) => {
         console.log(data);
         setPosts(data);
      });
}, []);

Si miramos la consola, vamos a ver que obtuvimos 10 posts de nuestra API, lo que también especificamos anteriormente en el estado.

Esto no está completo porque solamente estuvimos manejando la resolución favorable de la promesa pero no su rechazo, lo cual haremos con el método .catch():

useEffect(() => {
   fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
      .then((response) => response.json())
      .then((data) => {
         console.log(data);
         setPosts(data);
      })
      .catch((err) => {
         console.log(err.message);
      });
}, []);

Hasta ahora vimos como hacer una petición GET. Esta puede ser consumida dentro de nuestra aplicación, a través de un bucle que recorra nuestro arreglo:

const App = () => {
// ...

   return (
   <div className="posts-container">
      {posts.map((post) => {
         return (
            <div className="post-card" key={post.id}>
               <h2 className="post-title">{post.title}</h2>
               <p className="post-body">{post.body}</p>
               <div className="button">
               <div className="delete-btn">Delete</div>
               </div>
            </div>
         );
      })}
   </div>
   );
};

export default App;

Cómo realizar una petición POST en React con Fetch API

Es posible usar el método HTTP POST para enviar información a un endpoint. Funciona de manera similar a la petición GET, la única diferencia está en que es necesario agregar el método y dos parámetros adicionales al objeto opcional:

const addPosts = async (title, body) => {
await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: JSON.stringify({
   title: title,
   body: body,
   userId: Math.random().toString(36).slice(2),
}),
headers: {
   'Content-type': 'application/json; charset=UTF-8',
},
})
.then((response) => response.json())
.then((data) => {
   setPosts((posts) => [data, ...posts]);
   setTitle('');
   setBody('');
})
.catch((err) => {
   console.log(err.message);
});
};

Los dos parámetros que pueden parecer extraños son el body (cuerpo) y el header (encabezado).

En el body va a estar toda la información que queremos enviar a la API, la cual primero debemos convertir en una cadena de texto porque estamos enviando información al navegador. El header nos dice el tipo de información, que es siempre el mismo cuando consumimos APIs REST. También creamos el estado que va a guardar ésta nueva información y distribuir la información restante por el arreglo.

Si miramos el método addPost() que creamos, va a necesitar que esa información venga de un formulario o de otro lado. En nuestro caso, hice un formulario, obtuve la información mediante estados. Al enviar el formulario, la información es incluida en el método:

import React, { useState, useEffect } from 'react';
const App = () => {
const [title, setTitle] = useState('');
const [body, setBody] = useState('');
// ...
const addPosts = async (title, body) => {
   await fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'POST',
      body: JSON.stringify({
         title: title,
         body: body,
         userId: Math.random().toString(36).slice(2),
      }),
      headers: {
         'Content-type': 'application/json; charset=UTF-8',
      },
   })
      .then((response) => response.json())
      .then((data) => {
         setPosts((posts) => [data, ...posts]);
         setTitle('');
         setBody('');
      })
      .catch((err) => {
         console.log(err.message);
      });
};

const handleSubmit = (e) => {
   e.preventDefault();
   addPosts(title, body);
};    

return (
   <div className="app">
      <div className="add-post-container">
         <form onSubmit={handleSubmit}>
            <input type="text" className="form-control" value={title}
               onChange={(e) => setTitle(e.target.value)}
            />
            <textarea name="" className="form-control" id="" cols="10" rows="8" 
               value={body} onChange={(e) => setBody(e.target.value)} 
            ></textarea>
            <button type="submit">Add Post</button>
         </form>
      </div>
      {/* ... */}
   </div>
);
};

export default App;

Cómo realizar una petición DELETE en React con Fetch API

Es posible usar el método HTTP DELETE para eliminar información de un endpoint. También funciona de forma similar a la petición GET, la principal diferencia radica en que tenemos que agregar el método:

const deletePost = async (id) => {
await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
   method: 'DELETE',
}).then((response) => {
   if (response.status === 200) {
      setPosts(
         posts.filter((post) => {
            return post.id !== id;
         })
      );
   } else {
      return;
   }
});
};

Este código se dispara al hacer click en el botón. De esta manera, obtenemos el id del post específico en el cual se hizo click.

Este método hace que se elimine la información del resultado devuelto. Esto permite que la información sea borrada de la API, pero no inmediatamente de la UI y por eso necesitamos agregar un filtro que elimine la información también de ahí. Para cada ítem del bucle, tú botón de borrar debería lucir así:

const App = () => {
// ...

   return (
   <div className="posts-container">
      {posts.map((post) => {
         return (
            <div className="post-card" key={post.id}>
               {/* ... */}
               <div className="button">
                  <div className="delete-btn" onClick={() => deletePost(post.id)}>
                     Delete
                  </div>
               </div>    
            </div>
         );
      })}
   </div>
   );
};

export default App;

Cómo usar Async/Await en Fetch API

Hasta ahora vimos como hacer peticiones fetch usando la sintaxis de las promesas, que puede ser confusa a veces. Y después, el encadenamiento con .then(). Podemos evitar el encadenamiento usando Async/await y así escribir código más legible.

Para usar async/awaiy, primero hay que escribir async en la función. Después, al hacer una petición y esperar un respuesta, hay que agregar la sintaxis await delante de la función para esperar a que la promesa devuelva el resultado.

Cuando usamos async/await, todas las peticiones Fetch se ven así:

import React, { useState, useEffect } from 'react';

const App = () => {
   const [title, setTitle] = useState('');
   const [body, setBody] = useState('');
   const [posts, setPosts] = useState([]);

   // GET with fetch API
   useEffect(() => {
      const fetchPost = async () => {
         const response = await fetch(
            'https://jsonplaceholder.typicode.com/posts?_limit=10'
         );
         const data = await response.json();
         console.log(data);
         setPosts(data);
      };
      fetchPost();
   }, []);

   // Delete with fetchAPI
   const deletePost = async (id) => {
      let response = await fetch(
         `https://jsonplaceholder.typicode.com/posts/${id}`,
         {
            method: 'DELETE',
         }
      );
      if (response.status === 200) {
         setPosts(
            posts.filter((post) => {
               return post.id !== id;
            })
         );
      } else {
         return;
      }
   };

   // Post with fetchAPI
   const addPosts = async (title, body) => {
      let response = await fetch('https://jsonplaceholder.typicode.com/posts', {
         method: 'POST',
         body: JSON.stringify({
            title: title,
            body: body,
            userId: Math.random().toString(36).slice(2),
         }),
         headers: {
            'Content-type': 'application/json; charset=UTF-8',
         },
      });
      let data = await response.json();
      setPosts((posts) => [data, ...posts]);
      setTitle('');
      setBody('');
   };

   const handleSubmit = (e) => {
      e.preventDefault();
      addPosts(title, body);
   };

   return (
      // ...
   );
};

export default App;

Aquí tienes un scrim interactivo para guiarte:

Cómo manejar errores con Fetch API

En esta sección, vamos a ver como manejar error de manera tradicional y con async/await.

Para manejar los errores con Fetch API, podemos usar la misma información de la respuesta o usar la sentencia try/catch si usamos async/await.

Primero veamos cómo podemos hacerlo con Fetch API:

const fetchPost = () => {
fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
   .then((response) => {
      if (!response.ok) {
         throw Error(response.statusText);
      }
      return response.json();
   })
   .then((data) => {
      console.log(data);
      setPosts(data);
   })
   .catch((err) => {
      console.log(err.message);
   });
};

Aquí (en inglés) puedes leer más sobre errores en Fetch API.

Para async/await podemos usar  try y catch de esta forma:

const fetchPost = async () => {
   try {
      const response = await fetch(
         'https://jsonplaceholder.typicode.com/posts?_limit=10'
      );
      const data = await response.json();
      setPosts(data);
   } catch (error) {
      console.log(error);
   }
};

Cómo consumir APIs con Axios:

Axios es un  cliente HTTP basado en promesas que hace más simple el envío de peticiones HTTP asíncronas a endpoints REST. El endpoint en nuestro caso la API de posts de JSONPlaceholder, a la cual haremos peticiones  GET, POST, y DELETE.

Aquí tienes un scrim interactivo que te guiará por los distintos pasos mientras lees:

Cómo instalar y configurar Axios

A diferencia de Fetch API, Axios no viene incluido en el navegador por lo que necesitamos instalarlo en nuestro proyecto para poder utilizarlo.

Para poder instalar Axios en tu proyecto necesitas correr el siguiente comando:

npm install axios

Una vez que lo instalamos correctamente, podemos crear una instancia, lo cual es opcional pero recomendable ya que nos evita repeticiones innecesarias.

Para crear una instancia, usamos el método .create(), que podemos usarlo para especificar información como la URL y posibles encabezados:

import axios from "axios";

const client = axios.create({
   baseURL: "https://jsonplaceholder.typicode.com/posts" 
});

Cómo realizar una petición GET en React con Axios

Usaremos la instancia que creamos anterior para realizar la petición GET. Todo lo que necesitamos es establecer parámetros, si los hay, y obtener la respuesta como un JSON por defecto.

A diferencia del método Fetch API, no se necesita una option para declarar el método. Solamente agregamos el método a la instancia y hacemos la petición.

useEffect(() => {
   client.get('?_limit=10').then((response) => {
      setPosts(response.data);
   });
}, []);

Cómo hacer una petición POST en React con Axios

Como dijimos antes, se puede usar el método POST para enviar información a un endpoint. Funciona de manera similar a la petición  GET, siendo la principal diferencia el requesito de incluir el método y una option para guardar la información que estamos enviando:

const addPosts = (title, body) => {
   client
      .post('', {
         title: title,
         body: body,
      })
      .then((response) => {
         setPosts((posts) => [response.data, ...posts]);
      });
};

Cómo hacer una petición DELETE en React con Axios

Podemos hacer peticiones delete con el método delete, el cual obtiene el id  y lo borra de la API. También usaremos el método filter para borrarlo de la UI, como lo hicimos con el método Fetch API:

const deletePost = (id) => {
   client.delete(`${id}`);
   setPosts(
      posts.filter((post) => {
         return post.id !== id;
      })
   );
};

Cómo usar Async/Await en Axios

Hasta ahora hemos visto con hacer peticiones con Axios usando la sintaxis para promesas. Pero ahora veamos cómo usar async/await para escribir menos código y evitar el encadenamiento con .then()

Cuando usemos async/await, todas nuestras peticiones Axios se verán así:

import React, { useState, useEffect } from 'react';

const App = () => {
   const [title, setTitle] = useState('');
   const [body, setBody] = useState('');
   const [posts, setPosts] = useState([]);

   // GET with Axios
   useEffect(() => {
      const fetchPost = async () => {
         let response = await client.get('?_limit=10');
         setPosts(response.data);
      };
      fetchPost();
   }, []);

   // Delete with Axios
   const deletePost = async (id) => {
      await client.delete(`${id}`);
      setPosts(
         posts.filter((post) => {
            return post.id !== id;
         })
      );
   };

   // Post with Axios
   const addPosts = async (title, body) => {
      let response = await client.post('', {
         title: title,
         body: body,
      });
      setPosts((posts) => [response.data, ...posts]);
   };

   const handleSubmit = (e) => {
      e.preventDefault();
      addPosts(title, body);
   };

   return (
      // ...
   );
};

export default App;

Cómo manejar errores con Axios

Para peticiones con Axios basadas en promesas, podemos usar los métodos .then() y .catch () , pero para async/awit, podemos usar el bloque try...catch. Esto es muy similar a como lo implementamos en Fetch API.
El bloque try...catch se verá así:

const fetchPost = async () => {
   try {
      let response = await client.get('?_limit=10');
      setPosts(response.data);
   } catch (error) {
      console.log(error);
   }
};

Aquí (en inglés) puedes leer más sobre como manejar errores en Axios.

Fetch API vs Axios

Quizás hayas notado algunas diferencias, pero vamos a ponerlas en un tabla así podemos comparar Fetch y Axios.

Estás distinciones te permitirán decidir que método elegir según el proyecto (aquí tienes el cuadro traducido al español):

AXIOSFETCH
Axios is a standalone third-party package that is simple to install.Fetch is built into most modern browsers. No installation is required as such.
Axios uses the data property.Fetch uses the body property.
Axios data contains the object.Fetch’s body has to be stringified.
When the status is 200 and the statusText is 'OK,' the Axios request is accepted.Fetch request is ok when response object contains the ok property.
Axios performs automatic transforms of JSON data.Fetch is a two-step process when handling JSON data- first, to make the actual request; second, to call the .json() method on the response.
Axios allows cancelling request and request timeout.Fetch does not.
Axios has built-in support for download progress.Fetch does not support upload progress.
Axios has wide browser support.Fetch is only compatible with Chrome 42+, Firefox 39+, Edge 14+, and Safari 10.1+. (This is known as Backward Compatibility).

Conclusión

En esta guía aprendimos a consumir APIs REST en React con Fetch API y Axios.

Esto te ayudará a empezar a consumir API en React, y desde ahí podrás consumir información de manera más complejas y manipular APIs de la manera que quieras.

¡Embárcate en un viaje de aprendizaje! Explora más de 200 artículos escritos por expertos en desarrollo web. Pégale un vistazo a mí blog (en inglés) con contenido interesante escrito por mí.

Joel Olawanle

Joel Olawanle

Desarrollador Web y Escritor Técnico