Como hacer peticiones a la API con Axios y React JS. ✨
Tags: react, javascript, spanish, tutorial
Published: 2022-11-04 14:00:00

En esta ocasión aprenderás a como realizar peticiones HTTP básicas (GET, POST, PUT, DELETE) con ayuda de axios y usando React JS.

Cualquier tipo de Feedback es bienvenido, gracias y espero disfrutes el articulo.🤗

&nbsp;

## Tabla de contenido.
> 📌 [Tecnologías a utilizar.](#1)  
> 📌 [Creando el proyecto.](#2)  
> 📌 [Primeros pasos.](#3)  
> 📌 [¿Qué es axios?](#4)  
> 📌 [Creando una nueva instancia de axios.](#5)  
> 📌 [Creando la petición GET.](#6)  
> 📌 [Creando la petición POST.](#7)  
> 📌 [Creando la petición PUT.](#8)  
> 📌 [Creando la petición DELETE.](#9)  
> 📌 [Manejando errores.](#10)  
> 📌 [Conclusión.](#11)  
>> 📌 [Demostración.](#12)  
>> 📌 [Código fuente.](#13)  


&nbsp;

## 🕯️ Tecnologías a utilizar. <a id="1"></a>

- ▶️ React JS (version 18)
- ▶️ Vite JS
- ▶️ TypeScript
- ▶️ Axios
- ▶️ CSS vanilla (Los estilos los encuentras en el repositorio al final de este post) 


## 🕯️ Creando el proyecto. <a id="2"></a>

Al proyecto le colocaremos el nombre de: **`axios-react`** (opcional, tu le puedes poner el nombre que gustes).
```bash
npm init vite@latest
```
Creamos el proyecto con Vite JS y seleccionamos React con TypeScript.  
Luego ejecutamos el siguiente comando para navegar al directorio que se acaba de crear.

```bash
cd axios-react
```
Luego instalamos las dependencias.
```bash
npm install
```
Después abrimos el proyecto en un editor de código (en mi caso VS code).
```bash
code .
```

## 🕯️ Primeros pasos. <a id="3"></a>
Dentro del archivo **src/App.tsx** borramos todo y creamos un componente que muestre un `hola mundo`
```tsx
const App = () => {
    return (
        <div>Hello world</div>
    )
}
export default App
```

La idea es crear cuatro diferentes componentes, y cada componente hará una tipo de petición (GET, POST, PUT, DELETE).
De antemano te menciono que habrá código repetido. Si tu deseas puedes refactorizar, pero yo no lo hare porque mi propósito es enseñarte a usar **axios**!


Creamos la carpeta `src/components` y dentro creamos 4 archivos:

- **CreatePost.tsx**
- **GetPost.tsx**
- **UpdatePost.tsx**
- **DeletePost.tsx**

> 🚨 Nota: Cada vez que creamos una nueva carpeta, también crearemos un archivo **index.ts** para agrupar y exportar todas las funciones y componentes de otros archivos que están dentro de la misma carpeta, y que dichas funciones puedan ser importadas a traves de una sola referencia a esto se le conoce como **archivo barril**. 

## 🕯️ ¿Qué es axios?. <a id="4"></a>
Axios es una librería cliente HTTP basada en promesas que se puede usar tanto en Node JS como en el navegador; por lo que podremos configurar y realizar solicitudes a un servidor y recibiremos respuestas fáciles de procesar. 
Nos ayuda en el envió de peticiones asíncronas HTTP, asi ayudándonos a realizar las operaciones CRUD.
Ademas es una buena alternativa al Fetch API por defecto de los navegadores.

## 🕯️ Creando una nueva instancia de axios. <a id="5"></a>
Primero instalamos **axios**:

```bash
npm install axios
```

Vamos a crear la carpeta `src/api` y creamos el archivo **client.ts**
Dentro del archivo, vamos a importar axios y vamos a crear una nueva instancia de axios (la mayoría de las veces siempre vas a querer hacer esto).

Esto nos ayuda a tener una configuración centralizada en un lugar y personalizada, ya que la mayoría de los casos no queremos andar colocando la misma configuración cada vez que hagamos una petición.

La propiedad **create** de axios recibe un objeto de configuración, donde en este caso de pasamos la propiedad **baseURL**, que es la url base a la que haremos las peticiones

También le pueden establecer los headers, el timeout (numero que indica el tiempo en milisegundos para que la petición sea abortada), y otras propiedades, pero solo con el base URL nos basta para este ejemplo.

> Vamos a usar la API de JSONPlaceholder para hacer las peticiones.

Y de una vez definimos, aquí mismo, la interfaz de la respuesta que nos va a dar la API.

```tsx
import axios from 'axios';

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

export interface ResponseAPI {
    userId: number;
    id: number;
    title: string;
    body: string;
}
```

## 🕯️ Creando la petición GET. <a id="6"></a>

Primero vamos a `src/App.tsx` y vamos a importar todos los componentes que creamos con anterioridad.
Vamos a colocar primero el GetPost y luego lo comentaremos para poner el siguiente y asi sucesivamente.

```tsx
import { CreatePost, DeletePost, GetPost, UpdatePost } from "./components"

const App = () => {

  return (
    <div>
      <GetPost />
    </div>
  )
}
export default App
```

Ahora dentro de `src/components` en el archivo **GetPost.tsx**, vamos a crear un componente que va a mostrar unas tarjetas que provienen de la API.


- Primero tenemos un estado que almacena los post que sera un arreglo.
- Después, tenemos el JSX que solo mostramos unos textos y recorremos la variable de estado **posts** y renderizamos la información que nos debería dar la API.

```tsx
import { useState } from 'react';
import { ResponseAPI } from '../api';

export const GetPost = () => {

    const [posts, setPosts] = useState<ResponseAPI[]>([])

    return (
        <>
            <h1>Get Post 👇</h1><br />
            <h2>posts list</h2>

            <div className='grid'>
                {
                    posts.map(post => (
                        <div key={post.id}>
                            <p>Title: <span>{post.title}</span></p>
                            <p>Body: <span>{post.body}</span></p>
                            <p>User: <span>{post.userId}</span></p>
                        </div>
                    ))
                }
            </div>
        </>
    )
}
```

Luego, vamos a crear una nueva carpeta `src/utils` donde crearemos varios archivos, el primero es *getData.ts* 

Para usar axios es muy sencillo, solamente importamos la instancia que creamos.

Primero creamos una función, que sera asíncrona y retornara una promesa que resuelve un arreglo de ResponseAPI.

```tsx
export const getPosts = async (): Promise<ResponseAPI[]> => {

}
```

Ahora vamos a usar la instancia de axios, dicha instancia usamos el método get que nos proporciona la instancia.

```tsx
import { client, ResponseAPI } from "../api"

export const getPosts = async (): Promise<ResponseAPI[]> => {

    await client.get('')
}
```

Nota que ejecutamos la función get y dentro colocamos unas comillas vacías, ¿por que?

Bueno esto es necesario para hacerle saber a la instancia que no queremos agregar mas parámetros a nuestra URL base.

Porque por el momento tenemos la URL asi: "https://jsonplaceholder.typicode.com/posts", **por lo que agreguemos dentro del método get (o cualquier otro método) se concatenara a la esta URL**.

Por eso solo colocamos comillas simples y vacías haciendo referencia a que no queremos concatenar nada.

Pero si queremos concatenar algo como por ejemplo el limite de resultados de la API lo colocaremos de la siguiente manera:

```tsx
import { client, ResponseAPI } from "../api"

export const getPosts = async (): Promise<ResponseAPI[]> => {

    await client.get('?_limit=6')
}
```

Ahora, esta promesa nos devuelve un objeto que contiene varias propiedades como la configuración, los headers, el estado de la petición, entre otros pero el que nos interesa es la propiedad data. La propiedad **data** es lo que vamos a retornar, porque es ahi donde vendrá la respuesta de la API.

Nota que también esta tipado el método **get** con ResponseAPI[], por si las dudas, y aunque no loe pongas el tipo y retornes la data, funcionara porque la propiedad data por defecto es de tipo any.

```tsx
import { client, ResponseAPI } from "../api"

export const getPosts = async (): Promise<ResponseAPI[]> => {

    const { data } = await client.get<ResponseAPI[]>('?_limit=6')
    return data
}
```

Asi de fácil es hacer una petición GET.

Ahora devuelta a `src/components/GetPost.tsx`

Implementamos un efecto para hacer la llamada a la API (en esta ocasión la haremos asi, aunque lo recomendable es usar una librería para manejar el cache de las peticiones como React Query)

Dentro del efecto ejecutamos la función getPosts y resolvemos la promesa para después establecer la data que nos regresa dicha promesa.

```tsx
import { useState, useEffect } from 'react';
import { ResponseAPI } from '../api';
import { getPosts } from '../utils';

export const GetPost = () => {

    const [posts, setPosts] = useState<ResponseAPI[]>([])

    useEffect(() => {
        // You can implement a <Loading/>
        //  start loading
        getPosts().then(data => setPosts(data))
        //  finish loading
    }, [])

    return (
        <>
            <h1>Get Post 👇</h1><br />
            <h2>posts list</h2>

            <div className='grid'>
                {
                    posts.map(post => (
                        <div key={post.id}>
                            <p>Title: <span>{post.title}</span></p>
                            <p>Body: <span>{post.body}</span></p>
                            <p>User: <span>{post.userId}</span></p>
                        </div>
                    ))
                }
            </div>
        </>
    )
}
```

Y asi luciría la data de la API ya renderizada
![first](https://res.cloudinary.com/dnxchppfm/image/upload/v1665802023/axios-react/01_nr5x8x.png) 

## 🕯️ Creando la petición POST. <a id="7"></a>

Ahora vamos al archivo `src/components/CreatePost.tsx` y creamos un nuevo componente similar al anterior.

Este componente renderiza la misma lista de post y los almacena en un estado también.

> Nota que la key, al momento de recorrer el estado de posts, es **post.userId** porque este es el único valor que es diferente cuando creamos un nuevo post. 

También nota que se agrego un botón, para crear post, lo haremos sin un formulario, pero lo mejor seria recibir los valores por un formularios.

Este botón, en su evento onClick ejecuta la función **handleClick**, que por el momento no hace nada, pero ahi es donde tenemos que ejecutar le método para crear un nuevo post.

```tsx
import { useState } from 'react';
import { ResponseAPI } from '../api';

export const CreatePost = () => {

    const [posts, setPosts] = useState<ResponseAPI[]>([])

    const handleClick = async () => {}

    return (
        <div>
            <h1>Create Post 👇</h1>
            <button onClick={handleClick}>Add Post</button>

            <div className='grid'>
                {
                    posts.map(post => (
                        <div key={post.userId}>
                            <p>Title: <span>{post.title}</span></p>
                            <p>Body: <span>{post.body}</span></p>
                            <p>User: <span>{post.userId}</span></p>
                        </div>
                    ))
                }
            </div>
        </div>
    )
}
```

Después, vamos a crear un nuevo archivo en `src/utils` nombrado **createPost.ts**

Dentro del archivo creamos una nueva función que nos retorne una promesa resolviendo un solo ResponseAPI. Y también, esta función recibe algunos parámetros que son los necesarios para crear un nuevo post.

```tsx
import { client, ResponseAPI } from '../api';

export const createPost = async (title: string, body: string, userId: number): Promise<ResponseAPI> => {

}
```

Dentro de la función llamamos a la instancia y ejecutamos su método post, el cual también nos da acceso, una vez resuelta la promesa, a el objeto **data**.

> Nota que también debemos indicarle, si se necesita concatenar a la URL base o no, en este caso no, por eso solo colocamos comillas vacías

```tsx
import { client, ResponseAPI } from '../api';

export const createPost = async (title: string, body: string, userId: number): Promise<ResponseAPI> => {
    const { data } = await client.post('')
    return data
}
```

Pero el método **post** no solo recibe la URL, sino que también necesita el body o sea la data a enviar para crear nueva información.
Asi que ese body, se lo pasamos como segundo parámetro, en un objeto.

```tsx
import { client, ResponseAPI } from '../api';

export const createPost = async (title: string, body: string, userId: number): Promise<ResponseAPI> => {
    const { data } = await client.post('', { title, body, userId })
    return data
}
```

Listo, ya tenemos nuestra función para crear un nuevo post, ahora vamos a usarla en `src/components/CreatePost.tsx`

En la función **handleClick** llamamos a la función **createPost** y le pasamos los parámetros necesarios. Usamos async/await para resolver la promesa que nos retorna la función **createPost**, que si todo sale correcto, nos debe retornar un nuevo post.

Este nuevo post, lo vamos agregar al estado, manteniendo los posts anteriores.

```tsx
import { useState } from 'react';
import { ResponseAPI } from '../api';
import { createPost } from '../utils';

export const CreatePost = () => {

    const [posts, setPosts] = useState<ResponseAPI[]>([])

    const handleClick = async () => {
        const newPost = await createPost("new title", "something", Date.now())
        setPosts(prev => ([newPost, ...prev]))
    }

    return (
        <div>
            <h1>Create Post 👇</h1>
            <button onClick={handleClick}>Add Post</button>

            <div className='grid'>
                {
                    posts.map(post => (
                        <div key={post.userId}>
                            <p>Title: <span>{post.title}</span></p>
                            <p>Body: <span>{post.body}</span></p>
                            <p>User: <span>{post.userId}</span></p>
                        </div>
                    ))
                }
            </div>
        </div>
    )
}
```

Probablemente no veamos nada, pero recuerden comentar el anterior componente y colocar el nuevo, en `src/App.tsx`

```tsx
import { CreatePost, DeletePost, GetPost, UpdatePost } from "./components"

const App = () => {

  return (
    <div>
        {/* <GetPost /> */}
        <CreatePost/>
    </div>
  )
}
export default App
```

![create post](https://res.cloudinary.com/dnxchppfm/image/upload/v1665802023/axios-react/02_p9spbv.png)

## 🕯️ Creando la petición PUT. <a id="8"></a>

Primero vamos a cambiar el componente en `src/App.tsx`

```tsx
import { CreatePost, DeletePost, GetPost, UpdatePost } from "./components"

const App = () => {

  return (
    <div>
        {/* <GetPost /> */}
        {/* <CreatePost /> */}
        <UpdatePost/>
    </div>
  )
}
export default App
```

Luego vamos a `src/components/UpdatePost.tsx` para crear un nuevo componente funcional que es igual al de **GetPost.tsx**. ya que necesitamos una lista de posts existentes para poder actualizar alguno.

Nota que el div que se renderiza al momento de recorrer los post:
- Tiene un **className='card'**
- Tiene un evento onClick que ejecuta la función handleUpdate y le mandamos el id del post como parámetro.

La función handleUpdate es asíncrona y recibe el id del post que es de tipo numero, y por el momento no ejecuta nada.

```tsx
import { useState, useEffect } from 'react';
import { ResponseAPI } from '../api';
import { getPosts } from '../utils';

export const UpdatePost = () => {

    const [posts, setPosts] = useState<ResponseAPI[]>([])

    useEffect(() => {
        getPosts().then(data => setPosts(data))
    }, [])

    const handleUpdate = async (id: number) => {}

    return (
        <div>
            <h1>Update Post 👇</h1><br />
            <h2>Click a card</h2>
            <div className='grid'>
                {
                    posts.map(post => (
                        <div className='card' key={post.id} onClick={() => handleUpdate(post.id)}>
                            <p>Title: <span>{post.title}</span></p>
                            <p>Body: <span>{post.body}</span></p>
                            <p>User: <span>{post.userId}</span></p>
                        </div>
                    ))
                }
            </div>
        </div>
    )
}
```

Después, vamos a `src/utils` creamos un nuevo archivo **updatePost.ts**

Donde básicamente es casi igual que el método post.

> Nota que ahora los parámetros de la función están definidas en una interfaz.

La única diferencia entre el post y el put es que en la URL si tenemos que colocar un nuevo parámetro que es el id del post que queremos modificar.

```tsx
import { client, ResponseAPI } from '../api/client';

interface Props {
    id: number,
    userId: number,
    body: string,
    title: string
}

export const updatePost = async ({ id, body, title, userId }: Props): Promise<ResponseAPI> => {
    const { data } = await client.put(`${id}`, { body, title, userId })

    return data
}
```

Ahora vamos a usar nuestra función, en `src/components/UpdatePost.tsx`

El la función **handleUpdate** llamamos a la función updatePost y le pasamos los parámetros necesarios, nuevamente usamos async/await para resolver la promesa y obtener el post actualizado. 

Finalmente establecemos un nuevo estado, colocando el post actualizado.

```tsx
import { useState, useEffect } from 'react';
import { ResponseAPI } from '../api';
import { getPosts, updatePost } from '../utils';
export const UpdatePost = () => {

    const [posts, setPosts] = useState<ResponseAPI[]>([])

    useEffect(() => {
        getPosts().then(data => setPosts(data))
    }, [])

    const handleUpdate = async (id: number) => {

        const body = `Body updated`
        const title = `Title updated`
        const userId = Date.now()

        const postUpdated = await updatePost({ id, body, title, userId })

        setPosts(prev => ([
            postUpdated,
            ...prev.filter(post => post.id !== id),
        ]))
    }

    return (
        <div>
            <h1>Update Post 👇</h1><br />
            <h2>Click a card</h2>
            <div className='grid'>
                {
                    posts.map(post => (
                        <div className='card' key={post.id} onClick={() => handleUpdate(post.id)}>
                            <p>Title: <span>{post.title}</span></p>
                            <p>Body: <span>{post.body}</span></p>
                            <p>User: <span>{post.userId}</span></p>
                        </div>
                    ))
                }
            </div>
        </div>
    )
}
```

![update post](https://res.cloudinary.com/dnxchppfm/image/upload/v1665802024/axios-react/04_manzca.png)

## 🕯️ Creando la petición DELETE. <a id="9"></a>

Primero vamos a cambiar el componente en `src/App.tsx`

```tsx
import { CreatePost, DeletePost, GetPost, UpdatePost } from "./components"

const App = () => {

  return (
    <div>
        {/* <GetPost /> */}
        {/* <CreatePost /> */}
        {/* <UpdatePost /> */}
        <DeletePost/>
    </div>
  )
}
export default App
```

Luego vamos a `src/components/DeletePost.tsx` para crear un nuevo componente funcional que es igual al de **UpdatePost.tsx**. ya que necesitamos una lista de posts existentes para poder eliminar alguno.

Y ahora tenemos la función **handleDelete** que recibe un id y por el momento no hace nada.

> Nota que ahora el evento onClick en cada post, ejecuta una función llamada handleDelete y se le pasa el id del post



```tsx
import { getPosts } from "../utils"
import { useEffect, useState } from 'react';
import { ResponseAPI } from "../api";

export const DeletePost = () => {
    const [posts, setPosts] = useState<ResponseAPI[]>([])

    useEffect(() => {
        getPosts().then(data => setPosts(data))
    }, [])

    const handleDelete = async (id: number) => {
        
    }

    return (
        <>
            <h1>Delete Post 👇</h1> <br />
            <h2>Click a card</h2>
            <div className="grid">
                {
                    posts.map(post => (
                        <div className="card" key={post.id} onClick={() => handleDelete(post.id)}>
                            <p>ID: <span>{post.id}</span></p>
                            <p>Title: <span>{post.title}</span></p>
                            <p>Body: <span>{post.body}</span></p>
                        </div>
                    ))
                }
            </div>
        </>
    )
}
```

Entonces necesitamos crear un nuevo archivo `src/utils` nombrado **deletePost.ts** que adentro tendrá una función asíncrona que solo retornara una promesa resolviendo un valor booleano para indicar si la eliminación del post se hizo correctamente.

Solamente llamamos al método delete de nuestra instancia y agregamos el ID del post que vamos a eliminar.


En ese caso disponemos de las mismas propiedades que cuando ejecutamos un get, post o put. Pero la data nos nos sirve porque llegaría un objeto vació, en este caso evaluaremos el código de estado de la petición, si es un estado 200 significa que todo salio bien entonces retornaría un true.

```tsx
import { client } from '../api';

export const deletePost = async (id: number): Promise<Boolean> => {
    const { status } = await client.delete(`${id}`);
    return status === 200
}
```

Ahora vamos a usar esta función en `src/components/DeletePost.tsx`

Dentro de la función handleDelete ejecutamos la función deletePost mandando el ID del post que queremos eliminar, y mediante async/await resolvemos la promesa para obtener el valor booleano y en base a ese valor, actualizar el estado.

```tsx
import { deletePost, getPosts } from "../utils"
import { useEffect, useState } from 'react';
import { ResponseAPI } from "../api";

export const DeletePost = () => {
    const [posts, setPosts] = useState<ResponseAPI[]>([])

    useEffect(() => {
        getPosts().then(data => setPosts(data))
    }, [])

    const handleDelete = async (id: number) => {
        const isSuccess = await deletePost(id)
        if (isSuccess) setPosts(prev => prev.filter(post => post.id !== id))
    }

    return (
        <>
            <h1>Delete Post 👇</h1> <br />
            <h2>Click a card</h2>
            <div className="grid">
                {
                    posts.map(post => (
                        <div className="card" key={post.id} onClick={() => handleDelete(post.id)}>
                            <p>ID: <span>{post.id}</span></p>
                            <p>Title: <span>{post.title}</span></p>
                            <p>Body: <span>{post.body}</span></p>
                        </div>
                    ))
                }
            </div>
        </>
    )
}
```

![delete post](https://res.cloudinary.com/dnxchppfm/image/upload/v1665802024/axios-react/03_w38znw.png)


## 🕯️ Manejando errores. <a id="10"></a>

Manejar los errores con axios, solamente basta con ir a nuestras funciones helper (`src/utils/`) y usar un **try/catch** ya que hemos estado usando **async/await**.

Dentro del **try** colocamos todo el código que queremos ejecutar.


Dentro del **catch**, vamos a recibir el error si es que algo sale mal, por ejemplo: error en la solicitud, error en el servidor, entro otros.
Este error que recibimos por parámetro, lo podemos castear a un tipo que no ofrece axios que es **AxiosError** y asi tener el autocompletado.

Este error tiene varias propiedades, pero la mas común es el mensaje de error y el nombre del error.

Finalmente nota que en el **catch** retornamos un arreglo vació, esto es para cumplir con el contrato de la función ya que debemos devolver una promesa tipo ResponseAPI[]


```tsx
import { AxiosError } from "axios"
import { client, ResponseAPI } from "../api"


export const getPosts = async (): Promise<ResponseAPI[]> => {
    try {
        const { data } = await client.get('?_limit=6')

        return data

    } catch (error) {

        const err = error as AxiosError
        console.log(err.message)
        console.log(err.name)

        return []
    }
}
```

## 🕯️ Conclusión. <a id="11"></a>

Sin duda axios es una librería muy potente, aunque no te estoy diciendo que sea de uso obligatorios, es una librería muy util que te servirá en algún proyecto. 😉

Espero que te haya gustado esta publicación y que también espero haberte ayudado a entender como realizar peticiones básicas HTTP con esta librería, una alternativa a Fetch API. 🙌

Si conoces alguna otra forma distinta o mejor de realizar esta aplicación con gusto puedes comentarla.

> **Te invito a que revises mi portafolio en caso de que estés interesado en contactarme para algún proyecto! [Franklin Martinez Lucas](https://franklin-dev.netlify.app/)**

> 🔵 No olvides seguirme también en twitter: [**@Frankomtz361**](https://twitter.com/Frankomtz361)

{%embed https://twitter.com/Frankomtz361/status/1575329958674796547?s=20&t=AVAVQ1am_AkFb2TyMo9S-A %}

### 🕯️ Demostración simple. <a id="12"></a>
Hice unos cambios en el **App.tsx** para que no tengas que ir al código y comentar el componente y probar cada tipo de petición

[https://axios-reactjs-app.netlify.app/](https://axios-reactjs-app.netlify.app/)

![demo](https://res.cloudinary.com/dnxchppfm/image/upload/v1665852033/axios-react/Videograbaci%C3%B3n_2022-10-15_11_39_13_crykuv.gif)

### 🕯️ Código fuente. <a id="13"></a>
El código de **App.tsx** esta un poco diferente, pero el resto sigue igual.

{%embed https://github.com/Franklin361/axios-react %}