Acceso y Registro (Consumir API desde React)

Consumir datos de una API en React

Crear un login y signup con Django y React (Consumir una API)

El objetivo de crear una API es poder acceder e intercambiar información a través del internet independientemente de la plataforma. Con una API puedes acceder a tus datos y hacer operaciones CRUD desde cualquier aplicación desarrollada en cualquier lenguaje.

Requisitos para este tutorial

  1. Crear una app de React
  2. Añadir rutas a React

Instalando axios en nuestro proyecto.

Para poder consumir una API desde React, lo primero que debemos hacer es instalar una herramienta llamada axios.

Lo primero que tenemos que hacer es instalar axios en nuestro proyecto. Abrimos una terminal, nos dirigimos a la carpeta de nuestro proyecto de React y ejecutamos el siguiente código:

npm install axios --save

Una vez instalado axios podemos crear nuestros archivos para recuperar información. Basándonos en el tutorial anterior “Creando una REST API con Django” y teniendo en cuenta que en dicho tutorial se creó una aplicación y una API para manejar posts, vamos a crear una manera de poder utilizar nuestra API.

Para dicha tarea vamos a crear una carpeta llamada api dentro de la carpeta src de nuestro proyecto de React.

Lo primero que necesitamos es crear una manera de autenticación para poder enviar nuestro token a la API para poder realizar cualquier método HTTP. Para ello creamos un archivo llamado authRepository y escribimos lo siguiente:

import axios from 'axios';  
  
const authRepository = () => {  
 let debug = true;  
  
    let baseUrl = 'http://localhost:8000/rest-auth';  
    let userUrl = 'http://localhost:8000/api/auth/me/';  
  
    //Dar un nombre al token local  
    const tokenName = 'user_uaeh_token';  
    // Obtener el usuario Determinar si está logeado o no  
    const getLocalToken = () => {  
        return JSON.parse(localStorage.getItem(tokenName));  
    };  
    
    const logIn = user => {  
        // Retornar una nueva promesa, la promesa es como un try catch  
        // el constructor recibe una funcion (arrow function) 
        // donde recibe resolve y reject  
        return new Promise((resolve, reject) => {  
        // Creamos una instancia de axios, para poder  
        // comunicarnos con el servidor // axios.create recibe un objeto en donde pasamos 
        // baseURL y headers  
            const instance = axios.create({  
                //baseURL es la url del endpoint  
                baseURL: baseUrl,  
                // headers es lo necesario para hacer la petición  
                headers: {  
                    'Content-Type': 'application/json'  
                }  
            });  

            // Ejecutamos un método http, el primer argumento es  
            // la url (si queremos agregar algo más a la url) 
            // el segundo es el objeto con la información 
            // necesaria  
            instance.post('login/', user)  
            .then(r => {  
                // si es correcto  
                // guardamos el resultado (el token de Django) 
                // en la base de datos (Local Storage) 
                // El primer argumento es el nombre con el que se va a guardar 
                // El segundo es la información que se guardara 
                // Siempre debe hacer una llamada a JSON.stringify  
                localStorage.setItem(tokenName, JSON.stringify(r.data.key));  
                // si es correcto, mediante resolve, retornamos la Promise  
                resolve(r.data);  
            }).catch(e => {  
                console.log(e);  
                // Si algo salio mal, mediante reject, retornamos la promise  
                reject(e.response);  
            }); 
        }); 
    };  
    const signUp = user => {  
        return new Promise((resolve, reject) => {  
            const instance = axios.create({  
                baseURL: baseUrl,  
                headers: {  
                    'Content-Type': 'application/json'  
                }  
            });  
            instance.post('registration/', user)  
            .then(r => {  
                localStorage.setItem(tokenName, JSON.stringify(r.data.key));  
                resolve(r.data);  
            }).catch(e => {  
                console.log(e);  
                reject(e.response);  
            }); 
        }); 
    };  
    const logOut = () => {  
        return new Promise((resolve, reject) => {  
            const instance = axios.create({  
                baseURL: baseUrl,  
                headers: {  
                    'Content-Type': 'application/json'  
                }  
            });  

            instance.post('logout/', {})  
            .then(r => {  
                localStorage.removeItem(tokenName);  
                resolve(r.data);  
            }).catch(e => {  
                console.log(e);  
                reject(e.response);  
            }); 
        }); 
    };
    
    return {  
        logIn,  
        signUp,  
        logOut  
    }  
};  
  
