Construir una simple aplicación CRUD usando React y Node

react and node

Índice

En este artículo aprenderemos a crear una aplicación CRUD sencilla con React.js, Node.js y TailwindCSS.

¿Qué es una aplicación CRUD?

Una aplicación CRUD realiza cuatro operaciones básicas: crear, leer, actualizary eliminar. Muchas aplicaciones con las que interactúas a diario realizan estas tareas básicas. Por ejemplo, Instagram. Puedes Crear perfiles y mensajes almacenados en la base de datos, Leer tus perfiles y mensajes y los de otros usuarios. Estos detalles se recuperan de los datos creados almacenados. También puedes Actualizar y Eliminar tus publicaciones, fotos de perfil, etc.

También te puede interesar: “Integración de PayPal Checkout con React”

Además de requerir un Front-End y un servidor, estas funciones necesitan una base de datos como MongoDB para almacenar, recuperar, actualizar y eliminar los datos enviados por los usuarios. Como puedes imaginar, esto requiere un gran esfuerzo de configuración, por lo que me alegré cuando descubrí una sencilla API REST falsa que podemos utilizar para realizar tareas CRUD básicas para nuestra aplicación.

Objetivo

Nuestro objetivo hoy es aprender las siguientes tareas:

  • Instalar paquetes básicos con Node.js
  • Crea una excelente interfaz de usuario usando React y Tailwind CSS
  • Realiza operaciones CRUD básicas con nuestra app.

Requisitos previos

  • Conocimientos de React js y React Hooks.
  • React 16.8 o posterior

Crear una aplicación React

Empecemos usando el boilerplate de React CRA para crear nuestra React App. Como vamos a utilizar Tailwind CSS, seguiremos los pasos de la Documentación Oficial para que la instalación sea fluida.

Empieza con,

npx create-react-app my-react-crudapp
cd my-react-crudapp

Abra la carpeta en su IDE favorito y complete el resto de los pasos. A continuación, tenemos que instalar React Router DOM – una funcionalidad de enrutamiento que nos ayuda a navegar por nuestra aplicación.

npm install react-router-dom

Ejecuta, npm run start, y deberías obtener la popular Welcome React Page como se muestra a continuación.

Aplicación React

En primer lugar, crearemos nuestro componente Navigation. Para ello, utilizaremos componentes de la página de documentación de Tailwind en la medida de lo posible.

Crea un componente Naviagte.js en tu carpeta src.

import React from "react";
export default function Navigate(){
   return (
       <nav class="flex items-center justify-between flex-wrap bg-green-500 p-6">
           <div class="flex items-center flex-shrink-0 text-white mr-6">
               <span class="font-semibold text-xl tracking-tight">REACT CRUD APP</span>
           </div>          
           <div>
               <button class="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-green-500 hover:bg-white mt-4 lg:mt-0">
                   CREATE
               </button>
           </div>
       </nav>
   )
}

A continuación, llamaremos al react-router-dom y a nuestro Navigate.js en el Componente principal App.js .

import { Link } from "react-router-dom";
export default function Navigate(){
   return (
       <nav class="flex items-center justify-between flex-wrap bg-green-500 p-6">
       <div class="flex items-center flex-shrink-0 text-white mr-6">
           <span class="font-semibold text-xl tracking-tight">REACT CRUD APP</span>
       </div>
       <Link to="/">
           <button class="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-green-500 hover:bg-white mt-4 lg:mt-0">
               HOME
           </button>
       </Link>
       </nav>
   )
}
Aplicación React CRUD

OPERACIONES DE CRUDO

Es hora de empezar a construir nuestras funcionalidades CRUD. Estas son las llamadas a la API que haremos para conseguirlo.

React CRUD App API

En primer lugar, vamos a crear un UsersList.js que nos ayudará a leer los usuarios existentes ya proporcionados por la API para las pruebas. Una vez más, vamos a ser un componente oficial de interfaz de usuario para construir esto.

UsersList.js

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
 
