Como hacer peticiones a la API con Axios y React JS. ✨

franklin030601

Franklin Martinez

Posted on November 4, 2022

Como hacer peticiones a la API con Axios y React JS. ✨

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.🤗

 

Tabla de contenido.

📌 Tecnologías a utilizar.

📌 Creando el proyecto.

📌 Primeros pasos.

📌 ¿Qué es axios?

📌 Creando una nueva instancia de axios.

📌 Creando la petición GET.

📌 Creando la petición POST.

📌 Creando la petición PUT.

📌 Creando la petición DELETE.

📌 Manejando errores.

📌 Conclusión.

📌 Demostración.

📌 Código fuente.

 

🕯️ Tecnologías a utilizar.

  • ▶️ 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.

Al proyecto le colocaremos el nombre de: axios-react (opcional, tu le puedes poner el nombre que gustes).

npm init vite@latest
Enter fullscreen mode Exit fullscreen mode

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.

cd axios-react
Enter fullscreen mode Exit fullscreen mode

Luego instalamos las dependencias.

npm install
Enter fullscreen mode Exit fullscreen mode

Después abrimos el proyecto en un editor de código (en mi caso VS code).

code .
Enter fullscreen mode Exit fullscreen mode

🕯️ Primeros pasos.

Dentro del archivo src/App.tsx borramos todo y creamos un componente que muestre un hola mundo

const App = () => {
    return (
        <div>Hello world</div>
    )
}
export default App
Enter fullscreen mode Exit fullscreen mode

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?.

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.

Primero instalamos axios:

npm install axios
Enter fullscreen mode Exit fullscreen mode

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.

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;
}
Enter fullscreen mode Exit fullscreen mode

🕯️ Creando la petición GET.

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.

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

const App = () => {

  return (
    <div>
      <GetPost />
    </div>
  )
}
export default App
Enter fullscreen mode Exit fullscreen mode

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.
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>
        </>
    )
}
Enter fullscreen mode Exit fullscreen mode

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.

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

}
Enter fullscreen mode Exit fullscreen mode

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

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

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

    await client.get('')
}
Enter fullscreen mode Exit fullscreen mode

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:

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

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

    await client.get('?_limit=6')
}
Enter fullscreen mode Exit fullscreen mode

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.

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

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

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

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.

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>
        </>
    )
}
Enter fullscreen mode Exit fullscreen mode

Y asi luciría la data de la API ya renderizada
first

🕯️ Creando la petición POST.

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.

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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.

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

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

}
Enter fullscreen mode Exit fullscreen mode

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

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

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

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.

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
}
Enter fullscreen mode Exit fullscreen mode

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.

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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

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

const App = () => {

  return (
    <div>
        {/* <GetPost /> */}
        <CreatePost/>
    </div>
  )
}
export default App
Enter fullscreen mode Exit fullscreen mode

create post

🕯️ Creando la petición PUT.

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

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

const App = () => {

  return (
    <div>
        {/* <GetPost /> */}
        {/* <CreatePost /> */}
        <UpdatePost/>
    </div>
  )
}
export default App
Enter fullscreen mode Exit fullscreen mode

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.

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

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.

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
}
Enter fullscreen mode Exit fullscreen mode

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.

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>
    )
}
Enter fullscreen mode Exit fullscreen mode

update post

🕯️ Creando la petición DELETE.

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

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

const App = () => {

  return (
    <div>
        {/* <GetPost /> */}
        {/* <CreatePost /> */}
        {/* <UpdatePost /> */}
        <DeletePost/>
    </div>
  )
}
export default App
Enter fullscreen mode Exit fullscreen mode

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

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>
        </>
    )
}
Enter fullscreen mode Exit fullscreen mode

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.

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

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

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.

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>
        </>
    )
}
Enter fullscreen mode Exit fullscreen mode

delete post

🕯️ Manejando errores.

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[]

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 []
    }
}
Enter fullscreen mode Exit fullscreen mode

🕯️ Conclusión.

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

🔵 No olvides seguirme también en twitter: @Frankomtz361

🕯️ Demostración simple.

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/

demo

🕯️ Código fuente.

El código de App.tsx esta un poco diferente, pero el resto sigue igual.

GitHub logo Franklin361 / axios-react

Learn how to use axios with react 🚀

How to make API requests with Axios and React JS. ✨

This time, we are going to make API requests using axios and react js!

demo

 

Features ⚙️

  1. get posts.
  2. create posts.
  3. update posts by id
  4. delete post by id.
  5. handling errors.

 

Technologies 🧪

  • ▶️ React JS (v 18)
  • ▶️ Vite JS
  • ▶️ TypeScript
  • ▶️ Axios
  • ▶️ CSS vanilla

 

Installation 🧰

  1. Clone the repository (you need to have Git installed).
    git clone https://github.com/Franklin361/axios-react
Enter fullscreen mode Exit fullscreen mode
  1. Install dependencies of the project.
    npm install
Enter fullscreen mode Exit fullscreen mode
  1. Run the project.
    npm run dev
Enter fullscreen mode Exit fullscreen mode

 

Article links ⛓️

Here's the link to the tutorial in case you'd like to take a look at it! eyes 👀






💖 💪 🙅 🚩
franklin030601
Franklin Martinez

Posted on November 4, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related