export default authRepository();

Este sistema de autenticación corresponde a los endpoint de django-rest-auth que se describieron en el tutorial Creando una REST API con Django . Básicamente lo que hace este archivo es definir una serie de funciones que crean instancias axios, y posteriormente ejecutan métodos http.
Posteriormente crearemos un archivo llamado postRepository.js y agregamos las siguientes líneas de código:

import axios from 'axios';  

const tokenName = 'user_uaeh_token';

const getLocalToken = () => {  
    return JSON.parse(localStorage.getItem(tokenName));  
};

const postRepository = () => {  
    let baseUrl = 'http://localhost:8000/api/posts';
 //CRUD METHODS
 //Create
    const newPost = (post) => {  
        return new Promise( (resolve, reject) => {  
            const instance = axios.create({  
                baseURL : baseUrl,  
    headers: {  
                    'Content-Type': 'application/json'
                    'Authorization': 'Token ' + getLocalToken()  
                }  
            });  
     
            instance.post('',post)  
            .then(r => {   
                resolve(r.data);  
            }).catch(e => {  
                console.log(e);  
                reject(e.response);  
            });  
        }); 
    };  
 // Read
    const getPost = () => {  
        return new Promise( (resolve, reject) => {  
            const instance = axios.create({  
                baseURL : baseUrl,  
    headers: {  
                    'Content-Type': 'application/json'
                    'Authorization': 'Token ' + getLocalToken()  
                }  
            });  
     
            instance.get()  
            .then(r => {   
                resolve(r.data);  
            }).catch(e => {  
                console.log(e);  
                reject(e.response);  
            });  
        }); 
    };  
 //Update
    const updatePost = (post) => {  
        return new Promise( (resolve, reject) => {  
            const instance = axios.create({  
                baseURL : baseUrl,  
    headers: {  
                    'Content-Type': 'application/json'
                    'Authorization': 'Token ' + getLocalToken()  
                }  
            });  
     
            instance.patch(post.id + '/', post)  
            .then(r => {   
                resolve(r.data);  
            }).catch(e => {  
                console.log(e);  
                reject(e.response);  
            });  
        }); 
    };  

    //Delete
    const deletePost = (idPost) => {  
        return new Promise( (resolve, reject) => {  
            const instance = axios.create({  
                baseURL : baseUrl,  
    headers: {  
                    'Content-Type': 'application/json'
                    'Authorization': 'Token ' + getLocalToken()  
                }  
            });  
     
            instance.delete(idPost + '/')  
            .then(r => {   
                resolve(r.data);  
            }).catch(e => {  
                console.log(e);  
                reject(e.response);  
            });  
        }); 
    };  


    
    return {  
     newPost,
        getPost,
        updatePost,
        deletePost 
 }  
};  
  
export default postRepository();

Creando un registro

Para probar nuestras llamadas a nuestra API vamos a probarlo con componentes React. Lo primero que haremos es crear los componentes para el login y registro. Vamos a crear una carpeta dentro de src (en nuestro proyecto de React) y la llamaremos signup. En ella crearemos dos archivos: SignUpPage.js, SignUp.cssy SignUpForm.js, las cuales quedan de la siguiente manera:

Nota: El objetivo de este tutorial es consumir una API, por lo que el manejo de forms con React, conceptos sobre Web Storage , React , CSS y Django quedan fuera del alcance de este proyecto. En este tutorial nos centraremos en cómo recuperar datos y consumir nuestra API desde nuestra aplicación en React.

SignUpPage.js

import React, {Component} from 'react';
import {SignUpForm} from './SignUpForm';
import AuthApi from '../api/authRepository';
import './SignUp.css';

class SignUpPage extends Component {
    constructor(props){
        super(props);
        this.state = {
            user: {
                username: '',
                email: '',
                password1: '',
                password2: ''
            }
        };
    }

    onChange = (e) => {
        let user = Object.assign({},this.state.user);
        user[e.target.name] = e.target.value;
        this.setState({user});
    }; 

    onSubmit = (e) => {
        e.preventDefault();
        const user = Object.assign({},this.state.user);
        user.username = user.email;
        AuthApi.signUp(user)
        .then(r => {
            alert('Usuario creado');
        }).catch(e => {
            alert('Algo mal');
        });

    }