function UsersList() {
   const [users, setUsers] = useState([]);
   useEffect(() => {
       ReadUsers()
   }, [])
 
   const ReadUsers = () => {
       fetch("https://www.mecallapi.com/api/users")
         .then(res => res.json())
         .then(
           (result) => {
             setUsers(result)
           }
         )
   }
   return (
       <div class="flex flex-col">
           <div class="-my-2 overflow-x-auto">
               <div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
                   <div class="lg:flex lg:items-center lg:justify-between px-20 py-2">
                       <div class="flex-1 min-w-0">
                           <h2 class="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:truncate">
                           Users List
                           </h2>
                       </div>
                       <div class="mt-5 flex lg:mt-0 lg:ml-4">
                           <span class="hidden sm:block">
                           <Link to="/create">
                               <button type="button" class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                                   <svg class="-ml-1 mr-2 h-5 w-5 text-gray-500"  viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
                                   <path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
                                   </svg>
                                   CREATE
                               </button>
                           </Link>
                           </span>
                       </div>
                   </div>
               <div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
                   <table class="min-w-full divide-y divide-gray-200">
                   <thead class="bg-gray-50">
                       <tr>
                       <th scope="col" class="px-10 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                           ID
                       </th>
                       <th scope="col" class="px-10 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                           Full Name
                       </th>
                       <th scope="col" class="px-6 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                           Email Address
                       </th>
                      
                       <th scope="col" class="relative px-6 py-1">
                           <span class="sr-only">Edit</span>
                       </th>
                       </tr>
                   </thead>
                   <tbody class="bg-white divide-y divide-gray-200">
                   {users.map((user) => (
                       <tr key={user.ID}>
                           <td class="px-10 py-4 whitespace-nowrap">
                               <div class="text-sm text-gray-500">
                                   {user.id}
                               </div>
                           </td>
                           <td class="px-6 py-4 whitespace-nowrap">
                               <div class="flex items-center">
                               <div class="flex-shrink-0 h-10 w-10">
                                   <img class="h-10 w-10 rounded-full" src={user.avatar} alt=" />
                               </div>
                               <div class="ml-4">
                                   <div class="text-sm font-medium text-gray-900">
                                  <span> {user.fname}</span> <span>{user.lname}</span>
                                   </div>
                               </div>
                               </div>
                           </td>
                           <td class="px-6 py-4 whitespace-nowrap">
                               <div class="text-sm text-gray-500">
                                   {user.username}
                               </div>
                           </td>
                           <td class="px-6 py-4 space-x-2 whitespace-nowrap text-right text-sm font-medium">
                               <button class="inline-block text-sm px-4 py-2 leading-none border rounded text-blue-800 border-blue-600 hover:bg-blue-300 hover:text-blue-500 mt-4 lg:mt-0">EDIT</button>
                               <button class="inline-block text-sm px-4 py-2 leading-none border rounded text-red-800 border-red-600 hover:bg-red-300 hover:text-red-500 mt-4 lg:mt-0">DELETE</button>
                           </td>
                       </tr>
                       ))}
                   </tbody>
                   </table>
               </div>
               </div>
           </div>
       </div>
   )
}
 
export default UsersList;

En el bloque de código anterior, usamos el React useEffect Hook para obtener la lista de usuarios una vez al cargar. A continuación, almacenamos los datos recibidos mediante un gancho useState y los asignamos a los componentes de tabla proporcionados por Tailwind CSS. En secciones posteriores, utilizaremos los botones Editar y Eliminar y Crear que hemos creado.

Aplicación React CRUD

CrearUsuario.js

A continuación, crearemos CreateUser.js que nos permitirá crear un usuario.

import React, { useState } from "react";
 
function CreateUser() {
  
   const handleSubmit = event => {
     event.preventDefault();
     var data = {
       'fname': fname,
       'lname': lname,
       'username': username,
       'email': email,
       'avatar': avatar,
     }
 
     fetch('https://www.mecallapi.com/api/users/create', {
       method: 'POST',
       headers: {
         Accept: 'application/form-data',
         'Content-Type': 'application/json',
       },
       body: JSON.stringify(data),
     })
     .then(res => res.json())
     .then(
       (result) => {
         alert(result['message'])
         if (result['status'] === 'ok') {
           window.location.href = '/';
         }
       }
     )
   }
    const [fname, setFname] = useState('');
   const [lname, setLname] = useState('');
   const [username, setUsername] = useState('');
   const [email, setEmail] = useState('');
   const [avatar, setAvatar] = useState('');
 
   return (
       <form class="w-full max-w-lg mx-auto my-20" onSubmit={handleSubmit}>
       <div class="flex flex-wrap mx-3 mb-2">
           <div class="w-full md:w-1/2 px-3 mb-3 md:mb-0">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
               First Name
           </label>
           <input
           class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name"
           type="text"
           placeholder="First Name"
           onChange={(e) => setFname(e.target.value)}
           label="First Name"
           />
              
          
           </div>
           <div class="w-full md:w-1/2 px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
           for="grid-last-name">
               Last Name
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name"
           type="text" placeholder="Last Name"
           onChange={(e) => setLname(e.target.value)}
           label="Last Name"
           />
           </div>
       </div>
       <div class="flex flex-wrap mx-3 mb-2">
           <div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
               <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
                   Email Address
               </label>
               <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name"
               type="email"  placeholder="Email address"
               onChange={(e) => setEmail(e.target.value)}
               label="Email"
               />
           </div>
           <div class="w-full md:w-1/2 px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-last-name">
               Username
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name"
           label="Username"
           onChange={(e) => setUsername(e.target.value)}
           type="text" placeholder="janeDoe"
           />
           </div>
       </div>
       <div class="flex flex-wrap mx-3 mb-1">
           <div class="w-full px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-image">
               Avatar
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-image" type="text"
           placeholder="Image link"
           label="Avatar"
           onChange={(e) => setAvatar(e.target.value)}
           />
           </div>
       </div>
       <button type="submit" class="inline-flex items-center ml-8 px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
           SUBMIT
       </button>
      
       </form>
   )
}
 
export default CreateUser;

En el bloque de código anterior, creamos un formulario y agregamos sus valores de entrada a las variables de estado correspondientes utilizando el gancho UseState. A continuación, enviamos estos valores a la API REST CRUD para crear un usuario.

También importaremos y utilizaremos el Componente recién creado en nuestro App.js:

import CreateUser from './CreateUser'
………………….
<Route path='/create' element={<CreateUser />} />
Crear página

ActualizarUsuario.js

El siguiente paso es crear un componente UpdateUser.js que nos permitirá editar con éxito los usuarios creados.

En primer lugar, crearemos una función updateUser en UsersLists.js

const UpdateUser = id => {
   window.location = '/update/'+id
 }

y pasarlo al botón de edición.

onClick={() => UpdateUser(user.id)}

Ahora, podemos crear nuestro UpdateUser.js

import React, { useState, useEffect } from "react";
import { useParams } from 'react-router-dom';
 
function UpdateUser() {
    const { id } = useParams();
 
   useEffect(() => {
     fetch("https://www.mecallapi.com/api/users/"+id)
       .then(res => res.json())
       .then(
         (result) => {
           setFname(result.user.fname)
           setLname(result.user.lname)
           setUsername(result.user.username)
           setEmail(result.user.email)
           setAvatar(result.user.avatar)
         }
       )
   }, [id])
    const handleSubmit = event => {
     event.preventDefault();
     var data = {
       'id': id,
       'fname': fname,
       'lname': lname,
       'username': username,
       'email': email,
       'avatar': avatar,
     }
     fetch('https://www.mecallapi.com/api/users/update', {
       method: 'PUT',
       headers: {
         Accept: 'application/form-data',
         'Content-Type': 'application/json',
       },
       body: JSON.stringify(data),
     })
     .then(res => res.json())
     .then(
       (result) => {
         alert(result['message'])
         if (result['status'] === 'ok') {
           window.location.href = '/';
         }
       }
     )
   }
    const [fname, setFname] = useState('');
   const [lname, setLname] = useState('');
   const [username, setUsername] = useState('');
   const [email, setEmail] = useState('');
   const [avatar, setAvatar] = useState('');
    return (
       <form class="w-full max-w-lg mx-auto my-20" onSubmit={handleSubmit}>
       <div class="flex flex-wrap mx-3 mb-2">
           <div class="w-full md:w-1/2 px-3 mb-3 md:mb-0">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
               First Name
           </label>
           <input
           class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name"
           type="text"
           value={fname}
           onChange={(e) => setFname(e.target.value)}
           label="First Name"
           />
              
          
           </div>
           <div class="w-full md:w-1/2 px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
           for="grid-last-name">
               Last Name
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name"
           type="text" value={lname}
           onChange={(e) => setLname(e.target.value)}
           label="Last Name"
           />
           </div>
       </div>
       <div class="flex flex-wrap mx-3 mb-2">
           <div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
               <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
                   Email Address
               </label>
               <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name"
               type="email"  value={email}
               onChange={(e) => setEmail(e.target.value)}
               label="Email"
               />
           </div>
           <div class="w-full md:w-1/2 px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-last-name">
               Username
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name"
           label="Username"
           onChange={(e) => setUsername(e.target.value)}
           type="text" value={username}
           />
           </div>
       </div>
       <div class="flex flex-wrap mx-3 mb-1">
           <div class="w-full px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-image">
               Avatar
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-image" type="text"
           value={avatar}
           label="Avatar"
           onChange={(e) => setAvatar(e.target.value)}
           />
           </div>
       </div>
       <button type="submit" class="inline-flex items-center ml-8 px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
           Update
       </button>
      
       </form>
   )
}
 