    render(){
        return (
            <div className="container">
                <SignUpForm
                    user={this.state.user}
                    onChange={this.onChange}
                    onSubmit={this.onSubmit}
                />
            </div>
        );
    }

}

export default SignUpPage;

SignUpForm.js

import React from 'react';

export const SignUpForm = ({onChange,onSubmit, user:{email,password1,password2}}) => (
    <form className="form" onSubmit={onSubmit}>
        <label htmlFor="email">Email</label>
        <input 
            type="text"
            id="email" 
            name="email" 
            value={email}
            onChange={onChange}
        />
        <label htmlFor="password1">Contraseña</label>
        <input 
            type="password" 
            name="password1" 
            id="password1" 
            value={password1}
            onChange={onChange}
        />
        <label htmlFor="password2">Contraseña</label>
        <input 
            type="password" 
            name="password2" 
            id="password2" 
            value={password2}
            onChange={onChange}
        />
        <input 
            type="submit"  
            value="Registrar"
        />
    </form>
);

SignUp.css

@import url('https://fonts.googleapis.com/css?family=Mina|Roboto');
* {
 box-sizing: border-box;
 font-family: 'Mina', sans-serif;
}

.container {
 width: 100vw;
 height: 100vh;
 display: flex;
 justify-content: center;
 align-items: center;
 flex-wrap: wrap;
 background-image: url(https://images.pexels.com/photos/216798/pexels-photo-216798.jpeg?w=1260&h=750&auto=compress&cs=tinysrgb);
 background-size: cover; 
}

.form {
 width: 35vw;
 margin: 0 auto;
 padding: 40px;
 /*box-shadow:20px 20px 50px grey;  */
 background-color: white;
}

.form input[type=text], input[type=password] {
 width: 100%;
 padding: 12px 20px;
    margin: 8px 0;
    box-sizing: border-box;
    border: 3px solid #ccc;
    -webkit-transition: 0.5s;
    transition: 0.5s;
    outline: none;
}

.form input[type=text]:focus, input[type=password]:focus {
    border: 3px solid #555;
}

.form input[type=submit] {
 width: 100%;
 background-color: #555;
    border: none;
    color: white;
    padding: 16px 32px;
    text-decoration: none;
    margin: 16px 2px 4px 2px;
    cursor: pointer;
    outline: none;
    transition: 0.5s;
}

.form input[type=submit]:hover {
 background-color: #123;
}

La parte en la que nos centraremos es en el siguiente método ubicado en SignUpPage.js:

import React, {Component} from 'react';
...
import AuthApi from '../api/authRepository';


class SignUpPage extends Component {
    ...
    onSubmit = (e) => {
        e.preventDefault();
        const user = Object.assign({},this.state.user);
        user.username = user.email;
        AuthApi.signUp(user)
        .then(r => {
            alert('Usuario creado');
        }).catch(e => {
            alert('Algo mal');
        });

    }
    ...

}

export default SignUpPage;

Esta función se ejecuta cuando nuestro formulario es enviado (cuando se da clic en el botón de registrar). Vamos a analizar linea por linea:

onSubmit = (e) => {} Esto define nuestra función la cual recibe un evento de nuestro botón de html.
e.preventDefault(); Cancela la acción por defecto. En un formulario, cuando los datos son enviados se espera que un lenguaje del lado del servidor maneje estos datos y posteriormente ejecute una acción en la base de datos. Como no estamos trabajando con un lenguaje del lado del servidor que maneje estos datos, por ello se cancela la acción por defecto y así podemos redirigir la información hacia nuestro endpoint.
const user = Object.assign({},this.state.user); Object.assign clona un objeto de javascript. El primer argumento es el objeto destino y el segundo argumento es el objeto a clonar. React sugiere que el state de un componente es inmutable, por esta razón y para evitar errores en nuestros componentes el state se debe clonar. En este ejemplo estamos clonando al objeto user de nuestro state.
user.username = user.email; Lo que se hace en esta linea es hacer que nuestro username sea igual a nuestro email, esto lo hacemos debido a que el
que tiene la función del registro requiere que le pasemos un objeto conteniendo email, username, password1 y password2.
AuthApi.signUp(user) Esta linea de código hace la llamada al método signUp que definimos en authRepository.js. Si analizamos detalladamente el método:

    const signUp = user => {  
        return new Promise((resolve, reject) => { 

Creamos y retornamos una nueva promesa de JavaScript.

...
let baseUrl = 'http://localhost:8000/rest-auth'; 
...
const instance = axios.create({  
    baseURL: baseUrl,  
    headers: {  
        'Content-Type': 'application/json'  
    }  
});

Creamos una nueva instancia de axios mediante la función axios.create. Esta función recibe un objeto con la información necesaria para crear la petición a la API. Para este caso mandamos la baseUrl ( url base de nuestro endpoint ) y los headers. En los headers mandamos el tipo de contenido y especificamos que la información que vamos a enviar/recibir está en formato JSON.

instance.post('registration/', user)  
    .then(r => {  
        localStorage.setItem(tokenName, JSON.stringify(r.data.key));  
        resolve(r.data);  
    }).catch(e => {  
        console.log(e);  
        reject(e.response);  
    });   

Por último realizamos la petición deseada. En este caso queremos hacer un post a nuestra API, por lo que ejecutamos el método post desde nuestra instancia creada con anterioridad. El método post() recibe dos parámetros. El primero es la extensión que se agregará a nuestra url base, en este caso nuestra url quedaría como http://localhost:8000/rest-auth/registration/. El segundo es el objeto con la información requerida (dependiendo los datos que necesitemos). En este caso, user contiene username, email, password1, y password2.
Axios también maneja promesas, es por eso que tenemos los métodos then y catch los cuales nos indican si la petición a la API fue exitosa o no. then y catch reciben una función o función flecha con los resultados. then envía la respuesta en r.data y catch en e.response. En el caso en que la petición fue exitosa vamos a guardar nuestra respuesta en nuestro web storage mediante la función localStorage.setItem(tokenName, JSON.stringify(r.data.key));. y dado que en pasos anteriores creamos una nueva promesa tenemos que indicar cuando una promesa fue exitosa y cuándo no. Para ello si es exitosa llamamos el método resolve y como argumento mandamos la respuesta y si fue erronea ejecutamos reject y como argumento pasamos la razón por la cual dio error.

Creando un login

Creamos una carpeta llamada login dentro de src y creamos dos archivos. LoginPage.js, LoginForm.js .
Básicamente el login es muy similar al login:

LoginPage.js

import React, {Component} from 'react';
import {LoginForm} from './LoginForm';
import AuthApi from '../api/authRepository';
import '../signup/SignUp.css';

class LoginPage extends Component {
    constructor(props){
        super(props);
        this.state = {
            user: {
                username: '',
                email: '',
                password: '',
            }
        };
    }

    onChange = (e) => {
        let user = Object.assign({},this.state.user);
        user[e.target.name] = e.target.value;
        this.setState({user});
    };

    onSubmit = (e) => {
        e.preventDefault();
        const user = Object.assign({},this.state.user);
        user.username = user.email;
        AuthApi.logIn(user)
            .then(r => {
                alert('Usuario creado');
                this.props.history.push('/');
            }).catch(e => {
                alert('Algo mal');
        });

    };

    render(){
        return (
            <div className="container">
                <LoginForm
                    user={this.state.user}
                    onChange={this.onChange}
                    onSubmit={this.onSubmit}
                />
            </div>
        );
    }

}

export default LoginPage;

La diferencia con signup page es que ejecutamos el método logIn en lugar de signUp de nuestro authRepository.js y ahora nuestro objeto user de nuestro state local solo contiene username, email y password.

LoginForm.js

import React from 'react';

export const LoginForm = ({onChange,onSubmit, user:{email,password}}) => (
    <form className="form" onSubmit={onSubmit}>
        <label htmlFor="email">Email</label>
        <input
            type="text"
            id="email"
            name="email"
            value={email}
            onChange={onChange}
        />
        <label htmlFor="password1">Contraseña</label>
        <input
            type="password"
            name="password"
            id="password"
            value={password}
            onChange={onChange}
        />
        <input
            type="submit"
            value="Iniciar Sesión"
        />
    </form>
);

Y listo tenemos nuestro sistema de registro y acceso listo.

Comentarios

Entradas populares de este blog

Creando una REST API con Django

Creando una app con React