export default UpdateUser;

En el fragmento de código anterior, recuperamos y mostramos el ID de usuario actual del usuario cuyos datos deseamos editar. A continuación, actualizamos los valores utilizando las entradas del formulario realizadas por el usuario.

También importaremos y utilizaremos el Componente recién creado en nuestro App.js:

import UpdateUser from './UpdateUser'
………
<Route path='/update/:id' element={<UpdateUser />}/>
Página de actualización

BORRAR

Por último, activaremos el botón de suprimir. Esto no requiere un nuevo componente en su lugar, estaremos actualizando nuestro Componente UsersList.js.

const DeleteUser = id => {
       var data = {
         'id': id
       }
       fetch('https://www.mecallapi.com/api/users/delete', {
         method: 'DELETE',
         headers: {
           Accept: 'application/form-data',
           'Content-Type': 'application/json',
         },
         body: JSON.stringify(data),
       })
       .then(res => res.json())
       .then(
         (result) => {
           alert(result['message'])
           if (result['status'] === 'ok') {
               ReadUsers()
           }
         }
       )
     }

<button onClick={() => DeleteUser(user.id)} class="inline-block text-sm px-4 py-2 leading-none border rounded text-red-800 border-red-600 hover:bg-red-300 hover:text-red-500 mt-4 lg:mt-0">DELETE</button>

Demo

Resumen

Eso es todo por ahora. En conclusión, aprendemos a usar Node.js para instalar paquetes esenciales para nuestro React. A continuación, utilizamos React Hooks y React Router para crear y navegar por nuestros componentes. Por último, realizamos interacciones CRUD básicas para crear, leer, actualizar y eliminar datos de usuario.

Si estás interesado en seguir desarrollando tus habilidades con React y Node js, aquí tienes dos tutoriales nuestros que deberían ayudarte. Una rápida introducción a React Hooks y la integración de Stripe Checkout con React.

Unimedia Technology

En Unimedia Technology contamos con un equipo deDesarrolladores Web que pueden ayudarte a desarrollar tus Aplicaciones React más complejas.

Recuerda que en Unimedia somos expertos en tecnologías emergentes, así que no dudes en ponerte en contacto con nosotros si necesitas asesoramiento o servicios. Estaremos encantados de ayudarte.

Unimedia Technology

Su socio de desarrollo de software

Somos una consultora tecnológica de vanguardia especializada en arquitectura y desarrollo de software a medida.

Nuestros servicios

Suscríbase a nuestras actualizaciones

Mantente al día, informado y ¡demos forma juntos al futuro de la tecnología!

Let’s make your vision a reality!

Simply fill out this form to begin your journey towards innovation and efficiency.

Hagamos realidad tu visión.

Sólo tienes que rellenar este formulario para iniciar tu viaje hacia la innovación y la eficiencia.