<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Leonardo José Castillo Lacruz - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Descubre miles de cursos de programación escritos por expertos. Aprende Desarrollo Web, Ciencia de Datos, DevOps, Seguridad y obtén asesoramiento profesional para desarrolladores. ]]>
        </description>
        <link>https://www.freecodecamp.org/espanol/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Leonardo José Castillo Lacruz - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/espanol/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 11 May 2026 15:32:39 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/espanol/news/author/leonardo/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Docker y dockerizar - qué significan estos términos? ]]>
                </title>
                <description>
                    <![CDATA[  Docker es una de las herramientas más populares en el mundo del desarrollo de software, especialmente en el ámbito de la virtualización y la gestión de aplicaciones. Empecemos por lo básico, partiendo con las definiciones y avanzamos en aspectos particulares más adelante. -------------------------------------------------------------------------------- ¿Qué es Docker? Docker es una ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/dockerizando-una-aplicacion-node/</link>
                <guid isPermaLink="false">67dc10375e145c04f2f2ea88</guid>
                
                    <category>
                        <![CDATA[ docker ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Fri, 11 Apr 2025 20:41:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/04/Youtube--36-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2></h2><p>Docker es una de las herramientas más populares en el mundo del desarrollo de software, especialmente en el ámbito de la virtualización y la gestión de aplicaciones. Empecemos por lo básico, partiendo con las definiciones y avanzamos en aspectos particulares más adelante.</p><hr><h3 id="-qu-es-docker">¿Qué es Docker?</h3><p>Docker es una plataforma de código abierto que permite a los desarrolladores crear, implementar y ejecutar aplicaciones en contenedores, ¿pero qué es un contenedor?</p><p>Un contenedor es una unidad ligera y portátil que incluye todo lo necesario para que una aplicación funcione: código, bibliotecas, dependencias y configuraciones. Más adelante encontrarás una definición formal tanto de imagen como de contenedor.</p><p>Esto asegura que las aplicaciones se ejecuten de manera consistente en cualquier entorno, ya sea en tu máquina local, en un servidor o en la nube.</p><hr><h3 id="aspectos-importantes-de-usar-docker">Aspectos importantes de usar Docker</h3><p>Docker ha revolucionado la forma en que se desarrollan y despliegan aplicaciones debido a sus múltiples beneficios:</p><ul><li><strong>Portabilidad:</strong> Los contenedores Docker funcionan de la misma manera en cualquier sistema operativo que soporte Docker, eliminando problemas de compatibilidad.</li><li><strong>Eficiencia:</strong> Los contenedores son más ligeros que las máquinas virtuales, ya que comparten el kernel del sistema operativo, lo que reduce el uso de recursos.</li><li><strong>Escalabilidad:</strong> Facilita la creación de entornos replicables, lo que permite escalar aplicaciones rápidamente.</li><li><strong>Aislamiento:</strong> Cada contenedor es independiente, lo que evita conflictos entre aplicaciones o dependencias.</li><li><strong>Automatización:</strong> Docker simplifica los procesos de integración y entrega continua (CI/CD).</li></ul><hr><h3 id="gu-a-r-pida-de-instalaci-n">Guía rápida de instalación</h3><p>Para instalar Docker en tu sistema, sigue estos pasos básicos:</p><ol><li><strong>Linux (Ubuntu): </strong>bashCopy Codesudo apt update sudo apt install docker.io sudo systemctl start docker sudo systemctl enable docker</li><li><strong>Windows/Mac:</strong></li></ol><ul><li>Descarga Docker Desktop desde <a href="https://www.docker.com/" rel="noopener noreferrer">docker.com</a>.</li><li>Sigue las instrucciones del instalador.</li><li>Asegúrate de habilitar la integración con WSL 2 (en Windows).</li></ul><p><strong>Verifica la instalación:</strong><br>Ejecuta el siguiente comando para confirmar que Docker está instalado correctamente: bashCopy Codedocker --version</p><p>Quieres un paso a paso de instalar Docker, acá te comparto un contenido donde mostramos la instalación en Windows usando WSL2, así que funciona también para Linux.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/Z27M2qGwHW4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Primeros Pasos con Docker: Instala WSL y Docker en Windows + Comandos Básicos 🚀" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><hr><h3 id="-qu-es-una-imagen">¿Qué es una imagen?</h3><p>Una imagen en Docker es una plantilla inmutable que contiene todo lo necesario para ejecutar una aplicación, incluyendo el sistema operativo base, las dependencias y el código de la aplicación. Las imágenes son la base para crear contenedores y se almacenan en repositorios como Docker Hub.</p><hr><h3 id="-qu-es-un-contenedor">¿Qué es un contenedor?</h3><p>Un contenedor es una instancia en ejecución de una imagen. Es un entorno aislado que ejecuta una aplicación de manera independiente. Los contenedores son ligeros, rápidos y desechables, lo que significa que puedes detenerlos, eliminarlos y recrearlos fácilmente.</p><hr><h3 id="-c-mo-correr-un-contenedor-de-una-imagen">¿Cómo correr un contenedor de una imagen?</h3><p>Para ejecutar un contenedor a partir de una imagen, utiliza el siguiente comando:</p><pre><code>docker run -d --name nombre_del_contenedor imagen</code></pre><p>Por ejemplo, para ejecutar un servidor web Nginx:</p><pre><code>docker run -d --name mi_nginx -p 8080:80 nginx</code></pre><p>Este comando ejecuta un contenedor en segundo plano (<code>-d</code>), lo nombra como <code>mi_nginx</code> y mapea el puerto 80 del contenedor al puerto 8080 de tu máquina. </p><p>Con esto tendras un servidor web en tu máquina ejecutandose en el puerto 8080.</p><hr><h3 id="-qu-es-dockerizar-una-aplicaci-n">¿Qué es dockerizar una aplicación?</h3><p>Dockerizar una aplicación significa empaquetarla en un contenedor Docker para que pueda ejecutarse de manera consistente en cualquier entorno. Esto incluye agregar todas las dependencias, configuraciones y scripts necesarios en una imagen Docker.</p><hr><h3 id="-qu-es-el-dockerfile">¿Qué es el Dockerfile?</h3><p>El <code>Dockerfile</code> es un archivo de texto que contiene una serie de instrucciones para construir una imagen Docker. Cada instrucción en el Dockerfile define una capa en la imagen, lo que permite que Docker reutilice capas anteriores para optimizar el proceso de construcción.</p><hr><h3 id="comandos-b-sicos-del-dockerfile">Comandos básicos del Dockerfile</h3><p>Algunos comandos comunes en un Dockerfile son:</p><ul><li><strong>FROM:</strong> Define la imagen base. dockerfileCopy CodeFROM node:16</li><li><strong>WORKDIR:</strong> Establece el directorio de trabajo dentro del contenedor. dockerfileCopy CodeWORKDIR /app</li><li><strong>COPY:</strong> Copia archivos desde el sistema host al contenedor. dockerfileCopy CodeCOPY . .</li><li><strong>RUN:</strong> Ejecuta comandos durante la construcción de la imagen. dockerfileCopy CodeRUN npm install</li><li><strong>CMD:</strong> Define el comando que se ejecutará cuando el contenedor inicie. dockerfileCopy CodeCMD ["npm", "start"]</li></ul><hr><h3 id="-c-mo-dockerizar-una-aplicaci-n-node-js">¿Cómo dockerizar una aplicación Node.js?</h3><p>Para dockerizar una aplicación Node.js, sigue estos pasos:</p><p><strong>Crea un archivo <code>Dockerfile</code>:</strong><br>Este archivo define cómo se construirá la imagen Docker. Un ejemplo básico para una aplicación Node.js sería:</p><pre><code># Usa una imagen base de Node.js 
FROM node:16 

# Establece el directorio de trabajo 
WORKDIR /app 

# Copia la definición del proyecto 
COPY package*.json ./ 

#Carga las dependencias
RUN npm install 

# Copia los archivos de la aplicación 
COPY . . 

# Expone el puerto de la aplicación 
EXPOSE 3000 

# Comando para ejecutar la aplicación 
CMD ["npm", "start"]</code></pre><p><strong>Crea un archivo <code>.dockerignore</code>:</strong><br>Este archivo especifica qué archivos o carpetas deben excluirse al construir la imagen. Por ejemplo:</p><pre><code>node_modules  
npm-debug.log  
</code></pre><p><strong>Construye la imagen:</strong></p><pre><code>docker build -t mi_app_node .</code></pre><p><strong>Ejecuta el contenedor:</strong></p><pre><code>docker run -d -p 3000:3000 mi_app_node</code></pre><hr><h3 id="importancia-del-archivo-dockerignore">Importancia del archivo <code>.dockerignore</code></h3><p>El archivo <code>.dockerignore</code> es crucial para optimizar el proceso de construcción de imágenes Docker. Este archivo especifica qué archivos o carpetas deben excluirse al construir la imagen, lo que reduce el tamaño de la imagen y mejora la seguridad al evitar incluir archivos sensibles o innecesarios, como:</p><ul><li>Archivos de configuración locales.</li><li>Dependencias ya instaladas (como <code>node_modules</code>).</li><li>Archivos temporales o de depuración.</li></ul><p>Un ejemplo de <code>.dockerignore</code> sería:</p><pre><code>node_modules  
*.log  
.env  
</code></pre><hr><p>Veamos un ejemplo práctico de como dockerizar una aplicación Node.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/tpai8uzkROA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="¡Dockeriza tu Aplicación como un PRO! 🐳 Aprende Dockerfile, .dockerignore y Más 🚀" name="fitvid1"></iframe>
          </div>
        </div>
      </figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Rutas en Angular. Como implementarlas ]]>
                </title>
                <description>
                    <![CDATA[ Paso a paso de como realizar la implementación de rutas para diferentes vistas en aplicaciones Angular. ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/rutas-en-angular-como-implementarlas/</link>
                <guid isPermaLink="false">67a4ca84ef185e040575d777</guid>
                
                    <category>
                        <![CDATA[ angular ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Tue, 11 Feb 2025 18:39:09 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/02/Youtube--29-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="introducci-n-a-las-rutas-en-angular">Introducción a las Rutas en Angular</h2><p>Las aplicaciones en general presentan diferentes vistas, en el caso de aplicaciones web de tipo SPA (Single Page Application), el poder cambiar de vistas es un proceso normal, pero que por la naturaleza del tipo de aplicación requiere algunas tomar algunas consideraciones. En Angular, este proceso es realizado por el sistema de enrutamiento (<code>Angular Router</code>), el cual permite gestionar la navegación entre diferentes vistas sin necesidad de recargar la página (principio básico de las SPA)</p><h3 id="-c-mo-funciona-el-enrutamiento-en-angular"><strong>¿Cómo funciona el enrutamiento en Angular?</strong></h3><p>El enrutamiento en Angular se basa en la manipulación del historial del navegador utilizando la API de <code>History</code> de JavaScript. Cuando el usuario cambia de ruta, Angular intercepta la petición y renderiza el componente correspondiente dentro del <code>RouterOutlet</code> sin necesidad de recargar la página.</p><h2 id="tipos-de-rutas-en-angular"><strong>Tipos de Rutas en Angular</strong></h2><p>En Angular, existen diferentes tipos de rutas que podemos implementar según nuestras necesidades:</p><h3 id="-rutas-est-ticas"><strong>🔹 Rutas Estáticas</strong></h3><p>Son rutas predefinidas en la aplicación y no cambian en función de los datos o acciones del usuario. Se definen con un <code>path</code> o camino fijo.</p><p>Ejemplo:</p><pre><code>const routes: Routes = [ 
	{ path: 'home', component: HomeComponent }, 
    { path: 'about', component: AboutComponent } 
];</code></pre><h3 id="-rutas-din-micas"><strong>🔹 Rutas Dinámicas</strong></h3><p>Permiten que un componente reciba parámetros en la URL, lo que es útil para mostrar información específica, como detalles de un producto o perfil de usuario.</p><p>Ejemplo:</p><pre><code>const routes: Routes = [ 
	{ path: 'producto/:id', component: ProductoComponent } 
];</code></pre><h3 id="-rutas-programadas-program-ticas-"><strong>🔹 Rutas Programadas (Programáticas)</strong></h3><p>Nos permiten cambiar de ruta desde el código de TypeScript sin necesidad de que el usuario haga clic en un enlace. Se usa el servicio <code>Router</code> para navegar de manera dinámica.</p><p>Ejemplo:</p><pre><code>this.router.navigate(['/detalle-producto', id]);</code></pre><h2 id="elementos-claves-para-implementar-rutas-en-angular"><strong>Elementos claves para implementar rutas en Angular</strong></h2><p>Para implementar rutas en Angular, debemos conocer los siguientes conceptos y herramientas:</p><h3 id="-routeroutlet"><strong>📌 <code>RouterOutlet</code></strong></h3><p>Es una directiva que actúa como un contenedor donde se renderizan los componentes en función de la ruta activa. Se coloca en el <code>app.component.html</code> o en cualquier otro lugar donde queramos que aparezcan las vistas dinámicas.</p><pre><code>&lt;router-outlet&gt;&lt;/router-outlet&gt;</code></pre><h3 id="-routerlink"><strong>📌 <code>routerLink</code></strong></h3><p>Es una directiva de Angular que permite definir enlaces de navegación dentro de la aplicación sin recargar la página.</p><pre><code>&lt;a routerLink="/home"&gt;Ir a Inicio&lt;/a&gt;</code></pre><h3 id="-activatedroute"><strong>📌 <code>ActivatedRoute</code></strong></h3><p>Es un servicio que nos permite acceder a los parámetros de la URL dentro de un componente.</p><pre><code>constructor(private route: ActivatedRoute) { 
} 

ngOnInit() { 
	const id = this.route.snapshot.paramMap.get('id'); 
}</code></pre><h3 id="-router"><strong>📌 <code>Router</code></strong></h3><p>Es un servicio que nos permite cambiar de ruta de forma programática. Simplemente, lo importamos en nuestro componente y hacemos uso del mismo.</p><pre><code>this.router.navigate(['/dashboard']);</code></pre><hr><p>Ahora tomemos hagamos la implementación en una aplicación real.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/JQ6Vrwde424?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Cómo Implementar Rutas en Angular: Estáticas, Dinámicas y Programadas 🚀 | Guía Completa con Ejemplo" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h3 id="configuraci-n-inicial"><strong>Configuración Inicial</strong></h3><p>Primero, creamos un nuevo proyecto de Angular si no lo tenemos:</p><pre><code>ng new mi-proyecto-angular
cd mi-proyecto-angular
ng serve</code></pre><p>Luego, creamos los siguientes componentes para las diferentes vistas, nos aprovechamos del cliente Angular para realizar esta tarea, en nuestro caso vamos a crear un componente para una ruta estática y otro para una ruta dinámica:</p><pre><code>ng generate component components/quienes-somos 
ng generate component components/detalle-mascota</code></pre><h3 id="configuraci-n-del-m-dulo-de-rutas-app-routing-ts-"><strong>Configuración del Módulo de Rutas (<code>app-routing.ts</code>)</strong></h3><p>Definimos las rutas de la aplicación en el archivo <code>app-routing.module.ts</code>:</p><pre><code>import { Routes } from '@angular/router';
import { QuienesSomosComponent } from './components/quienes-somos/quienes-somos.component';
import { MascotasListComponent } from './components/mascotas-list/mascotas-list.component';
import { DetalleMascotaComponent } from './components/detalle-mascota/detalle-mascota.component';
import { FormularioComponent } from './components/formulario/formulario.component';

export const routes: Routes = [
    { path: '', component: MascotasListComponent },
    { path: 'mascota/:id', component: DetalleMascotaComponent },
    { path: 'quienes-somos', component: QuienesSomosComponent },
    { path: 'adopcion/:id', component: FormularioComponent }
];
</code></pre><h3 id="uso-de-routerlink-en-la-barra-de-navegaci-n"><strong>Uso de <code>routerLink</code> en la Barra de Navegación</strong></h3><p>En <code>navbar.component.html</code>, agregamos enlaces de navegación con <code>routerLink</code>:</p><pre><code>&lt;nav&gt;  
	&lt;a routerLink="/"&gt;Inicio&lt;/a&gt;  
    &lt;a routerLink="/quienes-somos"&gt;Quiénes Somos&lt;/a&gt;
&lt;/nav&gt;</code></pre><h3 id="mostrar-componentes-con-routeroutlet"><strong>Mostrar Componentes con <code>RouterOutlet</code></strong></h3><p>En <code>app.component.html</code>, añadimos el <code>RouterOutlet</code> para mostrar las vistas dinámicamente:</p><pre><code>&lt;app-navbar&gt;&lt;/app-navbar&gt;
&lt;router-outlet&gt;&lt;/router-outlet&gt;
&lt;app-footer&gt;&lt;/app-footer&gt;</code></pre><h3 id="implementar-una-ruta-din-mica"><strong>Implementar una Ruta Dinámica</strong></h3><p>En <code>detalle-mascota.component.ts</code>, obtenemos el ID de la mascota desde la URL usando <code>ActivatedRoute</code>:</p><pre><code>export class DetalleMascotaComponent implements OnInit {
  mascota?: Mascota;
  loading: boolean = true;

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private mascotaService: MascotaService
  ) { }

  ngOnInit(): void {
    const id = this.route.snapshot.paramMap.get('id');

    if (id) {
      this.mascotaService.
        getMascotaById(Number(id)).
        subscribe({
          next: (mascota) =&gt; {
            this.mascota = mascota;
            this.loading = false;
          }
        })
    }
  }
</code></pre><p>Y en <code>mascota.component.html</code>, enlazamos a la ruta dinámica usando <code>routerLink</code>:</p><pre><code>&lt;a [routerLink]="['/mascota', mascota.id]"&gt;
	Ver detalles de {{ mascota.nombre }}
&lt;/a&gt;</code></pre><h3 id="navegaci-n-program-tica-con-router"><strong>Navegación Programática con <code>Router</code></strong></h3><p>Para cambiar de ruta desde TypeScript, usamos el servicio <code>Router</code>. Por ejemplo, si queremos redirigir al usuario a un formulario de adopción:</p><pre><code>import { Router } from '@angular/router'; 

export class DetalleMascotaComponent { 
	constructor(private router: Router) {
    } 
    
    irAFormulario() { 
    	this.router.navigate(['/adopcion', this.id]); 
    } 
}</code></pre><p>Y en <code>detalle-mascota.component.html</code>:</p><pre><code>&lt;button (click)="irAFormulario()"&gt;Adoptar&lt;/button&gt;</code></pre><hr><p>Siguiendo este paso a paso, podrás implementar los tipos de rutas que Angular nos permite construir y con ello lograrás mejorar la usabilidad de tus aplicaciones.</p><p>Si te gustó el contenido, compártelo en tus redes.</p><p>Para cualquier duda me puedes contactar por:</p><ul><li><a href="https://www.linkedin.com/in/leonardo-castillo-4911571a/">LinkedIn</a></li><li><a href="https://www.youtube.com/leonardocastillo79">Mi canal de YouTube</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Introducción a los Hooks Avanzados ]]>
                </title>
                <description>
                    <![CDATA[ Explorando useMemo, useCallback y useLayoutEffect en React React nos ha proporcionado herramientas poderosas para desarrollar aplicaciones web interactivas y eficientes. Con la introducción de los hooks, se abrió un mundo de posibilidades para manejar estados, efectos y funciones de manera más declarativa. Más allá de los hooks básicos (useState, useEffect), ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/introduccion-a-los-hooks-avanzados/</link>
                <guid isPermaLink="false">677b070dd2e79604442d277b</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Mon, 13 Jan 2025 14:49:22 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/01/portada.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="explorando-usememo-usecallback-y-uselayouteffect-en-react">Explorando useMemo, useCallback y useLayoutEffect en React</h2><p>React nos ha proporcionado herramientas poderosas para desarrollar aplicaciones web interactivas y eficientes. Con la introducción de los hooks, se abrió un mundo de posibilidades para manejar estados, efectos y funciones de manera más declarativa. Más allá de los hooks básicos (<code>useState</code>, <code>useEffect</code>), existen otros más avanzados como <code>useMemo</code>, <code>useCallback</code> y <code>useLayoutEffect</code>, diseñados para abordar desafíos específicos relacionados con el rendimiento y la experiencia del usuario.</p><p>En este artículo, exploraremos estos tres hooks en profundidad, desde sus fundamentos teóricos hasta su implementación práctica en una aplicación React que simula un ecommerce.</p><hr><h4 id="1-usememo-optimizando-c-lculos-innecesarios"><strong>1. useMemo: Optimizando cálculos innecesarios</strong></h4><p><strong>¿Qué es?</strong></p><p><code>useMemo</code> es un hook que nos permite memorizar el resultado de una función costosa para evitar que se recalculen innecesariamente en cada renderizado. Es especialmente útil cuando trabajamos con grandes conjuntos de datos o funciones intensivas en computación.</p><p><strong>¿Cómo funciona?</strong></p><p>Cuando React renderiza un componente, todas las funciones dentro del componente se ejecutan. Si una función realiza cálculos pesados, esto puede afectar significativamente el rendimiento. Aquí es donde entra <code>useMemo</code>, que guarda ("memoriza") el resultado de una función y solo la vuelve a ejecutar si alguna de las dependencias cambia.</p><p><strong>Características clave:</strong></p><ul><li>Memoriza valores computados.</li><li>Se usa principalmente para optimizar componentes que dependen de cálculos intensivos.</li><li>Acepta dos argumentos: una función y un arreglo de dependencias.</li></ul><p><strong>Ejemplo básico:</strong></p><pre><code>const memoizedValue = useMemo(() =&gt; { 
	return computeExpensiveValue(a, b); 
}, [a, b]);</code></pre><p>En este caso, <code>computeExpensiveValue</code> solo se recalculará cuando <code>a</code> o <code>b</code> cambien.</p><hr><h4 id="2-usecallback-memoriza-funciones"><strong>2. useCallback: Memoriza funciones</strong></h4><p><strong>¿Qué es?</strong></p><p><code>useCallback</code> se utiliza para memorizar funciones, evitando que React cree nuevas instancias de la función en cada renderizado. Esto es útil cuando pasamos funciones como props a componentes hijos, ya que previene que estos se re-rendericen innecesariamente.</p><p><strong>¿Por qué es importante?</strong></p><p>En React, cuando un componente padre se vuelve a renderizar, React considera que cualquier función definida dentro de ese componente es "nueva". Esto puede llevar a que los componentes hijos que reciben esa función como prop también se re-rendericen, incluso si no es necesario.</p><p><strong>Características clave:</strong></p><ul><li>Memoriza la referencia de una función.</li><li>Es útil en combinación con <code>React.memo</code>, que optimiza componentes para no renderizarse si sus props no cambian.</li><li>Acepta dos argumentos: una función y un arreglo de dependencias.</li></ul><p><strong>Ejemplo básico:</strong></p><pre><code>const memoizedCallback = useCallback(() =&gt; { 
	doSomething(a, b); 
}, [a, b]);</code></pre><p>Aquí, la función <code>doSomething</code> se volverá a definir únicamente si <code>a</code> o <code>b</code> cambian.</p><hr><h4 id="3-uselayouteffect-ajustes-visuales-antes-del-renderizado"><strong>3. useLayoutEffect: Ajustes visuales antes del renderizado</strong></h4><p><strong>¿Qué es?</strong></p><p><code>useLayoutEffect</code> es similar a <code>useEffect</code>, pero se ejecuta de manera síncrona después de que React ha realizado todas las modificaciones al DOM y antes de que el navegador pinte la pantalla. Esto permite realizar ajustes visuales inmediatos, como calcular dimensiones o reposicionar elementos.</p><p><strong>¿Cuándo usarlo?</strong></p><p>Se utiliza en situaciones donde necesitas leer o escribir directamente en el DOM y esos cambios deben reflejarse antes de que el usuario vea algo en pantalla.</p><p><strong>Características clave:</strong></p><ul><li>Se ejecuta antes del renderizado visual.</li><li>Es útil para medir dimensiones, realizar cálculos de posición o sincronizar animaciones.</li><li>Acepta dos argumentos: una función y un array de dependencias.</li></ul><p><strong>Ejemplo básico:</strong></p><pre><code>useLayoutEffect(() =&gt; { 
	const dimension = element.getBoundingClientRect();
    console.log(dimension); 
}, [element]);</code></pre><hr><h3 id="implementaci-n-pr-ctica"><strong>Implementación Práctica</strong></h3><p>Para fortalecer el conocimiento, vamos a aplicar estos hooks en una aplicación React, &nbsp;un ecommerce que hemos venido desarrollando. El objetivo es mejorar el rendimiento y la experiencia del usuario utilizando <code>useMemo</code>, <code>useCallback</code> y <code>useLayoutEffect</code>.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/295yPSj4y9I?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Hooks Avanzados en React 🚀 | useMemo + useCallback + useLayoutEffect | Mejora Performance y UX" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p><strong>Contexto de la Aplicación:</strong></p><ul><li>Es un ecommerce que ya utiliza hooks como <code>useState</code> y <code>useEffect</code>.</li><li>Necesitamos agregar filtros y tooltips, los cuales pueden generar impacto en el performance.</li><li>Queremos optimizar cálculos, evitar renders innecesarios y ajustar elementos visuales dinámicamente.</li></ul><h4 id="1-aplicaci-n-de-usememo-filtros-din-micos"><strong>1. Aplicación de useMemo: Filtros dinámicos</strong></h4><p>En el componente <code>ProductFilters.jsx</code>, utilizamos <code>useMemo</code> para calcular las categorías y el rango de precios:</p><pre><code>const categories = useMemo(() =&gt; { 
	return [...new Set(products.map((product) =&gt; product.category))]; }, [products]); 
    
const priceRange = useMemo(() =&gt; { 
	const prices = products.map((product) =&gt; product.price); 
    return { min: Math.floor(Math.min(...prices)), 
    		 max: Math.ceil(Math.max(...prices)), 
    }; 
 }, [products]);</code></pre><p>Esto asegura que las categorías y los precios solo se recalculen cuando <code>products</code> cambie, mejorando el rendimiento en aplicaciones con grandes cantidades de datos.</p><hr><h4 id="2-aplicaci-n-de-usecallback-optimizaci-n-de-funciones"><strong>2. Aplicación de useCallback: Optimización de funciones</strong></h4><p>En <code>Home.jsx</code>, usamos <code>useCallback</code> para manejar cambios en los filtros:</p><pre><code>const handleCategoryChange = useCallback((category) =&gt; { 
	setCategory(category); 
}, []); 

const handlePriceChange = useCallback((price) =&gt; { 
	setMaxPrice(price); 
}, []);</code></pre><p>Esto evita que las funciones sean recreadas en cada renderizado, reduciendo renders innecesarios en el componente <code>ProductFilters</code>.</p><hr><h4 id="3-aplicaci-n-de-uselayouteffect-tooltips-ajustables"><strong>3. Aplicación de useLayoutEffect: Tooltips ajustables</strong></h4><p>En el componente <code>PriceTooltip.jsx</code>, utilizamos <code>useLayoutEffect</code> para ajustar la posición de un tooltip dentro del viewport:</p><pre><code>useLayoutEffect(() =&gt; { 
	if (showTooltip &amp;&amp; tooltipRef.current &amp;&amp; containerRef.current) { 
    	const containerRect = containerRef.current.getBoundingClientRect(); 
        const tooltipRect = tooltipRef.current.getBoundingClientRect(); 
        const newPosition = { 
        	top: -tooltipRect.height, 
        	left: containerRect.left + tooltipRect.width &gt; window.innerWidth ? -(tooltipRect.width - containerRect.width) : 0, 
        }; 
       	setTooltipPosition(newPosition); 
    } 
}, [showTooltip]);</code></pre><p>Esto asegura que los tooltips sean visibles y estén correctamente posicionados en todo momento.</p><hr><p>Al finalizar la implementación de filtros y mejoras visuales, usando la combinación de <code>useMemo</code>, <code>useCallback</code> y <code>useLayoutEffect</code>, logramos mejorar el perfomance de nuestra aplicación y en general estos hooks permiten crear aplicaciones React más rápidas, eficientes y visualmente atractivas. </p><p>Estos hooks avanzados no solo mejoran el rendimiento, sino que también optimizan la experiencia del usuario, llevándote un paso más allá en el desarrollo frontend. ¡Ponlos en práctica y observa cómo tus aplicaciones alcanzan un nuevo nivel!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Documentando APIs REST en Node.js con Swagger Autogen y Swagger UI Express ]]>
                </title>
                <description>
                    <![CDATA[ La documentación de APIs REST es una tarea esencial para garantizar que otros desarrolladores puedan consumir y comprender el funcionamiento de nuestras APIs. Una buena documentación no solo es un signo de profesionalismo, sino también una herramienta poderosa para optimizar la colaboración y la eficiencia. Vamos a aprender a documentar ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/documentando-apis-rest-en-node-js-con-swagger-autogen-y-swagger-ui-express/</link>
                <guid isPermaLink="false">6755a38c96d716043ef3d6ad</guid>
                
                    <category>
                        <![CDATA[ api rest ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Tue, 10 Dec 2024 02:26:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/12/Youtube--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>La documentación de APIs REST es una tarea esencial para garantizar que otros desarrolladores puedan consumir y comprender el funcionamiento de nuestras APIs. Una buena documentación no solo es un signo de profesionalismo, sino también una herramienta poderosa para optimizar la colaboración y la eficiencia.</p><p>Vamos a aprender a documentar una API REST creada en Node.js utilizando las herramientas <strong>Swagger Autogen</strong> y <strong>Swagger UI Express</strong>. Además, exploraremos las ventajas que estas herramientas ofrecen para facilitar el desarrollo y el consumo de APIs.</p><hr><h3 id="-qu-es-swagger">¿Qué es Swagger?</h3><p><strong>Swagger</strong> es un conjunto de herramientas para definir y documentar APIs REST bajo el estándar OpenAPI. Es importante destacar que Swagger no está ligado a un lenguaje de programación específico, sino que su objetivo principal es describir de manera clara cómo interactuar con los servicios de una API. Con Swagger, es posible crear documentación interactiva que permite a los desarrolladores explorar y probar los endpoints directamente desde el navegador.</p><blockquote><strong>Nota:</strong> Aunque Swagger facilita la visualización y uso de la documentación, no genera automáticamente el archivo de especificación de la API. Este trabajo es realizado por herramientas como <strong>Swagger Autogen</strong>, que automatiza la creación de dicho archivo JSON o YAML.</blockquote><hr><h3 id="ventajas-de-usar-swagger-y-swagger-autogen">Ventajas de usar Swagger y Swagger Autogen</h3><ol><li><strong>Automatización del proceso de documentación</strong><br>Con <strong>Swagger Autogen</strong>, puedes generar el archivo de especificación a partir de tu código, reduciendo la carga manual y garantizando que siempre esté actualizado.</li><li><strong>Interfaz interactiva con Swagger UI Express</strong><br>La integración con <strong>Swagger UI Express</strong> permite visualizar y probar los endpoints de manera fácil y amigable.</li><li><strong>Estandarización y compatibilidad</strong><br>Al seguir el estándar OpenAPI, las APIs documentadas con Swagger son fácilmente entendibles por otros desarrolladores y herramientas.</li><li><strong>Reducción de Errores y Mejor Colaboración</strong><br>La sincronización entre código y documentación minimiza malentendidos y agiliza la colaboración entre equipos.</li><li><strong>Experiencia mejorada para consumidores</strong><br>Los desarrolladores que consumen tu API pueden integrarse más rápido gracias a la claridad y accesibilidad de la documentación.</li></ol><hr><h3 id="c-mo-documentar-tu-api-rest-con-swagger-autogen-y-swagger-ui-express">Cómo documentar tu API REST con Swagger Autogen y Swagger UI Express</h3><h4 id="1-preparaci-n-del-proyecto">1. <strong>Preparación del proyecto</strong></h4><p><strong>Inicia un proyecto Node.js</strong><br>Si aún no tienes un proyecto, inicialízalo con:</p><p><code>npm init -y</code></p><p><strong>Instala las dependencias necesarias</strong><br>Usa los siguientes comandos para instalar las herramientas requeridas:</p><p><code>npm install swagger-ui-express npm install --save-dev swagger-autogen</code></p><hr><h4 id="2-configuraci-n-de-swagger-autogen">2. <strong>Configuración de Swagger Autogen</strong></h4><p><strong>Crea el Archivo de Configuración</strong><br>Añade un archivo <code>swagger.js</code> en el directorio raíz con el siguiente contenido:</p><pre><code>const swaggerAutogen = require('swagger-autogen')();

const doc = {
    info: {
        title: 'API de Mascotas',
        description: 'Documentación de la API para la gestión de mascotas',
    },
    host: 'localhost:3000',
    schemes: ['http'],
};

const outputFile = './swagger_output.json';
const endpointsFiles = ['./index.js']; // Cambia este archivo según el punto de entrada de tu API

swaggerAutogen(outputFile, endpointsFiles).then(() =&gt; {
    require('./index'); // Inicia el servidor automáticamente
});
</code></pre><p>En el script anterior, importamos el paquete swagger-autogen y definimos las configuraciones necesarias para realizar la construcción del archivo .json necesario para mostrar la documentación.</p><p><strong>Ejecuta el script de generación</strong><br>Ejecuta el siguiente comando para generar el archivo JSON de especificación:bashCopiar código<code>node swagger.js</code></p><p>Esto creará un archivo llamado <code>swagger_output.json</code> que contiene la descripción de los endpoints de tu API. Este archivo será usado por el paquete swagger-ui-express.</p><hr><h4 id="3-integraci-n-con-swagger-ui-express">3. <strong>Integración con Swagger UI Express</strong></h4><p><strong>Configura la ruta de documentación</strong><br>Modifica el archivo principal de tu API (<code>index.js</code>) para incluir el middleware de Swagger UI Express:</p><pre><code>const express = require('express');
const swaggerUi = require('swagger-ui-express');
const swaggerFile = require('./swagger_output.json');

const app = express();

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile));

app.listen(3000, () =&gt; {
    console.log('Servidor corriendo en http://localhost:3000');
});
</code></pre><ol><li><strong>Accede a la documentación</strong><br>Inicia tu servidor y abre un navegador en <code>http://localhost:3000/api-docs</code> para explorar los endpoints de tu API a través de la interfaz interactiva.</li></ol><hr><p>Swagger Autogen, hace una lectura del código, luego lo verifica y a partir del él va a generar todo lo necesario. </p><p>Importante cuando uses métodos que requieren o usan el payload (req.body) haz la desestructuración que swagger autogen, va a incluir esta definición en la llamada o uso del endpoint respectivo.</p><p>Si deseas personalizar o complementar la documentación, puedes agregar comentarios en el controller de forma que swagger autogen los tome en cuenta. &nbsp;</p><h3 id="ejemplo-de-c-digo-documentado">Ejemplo de código documentado</h3><p>El uso de comentarios en tus controladores puede mejorar la documentación generada. Por ejemplo:</p><pre><code>/**
 * @route GET /pets
 * @description Obtiene una lista de mascotas
 * @response 200 - Lista de mascotas
 */
app.get('/pets', (req, res) =&gt; {
    res.send([{ id: 1, name: 'Fido' }, { id: 2, name: 'Luna' }]);
});
</code></pre><p>Swagger Autogen extraerá estos comentarios para enriquecer la especificación de tu API.</p><hr><p>El uso de <strong>Swagger Autogen</strong> y <strong>Swagger UI Express</strong> transforma la documentación de APIs REST en un proceso eficiente y profesional. Estas herramientas no solo automatizan tareas tediosas, sino que también proporcionan una experiencia superior para los consumidores de la API, quienes pueden explorar y probar los servicios de manera intuitiva.</p><p>Implementar Swagger en tus proyectos garantiza una documentación clara, estandarizada y accesible, facilitando la colaboración entre equipos y mejorando la percepción de calidad de tu API.</p><p>En este contenido, realizamos la implementación de la documentación de una API real, donde hacemos el paso a paso y obtenemos el swagger a partir de la ejecución de lo descrito anteriormente.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/3EMLqQ6ny80?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Aprende a Documentar tu API Node.js con Swagger Autogen y Swagger UI Express 🚀" name="fitvid0"></iframe>
          </div>
        </div>
      </figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Servicios y Observables en Angular ]]>
                </title>
                <description>
                    <![CDATA[ ¿Qué son los servicios en Angular? Un servicio es una clase diseñada para encapsular lógica o funcionalidad que debe ser compartida entre diferentes partes de la aplicación. Se utiliza principalmente para:  1. Separar lógica de negocio del componente.  2. Compartir datos y métodos entre componentes.  3. Facilitar ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/rxjs-como-sacarle-provecho-a-angular/</link>
                <guid isPermaLink="false">673b637e296a1703fde9bf01</guid>
                
                    <category>
                        <![CDATA[ angular ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Mon, 25 Nov 2024 14:49:09 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/11/Youtube--15-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="-qu-son-los-servicios-en-angular"><strong>¿Qué son los servicios en Angular?</strong></h2><p>Un <strong>servicio</strong> es una clase diseñada para encapsular lógica o funcionalidad que debe ser compartida entre diferentes partes de la aplicación. Se utiliza principalmente para:</p><ol><li>Separar lógica de negocio del componente.</li><li>Compartir datos y métodos entre componentes.</li><li>Facilitar pruebas unitarias al desacoplar funcionalidades.</li><li>Gestionar recursos externos como APIs o almacenamiento local.</li></ol><p>Angular proporciona el mecanismo de <strong>inyección de dependencias (DI)</strong> para gestionar servicios, lo que garantiza que siempre se trabaje con una única instancia compartida (Patrón Singleton) a menos que se configure de otra manera.</p><hr><h2 id="c-mo-se-implementa-un-servicio"><strong>Cómo se implementa un servicio</strong></h2><p>Crear e implementar un servicio es sencillo gracias al Cliente Angular y al patrón de inyección de dependencias. Veamos los pasos necesarios para realizar esta implementación.</p><h3 id="1-crear-un-servicio"><strong>1. Crear un servicio</strong></h3><p>Utiliza el comando de Angular CLI para generar un servicio:</p><p><code>ng generate service nombre-del-servicio</code></p><p>Este comando genera dos archivos:</p><ul><li><code>nombre-del-servicio.service.ts</code> (código del servicio).</li><li><code>nombre-del-servicio.service.spec.ts</code> (archivo de pruebas).</li></ul><p>El archivo generado podría verse así:</p><pre><code>import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root' // Proveedor en el nivel raíz
})
export class NombreDelServicio {
  constructor() { }
}
</code></pre><h3 id="2-proveer-el-servicio"><strong>2. Proveer el servicio</strong></h3><p>Angular registra automáticamente el servicio en el <strong>nivel raíz</strong> mediante el decorador <code>@Injectable({ providedIn: 'root' })</code>. Esto significa que estará disponible en toda la aplicación como un Singleton.</p><p>Si necesitas limitar el alcance del servicio, puedes especificarlo manualmente en los <code>providers</code> de un módulo o componente.</p><p>En Angular, además del nivel raíz, podemos registrar el servicio a nivel de módulo o de componente.</p><hr><h2 id="casos-de-uso-de-los-servicios"><strong>Casos de uso de los servicios</strong></h2><h3 id="1-l-gica-de-negocio-compartida">1. <strong>Lógica de negocio compartida</strong></h3><p>Por ejemplo, nuestra aplicación de adopción de mascotas puede necesitar filtrar o procesar datos sobre mascotas disponibles:</p><pre><code>export class MascotaService {
  filtrarPorEdad(mascotas: Mascota[], edad: number): Mascota[] {
    return mascotas.filter(m =&gt; m.edad === edad);
  }
}
</code></pre><h3 id="2-gesti-n-de-estados-o-datos-globales">2. <strong>Gestión de estados o datos globales</strong></h3><p>Los servicios permiten compartir datos entre componentes sin necesidad de usar un patrón de emisión de eventos.</p><pre><code>export class SesionService {
  private usuarioLogeado: Usuario | null = null;

  setUsuario(usuario: Usuario): void {
    this.usuarioLogeado = usuario;
  }

  getUsuario(): Usuario | null {
    return this.usuarioLogeado;
  }
}
</code></pre><p>Para gestión de estados simples, usar servicios es posible y puede ser una solución aplicable.</p><h3 id="3-consumo-de-apis">3. <strong>Consumo de APIs</strong></h3><p>Este es uno de los casos más comunes. Un servicio interactúa con un backend para enviar o recibir datos. </p><p>Vamos a entrar en detalle para este caso de aplicación.</p><hr><h2 id="uso-de-servicios-con-consumos-de-apis"><strong>Uso de servicios con consumos de APIs</strong></h2><p>Para consumir APIs en Angular, utilizamos el módulo <code>HttpClientModule</code> y las herramientas de <strong>RxJS</strong>. A continuación, veamos cómo integrarlos.</p><h3 id="1-configuraci-n-inicial"><strong>1. Configuración inicial</strong></h3><p>Primero, asegúrate de importar el módulo <code>HttpClientModule</code> en el archivo principal del módulo de tu aplicación (<code>app.module.ts</code>):</p><pre><code>import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule // Importar aquí
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
</code></pre><h3 id="2-crear-el-servicio-para-la-api"><strong>2. Crear el servicio para la API</strong></h3><p>Supongamos que estamos construyendo una aplicación de adopción de mascotas y queremos consumir una API que proporciona una lista de mascotas. El servicio podría verse así:</p><pre><code>import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';

export interface Mascota {
  id: number;
  nombre: string;
  edad: number;
  tipo: string;
}

@Injectable({
  providedIn: 'root' // Disponible en toda la aplicación
})
export class MascotaService {
  private apiUrl = 'http:/localhost:5000/mascotas';

  constructor(private http: HttpClient) {}

  // Obtener la lista de mascotas
  getMascotas(): Observable&lt;Mascota[]&gt; {
    return this.http.get&lt;Mascota[]&gt;(this.apiUrl).pipe(
      map(data =&gt; data), // Transforma los datos si es necesario
      catchError(this.handleError) // Manejo de errores
    );
  }

  // Manejo de errores
  private handleError(error: HttpErrorResponse) {
    let mensaje = 'Ocurrió un error inesperado.';
    if (error.error instanceof ErrorEvent) {
      // Error del cliente
      mensaje = `Error del cliente: ${error.error.message}`;
    } else {
      // Error del servidor
      mensaje = `Error del servidor: ${error.status}, mensaje: ${error.message}`;
    }
    return throwError(() =&gt; mensaje);
  }
}
</code></pre><p><strong>3. Consumir el servicio en un componente</strong></p><p>Ahora que tenemos el servicio, podemos utilizarlo en un componente para mostrar la lista de mascotas.</p><pre><code>import { Component, OnInit } from '@angular/core';
import { MascotaService, Mascota } from './mascota.service';

@Component({
  selector: 'app-lista-mascotas',
  template: `
    &lt;div *ngIf="mascotas; else cargando"&gt;
      &lt;ul&gt;
        &lt;li *ngFor="let mascota of mascotas"&gt;{{ mascota.nombre }} - {{ mascota.tipo }}&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
    &lt;ng-template #cargando&gt;
      &lt;p&gt;Cargando mascotas...&lt;/p&gt;
    &lt;/ng-template&gt;
  `
})
export class ListaMascotasComponent implements OnInit {
  mascotas: Mascota[] | null = null;

  constructor(private mascotaService: MascotaService) {}

  ngOnInit() {
    this.mascotaService.getMascotas().subscribe({
      next: (data) =&gt; (this.mascotas = data),
      error: (error) =&gt; console.error('Error al cargar mascotas:', error),
    });
  }
}
</code></pre><p><strong>4. Ventajas de usar Observables</strong></p><p>Al usar Observables en Angular:</p><ul><li>Puedes cancelar suscripciones automáticamente con operadores como <code>takeUntil</code>.</li><li>Es posible combinar flujos de datos con operadores como <code>combineLatest</code>.</li><li>Tienes más control sobre el manejo de errores y la transformación de datos.</li></ul><hr><p>Los servicios son una herramienta fundamental en Angular para estructurar aplicaciones bien organizadas y escalables. Al integrarlos con <code>HttpClient</code> y RxJS, puedes manejar flujos de datos asíncronos de manera eficiente, aprovechar los operadores para transformar datos y garantizar una mejor experiencia del usuario.</p><p>Practicar la implementación de servicios con APIs reales es una excelente manera de dominar este concepto y sacarle el máximo provecho a Angular.</p><p>Te dejo un video donde de forma práctica verificamos los conceptos que hemos revisado anteriormente.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/bbaFNsqr4to?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Cómo Crear Servicios en Angular 18: Consumiendo una API REST para Adopción de Mascotas" name="fitvid0"></iframe>
          </div>
        </div>
      </figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Consumiendo servicios API desde Javascript ]]>
                </title>
                <description>
                    <![CDATA[ Cuando desarrollamos para la web, es una tarea común y crítica, el consumo de servicios de datos, que serán parte fundamental de nuestra aplicación. Siendo que JavaScript es el lenguaje que usamos para dar interactividad, revisemos como consumir APIs desde nuestras aplicaciones web, para obtener y manipular datos de forma ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/consumiendo-servicios-api-desde-javascript/</link>
                <guid isPermaLink="false">671bb58f408801043e04ba54</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Wed, 30 Oct 2024 13:06:08 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/10/Youtube--11-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Cuando desarrollamos para la web, es una tarea común y crítica, el consumo de servicios de datos, que serán parte fundamental de nuestra aplicación. Siendo que JavaScript es el lenguaje que usamos para dar interactividad, revisemos como consumir APIs desde nuestras aplicaciones web, para obtener y manipular datos de forma dinámica. En las siguientes líneas vamos a verificar cómo funcionan las solicitudes HTTP, el uso de <code>fetch</code> y el papel de <code>async/await</code> en el consumo de APIs. Usaremos la API de <a href="https://restcountries.com/" rel="noopener">REST Countries</a> como ejemplo, permitiéndonos acceder y mostrar datos detallados sobre países en una aplicación interactiva.</p><h2 id="introducci-n-a-las-apis-y-rest">Introducción a las APIs y REST</h2><h3 id="-qu-es-una-api">¿Qué es una API?</h3><p>API, o <em>Interfaz de Programación de Aplicaciones</em> (Application Programming Interface), es un conjunto de reglas y protocolos que permite que dos aplicaciones se comuniquen entre sí. Piensa en una API como un puente que conecta dos aplicaciones: una puede pedirle datos o servicios a otra sin necesidad de conocer su funcionamiento interno.</p><h3 id="-qu-es-rest">¿Qué es REST?</h3><p>REST, o <em>Transferencia de Estado Representacional</em> (Representational State Transfer), es un estilo arquitectónico para diseñar APIs. Las APIs REST suelen usar URLs para identificar recursos (como países, en nuestro caso) y métodos HTTP (GET, POST, PUT, DELETE) para definir las acciones a realizar sobre esos recursos.</p><h2 id="m-todos-http-en-el-consumo-de-apis">Métodos HTTP en el Consumo de APIs</h2><p>Las APIs REST se basan en el protocolo HTTP, el cual es el mismo protocolo que usamos al navegar en la web. Cada acción que llevamos a cabo con la API (consultar datos, crear un nuevo registro, actualizar o eliminar un registro) se representa a través de un <strong>método HTTP</strong>. Estos métodos son parte integral de la arquitectura REST, ya que permiten manipular recursos de forma estandarizada.</p><p>Cada método HTTP tiene un propósito específico y su propio rol en la interacción con la API:</p><p><strong>GET</strong>: Este es el método más común y se usa para <em>recuperar datos</em> de un recurso específico. Es como una consulta de solo lectura; no cambia ni afecta el estado de los datos en el servidor.</p><ul><li><strong>Ejemplo</strong>: Al consultar la API para obtener una lista de países, usamos una solicitud GET.</li></ul><p><strong>POST</strong>: Este método se utiliza para <em>enviar datos</em> al servidor y crear un nuevo recurso en él. A diferencia de GET, POST modifica el estado del servidor al agregar datos.</p><ul><li><strong>Ejemplo</strong>: En una API de usuarios, un POST a la URL <code>https://api.ejemplo.com/usuarios</code> podría crear un nuevo usuario en la base de datos.</li></ul><p><strong>PUT</strong>: Se usa para <em>actualizar un recurso existente</em> en el servidor. En general, se espera que una solicitud PUT contenga todos los datos del recurso a actualizar, y reemplaza el recurso en el servidor con los datos enviados.</p><ul><li><strong>Ejemplo</strong>: Actualizar los detalles de un país en la base de datos mediante una solicitud PUT a <code>https://api.ejemplo.com/paises/{id}</code>.</li></ul><p><strong>DELETE</strong>: Este método se utiliza para <em>eliminar un recurso específico</em> en el servidor. Como su nombre lo indica, DELETE quita o borra datos de la base de datos o sistema remoto.</p><ul><li><strong>Ejemplo</strong>: Una solicitud DELETE a <code>https://api.ejemplo.com/usuarios/{id}</code> eliminaría al usuario con el ID especificado.</li></ul><p>Cada uno de estos métodos se representa en las aplicaciones REST de forma clara, permitiendo a los desarrolladores y al servidor entender qué acción se desea realizar sobre los datos. Estos métodos, cuando se usan con <code>fetch</code> en JavaScript, nos permiten manipular y gestionar datos de manera precisa y efectiva en la aplicación.</p><h3 id="ejemplo-de-solicitudes-http-con-fetch">Ejemplo de Solicitudes HTTP con <code>fetch</code></h3><p>El método <code>fetch</code> en JavaScript nos permite implementar fácilmente cualquiera de estos métodos HTTP:</p><ul><li><strong>GET</strong>: Para recuperar datos, pasamos solo la URL a <code>fetch</code>.</li><li><strong>POST, PUT y DELETE</strong>: Estos métodos requieren configuraciones adicionales en <code>fetch</code>, como el tipo de método, los encabezados y el cuerpo de la solicitud.</li></ul><h2 id="uso-de-fetch-en-javascript">Uso de <code>fetch</code> en JavaScript</h2><p><code>fetch</code> es una función nativa en JavaScript que facilita el envío de solicitudes HTTP, devolviendo una <em>promesa</em>. Usar <code>fetch</code> con <code>async/await</code> mejora la legibilidad del código y simplifica la gestión de las respuestas, lo que es especialmente útil cuando trabajamos con datos de una API.</p><h3 id="estructura-b-sica-de-fetch">Estructura Básica de <code>fetch</code></h3><p>La sintaxis básica de <code>fetch</code> para una solicitud GET es:</p><pre><code>fetch(url)
    .then(response =&gt; response.json())
    .then(data =&gt; console.log(data))
    .catch(error =&gt; console.error('Error:', error));
</code></pre><p>O usando <code>async/await</code>:</p><pre><code class="language-Javascript">async function getData(url) {
    try {
        const response = await fetch(url);
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error:', error);
    }
}
</code></pre><h3 id="solicitud-get-con-fetch">Solicitud GET con <code>fetch</code></h3><p>En una solicitud GET, simplemente se pasa la URL de la API a <code>fetch</code>. Nuestro ejemplo utiliza este método para obtener datos de países según su subregión.</p><pre><code>async function getDatos(subregion) {
    try {
        const res = await fetch(`https://restcountries.com/v3.1/subregion/${subregion}`);
        const data = await res.json();
        dibujaCards(data);
    } catch (error) {
        alert('No pude conectarme a la API');
    }
}
</code></pre><h3 id="solicitud-post-put-y-delete-con-fetch">Solicitud POST, PUT y DELETE con <code>fetch</code></h3><p>Para otras operaciones (POST, PUT, DELETE), podemos añadir una configuración adicional a <code>fetch</code>, especificando el método, encabezados y el cuerpo de la solicitud.</p><h4 id="ejemplo-de-post">Ejemplo de POST</h4><p>Un ejemplo típico de POST podría ser enviar datos a una API para crear un nuevo recurso, como un usuario.</p><pre><code>async function createUser(userData) {
    try {
        const response = await fetch('https://example.com/api/users', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(userData)
        });
        const data = await response.json();
        console.log('Usuario creado:', data);
    } catch (error) {
        console.error('Error al crear usuario:', error);
    }
}
</code></pre><h4 id="ejemplo-de-put">Ejemplo de PUT</h4><p>PUT se usa para actualizar un recurso existente. Este ejemplo muestra cómo modificar los datos de un usuario:</p><pre><code>async function updateUser(userId, updatedData) {
    try {
        const response = await fetch(`https://example.com/api/users/${userId}`, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(updatedData)
        });
        const data = await response.json();
        console.log('Usuario actualizado:', data);
    } catch (error) {
        console.error('Error al actualizar usuario:', error);
    }
}
</code></pre><h4 id="ejemplo-de-delete">Ejemplo de DELETE</h4><p>Para eliminar un recurso, simplemente usamos el método DELETE y la URL correspondiente.</p><pre><code>async function deleteUser(userId) {
    try {
        const response = await fetch(`https://example.com/api/users/${userId}`, {
            method: 'DELETE'
        });
        if (response.ok) {
            console.log('Usuario eliminado');
        }
    } catch (error) {
        console.error('Error al eliminar usuario:', error);
    }
}
</code></pre><h2 id="proyecto-explorador-de-pa-ses">Proyecto: Explorador de Países</h2><p>Vamos a crear una pequeña aplicación, que consuma datos de la API de <a href="https://restcountries.com/" rel="noopener">REST Countries</a> y poder generar una presentación de galería, así como una presentación de detalle por país, con ello vamos a poder poner en práctica los conceptos anteriormente explicados.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/cb5BIStLYv4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Crea una Galería de Países en JavaScript: Consume APIs REST y Muestra Mapas con OpenStreetMap!" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="proyecto-explorador-de-pa-ses-1">Proyecto: Explorador de Países</h2><p>El proyecto está compuesto por:</p><ol><li><strong>HTML (<code>index.html</code>)</strong>: Define la estructura de la aplicación.</li><li><strong>JavaScript (<code>script.js</code>)</strong>: Realiza la solicitud a la API para obtener datos de países según la subregión seleccionada.</li><li><strong>HTML (</strong><code><strong>detail.html</strong></code><strong>)</strong>: Define la estructura de la presentación de detalle del país.</li><li><strong>JavaScript de Detalle (<code>detail.js</code>)</strong>: Muestra detalles específicos de un país.</li></ol><h3 id="html-de-la-p-gina-principal">HTML de la Página Principal</h3><p>En el archivo <code>index.html</code>, configuramos una barra de navegación para seleccionar una subregión (América del Norte, América del Sur o América Central) y una galería donde se muestran los países.</p><pre><code>&lt;nav&gt;
    &lt;ul class="navbar"&gt;
        &lt;li&gt;&lt;a href="#norteamerica"&gt;América del Norte&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="#suramerica"&gt;América del Sur&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="#centroamerica"&gt;América Central&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
&lt;/nav&gt;
&lt;div id="loading" class="loading hidden"&gt;Cargando...&lt;/div&gt;
&lt;section class="gallery"&gt;&lt;/section&gt;
&lt;footer&gt;
    &lt;p&gt;&amp;copy; 2024 América - Explorando Países&lt;/p&gt;
&lt;/footer&gt;
</code></pre><h3 id="funci-n-para-obtener-datos-en-script-js">Función para Obtener Datos en <code>script.js</code></h3><p>Aquí, <code>fetch</code> se usa para hacer una solicitud GET a la API y obtener datos sobre países de una subregión. Se muestra un indicador de carga durante la consulta y se actualiza la galería una vez que se reciben los datos.</p><pre><code>const loadingIndicator = document.getElementById('loading');

function showLoading() {
    loadingIndicator.classList.remove('hidden');
}

function hideLoading() {
    loadingIndicator.classList.add('hidden');
}

async function getDatos(subregion) {
    try {
        showLoading();
        const res = await fetch(`https://restcountries.com/v3.1/subregion/${subregion}`);
        const data = await res.json();
        dibujaCards(data);
    } catch (error) {
        alert('No pude conectarme a la API');
    } finally {
        hideLoading();
    }
}
</code></pre><h3 id="funci-n-dibujacards-paises-">Función <code>dibujaCards(paises)</code></h3><p>Esta función recibe los datos de la API y los muestra en una tarjeta HTML para cada país.</p><pre><code>function dibujaCards(paises) {
    const galeria = document.querySelector('.gallery');
    let htmlPaises = '';
    paises.forEach((pais) =&gt; {
        htmlPaises += `
        &lt;div class="card"&gt;
            &lt;h2&gt;${pais.name.official}&lt;/h2&gt;
            &lt;p&gt;&lt;strong&gt;Capital:&lt;/strong&gt; ${pais.capital ? pais.capital[0] : 'No Disponible'}&lt;/p&gt;
            &lt;p&gt;&lt;strong&gt;Idioma:&lt;/strong&gt; ${Object.values(pais.languages || { '': 'No Disponible' }).join(', ')}&lt;/p&gt;
            &lt;button onclick="viewMore('${pais.cca3}')"&gt;Ver más&lt;/button&gt;
        &lt;/div&gt;
        `;
    });
    galeria.innerHTML = htmlPaises;
}
</code></pre><h2></h2><p>Resumiendo lo realizado, &nbsp;<code>fetch</code> y <code>async/await</code> nos brindan una forma poderosa y sencilla de consumir APIs en JavaScript. Al conocer los diferentes métodos HTTP y cómo configurarlos en <code>fetch</code>, ahora tienes las bases para manejar solicitudes <code>GET</code>, <code>POST</code>, <code>PUT</code> y <code>DELETE</code>, lo cual te abrirá muchas posibilidades para crear aplicaciones interactivas que consuman o modifiquen datos de forma dinámica.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Primeros pasos con Angular 18 ]]>
                </title>
                <description>
                    <![CDATA[ Empecemos por definir que es angular: un framework de desarrollo web  desarrollado y mantenido por Google, diseñado para crear aplicaciones web dinámicas y robustas. Angular utiliza un enfoque modular basado en componentes, lo que facilita la organización y mantenimiento de proyectos grandes. La filosofía de Angular, es trabajar como ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/primeros-pasos-con-angular/</link>
                <guid isPermaLink="false">66cca7124ca9cf0431bda1bc</guid>
                
                    <category>
                        <![CDATA[ angular ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Mon, 23 Sep 2024 13:15:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/09/Youtube--4-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Empecemos por definir que es angular: un <strong>framework de desarrollo web</strong> desarrollado y mantenido por <strong>Google</strong>, diseñado para crear aplicaciones web dinámicas y robustas. Angular utiliza un enfoque modular basado en componentes, lo que facilita la organización y mantenimiento de proyectos grandes.</p><p>La filosofía de Angular, es trabajar como una SPA (Single Page Application), de forma que la usabilidad y el performance sea lo mejor posible.</p><h2 id="preparando-el-ambiente">Preparando el ambiente</h2><h3 id="instalar-node-js-y-npm">Instalar Node.js y npm</h3><p><strong>Angular</strong> se ejecuta sobre Node.js, que es un entorno de ejecución de JavaScript orientado a backend. Esto significa que para poder trabajar con Angular, primero necesitas tener Node.js instalado en tu computadora.</p><p><strong>npm</strong> (Node Package Manager) es la herramienta que viene con Node.js y nos permite instalar paquetes y librerías necesarios para el desarrollo. A través de npm, también instalaremos Angular.</p><h4 id="instalaci-n-de-node-js-">Instalación de Node.js:</h4><p>Visita el sitio oficial de <a href="https://nodejs.org/" rel="noopener">Node.js</a> y descarga la versión recomendada (LTS - Long Term Support). Sigue las instrucciones para instalarlo en tu sistema operativo.</p><p>Una vez instalado, abre una terminal y verifica que Node.js y npm se hayan instalado correctamente ejecutando los siguientes comandos:</p><pre><code>node -v
npm -v</code></pre><p>Este comando muestra en la pantalla la versión instalada de Node.js y npm, con ello sabemos que tenemos lo necesario para iniciar el aprendizaje de Angular.</p><h3 id="conociendo-a-angular-cli"><strong>Conociendo a Angular CLI</strong></h3><p>Angular tiene una herramienta muy útil llamada <strong>Angular CLI</strong> (Command Line Interface). Esta herramienta permite crear y gestionar proyectos de Angular mediante comandos sencillos. Nos ayuda a generar la estructura del proyecto, añadir componentes, servicios, y mucho más.</p><p>Para instalar Angular CLI, ejecuta el siguiente comando en tu terminal:</p><pre><code>npm install -g @angular/cli</code></pre><p>Este comando descarga e instala Angular CLI de forma global en tu sistema, lo que significa que puedes usarlo desde cualquier carpeta en tu computadora.</p><p>Una vez completada la instalación, verifica que Angular CLI esté correctamente instalado ejecutando:</p><pre><code>ng version</code></pre><p>Esto mostrará la versión de Angular CLI y otras herramientas relacionadas.</p><p>En este enlace podrás encontrar la referencia completa de los comandos disponibles para ejecutar en Angular CLI: <a href="https://angular.dev/cli">https://angular.dev/cli</a></p><h2 id="entiendo-la-estructura-de-la-aplicaci-n">Entiendo la estructura de la aplicación</h2><p>Ahora que tienes Angular CLI instalado, ya estás listo para crear tu primer proyecto Angular. Vamos a hacerlo paso a paso.</p><ol><li>Abre la terminal en la carpeta donde quieras crear el proyecto.</li><li>Ejecuta el siguiente comando para crear un nuevo proyecto Angular:</li></ol><pre><code>ng new nombre-del-proyecto</code></pre><p>Angular CLI te hará algunas preguntas, como si deseas añadir un enrutador (router) y qué formato de estilos (CSS, SCSS, etc.) prefieres usar. Responde según tus preferencias.</p><p>Después de responder estas preguntas, Angular CLI generará toda la estructura del proyecto y descargará las dependencias necesarias.</p><blockquote>Recuerda que el nombre del proyecto no debe llevar espacios en blanco, caracteres especiales o acentos.</blockquote><p>Cuando Angular CLI crea un proyecto, organiza todo en carpetas y archivos. Algunos de los archivos clave incluyen:</p><ul><li><strong><code>src/app</code></strong>: Aquí es donde se encuentra el código de tu aplicación. Contiene componentes, módulos y servicios.</li><li><strong><code>src/app/app.component.ts</code></strong>: Es el componente principal de la aplicación.</li><li><strong><code>src/app/app.module.ts</code></strong>: Define los módulos de la aplicación.</li><li><strong><code>angular.json</code></strong>: Archivo de configuración del proyecto Angular.</li></ul><h3 id="ejecutar-tu-proyecto-angular">Ejecutar Tu Proyecto Angular</h3><p>Una vez que el proyecto ha sido creado, es hora de ejecutarlo y ver tu primera aplicación Angular en acción. Angular CLI te permite correr un servidor de desarrollo para que puedas ver los cambios que haces en tiempo real.</p><p>Para iniciar el servidor, ejecuta el siguiente comando en la terminal desde la carpeta de tu proyecto:</p><pre><code>ng serve</code></pre><p>Angular comenzará a compilar el proyecto y luego te mostrará una URL que puedes abrir en tu navegador (por defecto es <code>http://localhost:4200</code>). Abre esa URL en tu navegador y deberías ver la página inicial de tu nueva aplicación Angular.</p><p>Cada vez que hagas un cambio en tu código, Angular recargará automáticamente la página para reflejar los cambios, lo que hace el desarrollo más eficiente y fluido.</p><h2 id="creando-componentes">Creando componentes</h2><p>Sabiendo que Angular basa su funcionamiento en componentes, es natural que podamos crearlos de forma rápida y simple, usando a Angular CLI. Para ello ejecutamos el siguiente comando:</p><pre><code>ng generate component nombre-componente</code></pre><p>Con esto Angular CLI generará los siguientes archivos:</p><ol><li><strong><code>.ts</code> (TypeScript)</strong>: Contiene la lógica del componente. Define la clase que controla su comportamiento.</li><li><strong><code>.html</code></strong>: Es la plantilla o vista, donde se define el contenido visual.</li><li><strong><code>.css</code></strong>: Aquí se especifican los estilos específicos del componente.</li><li><strong><code>.spec.ts</code></strong>: Es el archivo de pruebas unitarias, utilizado para verificar que el componente funcione como se espera.</li></ol><p>Cada componente trabaja de manera modular, permitiendo reutilización y organización clara del código.</p><p>Ahora solo queda que practiques, para ello te dejó un video donde paso a paso damos estos primeros pasos e incluso iniciamos un proyecto, para poner la práctica los conceptos anteriormente revisados.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/Tojwch4DQfI?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Primeros Pasos con Angular: Ambiente, Estructura de Aplicación y Creación de Componentes" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p>Si tienes alguna duda, me puedes mandar un mensaje a mi <a href="https://www.linkedin.com/in/leonardo-castillo-4911571a/">LinkedIn</a>, con gusto te respondo.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Formularios en ReactJS. Cómo manejarlos? ]]>
                </title>
                <description>
                    <![CDATA[ Cuando desarrollamos aplicaciones, una tarea común, fundamental y crítica es la recepción de datos por parte del usuario. Es esta actividad la inicia el proceso básico de un sistema:  Entrada (Input del usuario) --> Procesamiento --> Salida (Presentación de resultados). Veamos como realizamos esta tarea en ReactJS, partiendo de ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/formularios-en-react-como-manejarlos/</link>
                <guid isPermaLink="false">65a3c84351a59a0455c92a18</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Thu, 06 Jun 2024 14:03:32 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/06/Youtube--4-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Cuando desarrollamos aplicaciones, una tarea común, fundamental y crítica es la recepción de datos por parte del usuario. Es esta actividad la inicia el proceso básico de un sistema: </p><p><em>Entrada</em> (Input del usuario) --&gt; <em>Procesamiento</em> --&gt; <em>Salida</em> (Presentación de resultados).</p><p>Veamos como realizamos esta tarea en ReactJS, partiendo de la forma básica, para ello vamos a crear un componente llamado <code>Formulario</code> el cual va a permitirle al usuario indicar sus datos y luego estos serán enviados a una API para ser procesados y guardados.</p><p>Recordemos que con ReactJS implementamos SPAs (<em>Single Page Applications</em>), por lo tanto, la gestión del formulario va a variar un poco con relación a un <em>form</em> tradicional de HTML.</p><h2 id="formulario-como-componente-con-usestate">Formulario como componente con useState</h2><p>Para este componente formulario, nos vamos a apoyar en el hook <code>useState</code>, de forma tal que podamos capturar los valores introducidos por el usuario, toda vez que haga algún cambio en él alguna de las entradas disponibles. El código de ese componente será el siguiente:</p><pre><code>import React, { useState } from 'react';

const countries = [
  "Argentina", "Bolivia", "Brasil", "Chile", "Colombia",
  "Ecuador", "Guyana", "Paraguay", "Perú", "Surinam",
  "Uruguay", "Venezuela"
];

const Formulario = () =&gt; {
  const [formData, setFormData] = useState({
    email: '',
    password: '',
    confirmPassword: '',
    name: '',
    country: '',
    phone: '',
    photo: null
  });

  const handleChange = (e) =&gt; {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  const handlePhotoChange = (e) =&gt; {
    setFormData({ ...formData, photo: e.target.files[0] });
  };

  const handleSubmit = (e) =&gt; {
    e.preventDefault();
    console.log('Form data submitted:', formData);
  };

  return (
    &lt;div className="App"&gt;
      &lt;form onSubmit={handleSubmit}&gt;
        &lt;h2&gt;Datos de Acceso&lt;/h2&gt;
        &lt;div&gt;
          &lt;label&gt;Email:&lt;/label&gt;
          &lt;input type="email" name="email" value={formData.email} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Clave:&lt;/label&gt;
          &lt;input type="password" name="password" value={formData.password} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Confirmar Clave:&lt;/label&gt;
          &lt;input type="password" name="confirmPassword" value={formData.confirmPassword} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;h2&gt;Datos Personales&lt;/h2&gt;
        &lt;div&gt;
          &lt;label&gt;Nombre:&lt;/label&gt;
          &lt;input type="text" name="name" value={formData.name} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;País:&lt;/label&gt;
          &lt;select name="country" value={formData.country} onChange={handleChange} required&gt;
            &lt;option value=""&gt;Seleccione un país&lt;/option&gt;
            {countries.map(country =&gt; (
              &lt;option key={country} value={country}&gt;{country}&lt;/option&gt;
            ))}
          &lt;/select&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Teléfono:&lt;/label&gt;
          &lt;input type="tel" name="phone" value={formData.phone} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Foto:&lt;/label&gt;
          &lt;input type="file" name="photo" accept="image/*" onChange={handlePhotoChange} required /&gt;
        &lt;/div&gt;
        &lt;button type="submit"&gt;Enviar&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  );
};

export default Formulario;</code></pre><p>El componente tiene 3 funciones, las cuales explicamos:</p><ul><li><code>handleChange</code>, encargada de capturar el evento <code>onChange</code> de las cajas y listas del formulario, con ello actualizamos el estado definido al inicio que contendrá los valores indicados por el usuario.</li><li><code>handlePhotoChange</code>, la cual nos ayudará a capturar la información de la foto solicitada en el formulario, siendo que el tipo de input <code>file</code> se maneja de forma diferente, por ello es importante separar esta funcionalidad.</li><li><code>handleSubmit</code>, en esta función implementaremos lo necesario para enviar los datos a la capa de backend.</li></ul><h3 id="enviando-la-foto-en-formato-base64">Enviando la foto en formato base64</h3><p>Es común que el envío de archivos implique realizar algunos procesos adicionales, por ejemplo, tomar el contenido y codificarlo en <code>base64</code> para transformarlo en un string y así poderlo enviar de forma más fácil a la capa de backend, para ello agregamos una función responsable de esa tarea en el código:</p><pre><code>const toBase64 = (file) =&gt; new Promise((resolve, reject) =&gt; {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () =&gt; resolve(reader.result);
    reader.onerror = (error) =&gt; reject(error);
  });</code></pre><p>Observa, que por ser una operación que implica tiempo no determinado, JavaScript la maneja como una promesa, es decir, cuando finalice la conversión, nos va a llamar y nos dará el resultado.</p><p>Además de hacer esa transformación, es importante que enviemos los datos de nombre original de archivo, su tamaño y su tipo de contenido, ya que en la capa de backend esos datos serán necesarios, para ello mejoramos la función <code>handlePhotoChange</code>, dejándola de esta forma:</p><pre><code>  const handlePhotoChange = (e) =&gt; {
    const file = e.target.files[0];
    if (file) {
      setFormData({
        ...formData,
        photo: file,
        photoName: file.name,
        photoType: file.type,
        photoSize: file.size
      });
    }
  };</code></pre><p>Ahora con este proceso podemos mejorar nuestra función <code>handleSubmit</code> incorporando la conversión de la foto y el envío de los datos a la capa de backend.</p><pre><code>const handleSubmit = async (e) =&gt; {
    e.preventDefault();

    // Convertir la foto a base64
    let base64Photo = '';
    if (formData.photo) {
      base64Photo = await toBase64(formData.photo);
    }

    // Crear el objeto con los datos del formulario
    const dataToSend = {
      email: formData.email,
      password: formData.password,
      confirmPassword: formData.confirmPassword,
      name: formData.name,
      country: formData.country,
      phone: formData.phone,
      photo: {
        base64: base64Photo,
        name: formData.photoName,
        type: formData.photoType,
        size: formData.photoSize
      }
    };

    // Enviar la solicitud POST a la API
    try {
      const response = await fetch('https://leonardojose.dev/api/usuarios', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(dataToSend)
      });

      if (!response.ok) {
        throw new Error('Error en la solicitud');
      }

      const result = await response.json();
      console.log('Form data submitted:', result);
    } catch (error) {
      console.error('Error:', error);
    }
  };</code></pre><p>Recuerda, React trabaja como SPA, por lo tanto, el envío de datos es asíncrono, es por ello que lo primero que realizamos en la función de submit es evitar que el navegador nos haga un envío de datos o POST con la línea: <code>e.preventDefault();</code> </p><h2 id="trabajando-con-typescript">Trabajando con TypeScript</h2><p>El código anteriormente implementado es totalmente váildo cuando trabajamos en JavaScript, solo que actualmente, ha tenido un gran auge, el uso del <em>SuperJavascript</em>, o mejor conocido como <strong>TypeScript</strong>. </p><p>Vamos a convertir nuestro componente a lenguaje typescript, para ello vamos a definir los tipos de datos correctamente, vamos a cambiar la extensión del componente para <code>Formulario.tsx</code> y con ello vamos a aprovechar las bondades que TypeScript, nos da, la principal para mí, poder determinar errores en tiempo de compilación.</p><p>El código del componente ahora quedará:</p><pre><code>import React, { useState, ChangeEvent, FormEvent } from 'react';

const countries = [
  "Argentina", "Bolivia", "Brasil", "Chile", "Colombia",
  "Ecuador", "Guyana", "Paraguay", "Perú", "Surinam",
  "Uruguay", "Venezuela"
];

interface FormData {
  email: string;
  password: string;
  confirmPassword: string;
  name: string;
  country: string;
  phone: string;
  photo: File | null;
  photoName: string;
  photoType: string;
  photoSize: number | string;
}

const Formulario: React.FC = () =&gt; {
  const [formData, setFormData] = useState&lt;FormData&gt;({
    email: '',
    password: '',
    confirmPassword: '',
    name: '',
    country: '',
    phone: '',
    photo: null,
    photoName: '',
    photoType: '',
    photoSize: ''
  });

  const handleChange = (e: ChangeEvent&lt;HTMLInputElement | HTMLSelectElement&gt;) =&gt; {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  const handlePhotoChange = (e: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    const file = e.target.files ? e.target.files[0] : null;
    if (file) {
      setFormData({
        ...formData,
        photo: file,
        photoName: file.name,
        photoType: file.type,
        photoSize: file.size
      });
    }
  };

  const handleSubmit = async (e: FormEvent) =&gt; {
    e.preventDefault();

    // Convertir la foto a base64
    let base64Photo = '';
    if (formData.photo) {
      base64Photo = await toBase64(formData.photo);
    }

    // Crear el objeto con los datos del formulario
    const dataToSend = {
      email: formData.email,
      password: formData.password,
      confirmPassword: formData.confirmPassword,
      name: formData.name,
      country: formData.country,
      phone: formData.phone,
      photo: {
        base64: base64Photo,
        name: formData.photoName,
        type: formData.photoType,
        size: formData.photoSize
      }
    };

    // Enviar la solicitud POST a la API
    try {
      const response = await fetch('https://leonardojose.dev/api/usuarios', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(dataToSend)
      });

      if (!response.ok) {
        throw new Error('Error en la solicitud');
      }

      const result = await response.json();
      console.log('Form data submitted:', result);
    } catch (error) {
      console.error('Error:', error);
    }
  };

  const toBase64 = (file: File) =&gt; new Promise&lt;string&gt;((resolve, reject) =&gt; {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () =&gt; resolve(reader.result as string);
    reader.onerror = (error) =&gt; reject(error);
  });

  return (
    &lt;div className="App"&gt;
      &lt;form onSubmit={handleSubmit}&gt;
        &lt;h2&gt;Datos de Acceso&lt;/h2&gt;
        &lt;div&gt;
          &lt;label&gt;Email:&lt;/label&gt;
          &lt;input type="email" name="email" value={formData.email} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Clave:&lt;/label&gt;
          &lt;input type="password" name="password" value={formData.password} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Confirmar Clave:&lt;/label&gt;
          &lt;input type="password" name="confirmPassword" value={formData.confirmPassword} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;h2&gt;Datos Personales&lt;/h2&gt;
        &lt;div&gt;
          &lt;label&gt;Nombre:&lt;/label&gt;
          &lt;input type="text" name="name" value={formData.name} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;País:&lt;/label&gt;
          &lt;select name="country" value={formData.country} onChange={handleChange} required&gt;
            &lt;option value=""&gt;Seleccione un país&lt;/option&gt;
            {countries.map(country =&gt; (
              &lt;option key={country} value={country}&gt;{country}&lt;/option&gt;
            ))}
          &lt;/select&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Teléfono:&lt;/label&gt;
          &lt;input type="tel" name="phone" value={formData.phone} onChange={handleChange} required /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Foto:&lt;/label&gt;
          &lt;input type="file" name="photo" accept="image/*" onChange={handlePhotoChange} required /&gt;
        &lt;/div&gt;
        &lt;button type="submit"&gt;Enviar&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  );
};

export default Formulario;</code></pre><p>Observa que aparecen los tipos de datos de cada propiedad, de casda evento e incluso del componente React, algo que es fundamental para la mantenibilidad y legibilidad del código.</p><p>Luego nuestro código podrá ser incoporado en proyectos que usen TypeScript como base, lo cual está sucediendo con más frecuencia cada día.</p><h2 id="mejorando-la-visual-del-formulario">Mejorando la visual del formulario</h2><p>Hasta ahora nuestro código apenas tiene HTML y no usa nada de las bondades de CSS, por lo tanto, será funcional, pero no será agradable a la vista. Para ello vamos a incorporarle estilos del framework para CSS <code>tailwind</code>, específicamente del plugin <code>daisyui</code>.</p><p>Para instalar Tailwind en una aplicación <code>react</code> con <code>vite</code>, puedes seguir estas instrucciones:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.freecodecamp.org/news/how-to-install-tailwindcss-in-react/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">How to Setup React and Tailwind CSS with Vite in a Project</div><div class="kg-bookmark-description">Tailwind CSS is a popular CSS framework, and React is one of the most popularJavaScript libraries. And Tailwind CSS and React are a great combo to use if you’re building afrontend project. In this article, you will learn how to setup your coding environment with Vite,install React and Tailwind …</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://cdn.freecodecamp.org/universal/favicons/favicon.ico" width="48" height="48" alt="favicon" loading="lazy"><span class="kg-bookmark-author">freeCodeCamp.org</span><span class="kg-bookmark-publisher">Segun Ajibola</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.freecodecamp.org/news/content/images/2023/01/Parameters-vs-Arguments--1-.png" width="600" height="400" alt="Parameters-vs-Arguments--1-" loading="lazy"></div></a></figure><p>Luego de instalado Tailwind, debemos instalar daisyui, esto lo logramos con las siguientes instrucciones:</p><pre><code>npm install daisyui</code></pre><p>Incluso es posible usar un paquete que ya genera los componentes para react, llamado react-daisyui (no lo usamos aquí, pero es interesante conocerlo), el cual podemos instalar con el siguiente comando:</p><pre><code>npm install react-daisyui</code></pre><p>Ahora corresponde realizar el ajuste en nuestro componente, de forma que podamos aprovechar la visual de <code>daisyui</code>, quedando nuestro componente como sigue:</p><pre><code>import React, { useState, ChangeEvent, FormEvent } from 'react';
import 'daisyui/dist/full.css';

const countries = [
  "Argentina", "Bolivia", "Brasil", "Chile", "Colombia",
  "Ecuador", "Guyana", "Paraguay", "Perú", "Surinam",
  "Uruguay", "Venezuela"
];

interface FormData {
  email: string;
  password: string;
  confirmPassword: string;
  name: string;
  country: string;
  phone: string;
  photo: File 
  photoName: string;
  photoType: string;
  photoSize: number | string;
}

const Formulario: React.FC = () =&gt; {
  const [formData, setFormData] = useState&lt;FormData&gt;({
    email: '',
    password: '',
    confirmPassword: '',
    name: '',
    country: '',
    phone: '',
    photo: null,
    photoName: '',
    photoType: '',
    photoSize: ''
  });

  const handleChange = (e: ChangeEvent&lt;HTMLInputElement | HTMLSelectElement&gt;) =&gt; {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  const handlePhotoChange = (e: ChangeEvent&lt;HTMLInputElement&gt;) =&gt; {
    const file = e.target.files ? e.target.files[0] : null;
    if (file) {
      setFormData({
        ...formData,
        photo: file,
        photoName: file.name,
        photoType: file.type,
        photoSize: file.size
      });
    }
  };

  const handleSubmit = async (e: FormEvent) =&gt; {
    e.preventDefault();

    // Convertir la foto a base64
    let base64Photo = '';
    if (formData.photo) {
      base64Photo = await toBase64(formData.photo);
    }

    // Crear el objeto con los datos del formulario
    const dataToSend = {
      email: formData.email,
      password: formData.password,
      confirmPassword: formData.confirmPassword,
      name: formData.name,
      country: formData.country,
      phone: formData.phone,
      photo: {
        base64: base64Photo,
        name: formData.photoName,
        type: formData.photoType,
        size: formData.photoSize
      }
    };

    // Enviar la solicitud POST a la API
    try {
      const response = await fetch('https://leonardojose.dev/api/usuarios', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(dataToSend)
      });

      if (!response.ok) {
        throw new Error('Error en la solicitud');
      }

      const result = await response.json();
      console.log('Form data submitted:', result);
    } catch (error) {
      console.error('Error:', error);
    }
  };

  const toBase64 = (file: File) =&gt; new Promise&lt;string&gt;((resolve, reject) =&gt; {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () =&gt; resolve(reader.result as string);
    reader.onerror = (error) =&gt; reject(error);
  });

  return (
    &lt;div className="App container mx-auto p-4"&gt;
      &lt;form onSubmit={handleSubmit} className="bg-white p-6 rounded shadow-md w-full max-w-lg mx-auto"&gt;
        &lt;h2 className="text-2xl font-bold mb-4"&gt;Datos de Acceso&lt;/h2&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Email:&lt;/label&gt;
          &lt;input
            type="email"
            name="email"
            value={formData.email}
            onChange={handleChange}
            required
            className="input input-bordered w-full"
          /&gt;
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Clave:&lt;/label&gt;
          &lt;input
            type="password"
            name="password"
            value={formData.password}
            onChange={handleChange}
            required
            className="input input-bordered w-full"
          /&gt;
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Confirmar Clave:&lt;/label&gt;
          &lt;input
            type="password"
            name="confirmPassword"
            value={formData.confirmPassword}
            onChange={handleChange}
            required
            className="input input-bordered w-full"
          /&gt;
        &lt;/div&gt;
        &lt;h2 className="text-2xl font-bold mb-4"&gt;Datos Personales&lt;/h2&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Nombre:&lt;/label&gt;
          &lt;input
            type="text"
            name="name"
            value={formData.name}
            onChange={handleChange}
            required
            className="input input-bordered w-full"
          /&gt;
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;País:&lt;/label&gt;
          &lt;select
            name="country"
            value={formData.country}
            onChange={handleChange}
            required
            className="select select-bordered w-full"
          &gt;
            &lt;option value=""&gt;Seleccione un país&lt;/option&gt;
            {countries.map(country =&gt; (
              &lt;option key={country} value={country}&gt;{country}&lt;/option&gt;
            ))}
          &lt;/select&gt;
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Teléfono:&lt;/label&gt;
          &lt;input
            type="tel"
            name="phone"
            value={formData.phone}
            onChange={handleChange}
            required
            className="input input-bordered w-full"
          /&gt;
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Foto:&lt;/label&gt;
          &lt;input
            type="file"
            name="photo"
            accept="image/*"
            onChange={handlePhotoChange}
            required
            className="file-input file-input-bordered w-full"
          /&gt;
        &lt;/div&gt;
        &lt;button type="submit" className="btn btn-primary w-full"&gt;Enviar&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  );
};

export default Formulario;</code></pre><p>Observa que el código anterior ahora:</p><ol><li><strong>Importa DaisyUI CSS:</strong> <strong><code>import 'daisyui/dist/full.css';</code></strong> asegurando que DaisyUI está incluido.</li><li><strong>Uso de Clases de DaisyUI:</strong> Se aplicaron clases de DaisyUI como <strong><code>input input-bordered</code></strong>, <strong><code>select select-bordered</code></strong>, y <strong><code>btn btn-primary</code></strong> para estilizar los componentes del formulario.</li><li><strong>Estructura del Formulario:</strong> La estructura del formulario incluye contenedores como <strong><code>div</code></strong> y <strong><code>form</code></strong> con clases de TailwindCSS y DaisyUI para un diseño limpio y responsive.</li></ol><p>Con ello logramos una apariencia mucho más linda y agradable. </p><p>Puedes observar en el siguiente video, lo que hemos realizado y lo que explicaremos más adelante, con lo cual vas a reforzar los conocimientos.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/b6gUczGRcgM?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Formularios en ReactJS  -  Conociendo a react hook form" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="incorporando-a-react-hook-form">Incorporando a react-hook-form</h2><p>En el código anterior, la gestión de los valores la hicimos, usando a <code>useState</code>, no tenemos gestión de errores y tampoco tenemos validaciones. Podemos realizar esto de forma manual, sí, pero si nos aprovechamos de un paquete que ya realiza esto, y que además es altamente usado por la comunidad, es decir, está probado y es código que los desarrolladores ya conocemos, ¿mejor aún cierto?</p><p>Para instalar a react-hook-form, solo debemos hacer lo siguiente:</p><pre><code>npm install react-hook-form</code></pre><p>Luego incorporamos los hooks del paquete de esta forma:</p><pre><code>const { register, handleSubmit, watch, formState: { errors } } = useForm&lt;FormData&gt;();
const photoFile = watch('photo');</code></pre><ul><li><code>register</code>, nos permite indicarle al hook qué campos del formulario vamos a utilizar.</li><li><code>handleSubmit</code>, es la función propia de react-hook-form que va a manejar los valores de los campos (esto antes lo haciamos nosotros manualmente), y luego podemos indicar un callback para llamar nuestro backend.</li><li><code>watch</code>, nos permite colocar una referencia para saber en todo momento el valor de un campo, en nuestro caso observamos la foto porque debemos convertirla a base64.</li><li><code>formState</code>, nos indica en que estado está el formulario, por enviar, enviando o enviado y nos permite obtener los errores que se produjeron al momento de validar los campos (sí! Validaciones 😃)</li></ul><p>Y que tal agregar algunas validaciones, como por ejemplo que la contraseña sea a la confirmación, y que la contraseña además tenga 8 caracteres, un número y un carácter especial. Queremos adicionalmente que la foto venga en formato png o jpg. Todo esto es posible haciendo uso de <code>react-hook-form</code>.</p><p>Veamos algunos de los ajustes que debemos realizar para que nuestro formulario sea fácilmente administrable con <code>react-hook-form</code>:</p><pre><code>&lt;input
            type="email"
            {...register('email', { required: "Email es requerido" })}
            className="input input-bordered w-full"
          /&gt;
          {errors.email &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.email.message}&lt;/span&gt;}</code></pre><p>Observa que agregamos el uso de <code>register</code> y colocamos una validación, que sea obligatorio el valor, luego si hay error lo mostramos en pantalla.</p><p>Ahora veamos un caso más complejo, la contraseña:</p><pre><code>&lt;input
            type="password"
            {...register('password', {
              required: "Clave es requerida",
              minLength: { value: 8, message: "La clave debe tener al menos 8 caracteres" },
              pattern: {
                value: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&amp;])[A-Za-z\d@$!%*#?&amp;]{8,}$/,
                message: "La clave debe contener al menos un número y un caracter especial"
              }
            })}
            className="input input-bordered w-full"
          /&gt;
          {errors.password &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.password.message}&lt;/span&gt;}</code></pre><p>Se puede identificar que cuando usamos el <code>register</code>, podemos indicar si es obligatorio (<code>required</code>), el mensaje de error (<code>message</code>) y el tamaño del valor mínimo aceptable (<code>minLength</code>) y el patrón (<code>pattern</code>) del valor para obligar colocar un número y un carácter especial. </p><p>Con solo eso ya hemos hecho validaciones complejas de ese campo.</p><p>Veamos como queda el componente luego de todos los cambios realizados:</p><pre><code>import React from 'react';
import { useForm, SubmitHandler } from 'react-hook-form';
import 'daisyui/dist/full.css';

const countries = [
  "Argentina", "Bolivia", "Brasil", "Chile", "Colombia",
  "Ecuador", "Guyana", "Paraguay", "Perú", "Surinam",
  "Uruguay", "Venezuela"
];

interface FormData {
  email: string;
  password: string;
  confirmPassword: string;
  name: string;
  country: string;
  phone: string;
  photo: FileList;
}

const Formulario: React.FC = () =&gt; {
  const { register, handleSubmit, watch, formState: { errors } } = useForm&lt;FormData&gt;();
  const password = watch('password');
  const photoFile = watch('photo');

  const onSubmit: SubmitHandler&lt;FormData&gt; = async (data) =&gt; {
    // Convertir la foto a base64
    let base64Photo = '';
    const file = data.photo[0];
    if (file) {
      base64Photo = await toBase64(file);
    }

    // Crear el objeto con los datos del formulario
    const dataToSend = {
      email: data.email,
      password: data.password,
      confirmPassword: data.confirmPassword,
      name: data.name,
      country: data.country,
      phone: data.phone,
      photo: {
        base64: base64Photo,
        name: file.name,
        type: file.type,
        size: file.size
      }
    };

    // Enviar la solicitud POST a la API
    try {
      const response = await fetch('https://leonardojose.dev/api/usuarios', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(dataToSend)
      });

      if (!response.ok) {
        throw new Error('Error en la solicitud');
      }

      const result = await response.json();
      console.log('Form data submitted:', result);
    } catch (error) {
      console.error('Error:', error);
    }
  };

  const toBase64 = (file: File) =&gt; new Promise&lt;string&gt;((resolve, reject) =&gt; {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () =&gt; resolve(reader.result as string);
    reader.onerror = (error) =&gt; reject(error);
  });

  return (
    &lt;div className="App container mx-auto p-4"&gt;
      &lt;form onSubmit={handleSubmit(onSubmit)} className="bg-white p-6 rounded shadow-md w-full max-w-lg mx-auto"&gt;
        &lt;h2 className="text-2xl font-bold mb-4"&gt;Datos de Acceso&lt;/h2&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Email:&lt;/label&gt;
          &lt;input
            type="email"
            {...register('email', { required: "Email es requerido" })}
            className="input input-bordered w-full"
          /&gt;
          {errors.email &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.email.message}&lt;/span&gt;}
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Clave:&lt;/label&gt;
          &lt;input
            type="password"
            {...register('password', {
              required: "Clave es requerida",
              minLength: { value: 8, message: "La clave debe tener al menos 8 caracteres" },
              pattern: {
                value: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&amp;])[A-Za-z\d@$!%*#?&amp;]{8,}$/,
                message: "La clave debe contener al menos un número y un caracter especial"
              }
            })}
            className="input input-bordered w-full"
          /&gt;
          {errors.password &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.password.message}&lt;/span&gt;}
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Confirmar Clave:&lt;/label&gt;
          &lt;input
            type="password"
            {...register('confirmPassword', {
              required: "Confirmar clave es requerido",
              validate: value =&gt; value === password || "Las claves no coinciden"
            })}
            className="input input-bordered w-full"
          /&gt;
          {errors.confirmPassword &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.confirmPassword.message}&lt;/span&gt;}
        &lt;/div&gt;
        &lt;h2 className="text-2xl font-bold mb-4"&gt;Datos Personales&lt;/h2&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Nombre:&lt;/label&gt;
          &lt;input
            type="text"
            {...register('name', { required: "Nombre es requerido" })}
            className="input input-bordered w-full"
          /&gt;
          {errors.name &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.name.message}&lt;/span&gt;}
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;País:&lt;/label&gt;
          &lt;select
            {...register('country', { required: "País es requerido" })}
            className="select select-bordered w-full"
          &gt;
            &lt;option value=""&gt;Seleccione un país&lt;/option&gt;
            {countries.map(country =&gt; (
              &lt;option key={country} value={country}&gt;{country}&lt;/option&gt;
            ))}
          &lt;/select&gt;
          {errors.country &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.country.message}&lt;/span&gt;}
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Teléfono:&lt;/label&gt;
          &lt;input
            type="tel"
            {...register('phone', {
              required: "Teléfono es requerido",
              pattern: {
                value: /^[0-9()-]+$/,
                message: "Solo se permiten números, paréntesis y guiones"
              }
            })}
            className="input input-bordered w-full"
          /&gt;
          {errors.phone &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.phone.message}&lt;/span&gt;}
        &lt;/div&gt;
        &lt;div className="mb-4"&gt;
          &lt;label className="block text-sm font-medium mb-1"&gt;Foto:&lt;/label&gt;
          &lt;input
            type="file"
            {...register('photo', {
              required: "Foto es requerida",
              validate: {
                acceptedFormats: files =&gt; files[0] &amp;&amp; ['image/jpeg', 'image/png'].includes(files[0]?.type) || "Solo se permiten fotos en formato PNG o JPG"
              }
            })}
            accept="image/*"
            className="file-input file-input-bordered w-full"
          /&gt;
          {errors.photo &amp;&amp; &lt;span className="text-red-500 text-sm"&gt;{errors.photo.message}&lt;/span&gt;}
        &lt;/div&gt;
        &lt;button type="submit" className="btn btn-primary w-full"&gt;Enviar&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  );
};

export default Formulario;</code></pre><p>En el código anterior hemos realizado lo siguiente:</p><ol><li><strong>Validación del Email:</strong> <strong><code>required: "Email es requerido"</code></strong>.</li><li><strong>Validación de la Clave:</strong></li></ol><ul><li>Mínimo 8 caracteres.</li><li>Al menos un número y un carácter especial.</li></ul><p>3. <strong>Validación de Confirmar Clave:</strong> Verificamos que coincida con la clave.</p><p>4. <strong>Validación del País:</strong> Obligatorio.</p><p>5.<strong> Validación del Teléfono:</strong> Obligatorio y solo números</p><p>6. <strong>Validación de la Foto:</strong></p><ul><li>Obligatorio.</li><li>Formatos permitidos: PNG o JPG.</li></ul><p>En el video indicado anteriormente vas a poder visualizar en detalle cada paso realizado y podrás ver como evolucionamos en el uso de formulario en la aplicación que tomamos de ejemplo.</p><p>Para cualquier duda o comentario, me puedes enviar un mensaje en mis redes:</p><ul><li>YouTube: <a href="https://www.youtube.com/leonardocastillo79" rel="noopener noreferrer nofollow">@LeonardoCastillo79</a></li><li>LinkedIn: <a href="https://www.linkedin.com/in/leonardo-castillo-4911571a/" rel="noopener noreferrer nofollow">Leonardo José Castillo Lacruz</a></li><li>Twitter: <a href="https://twitter.com/ljcl79" rel="noopener noreferrer nofollow">@ljcl79</a></li><li>GitHub: <a href="https://github.com/ljcl79" rel="noopener noreferrer nofollow">@ljcl79</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Primeros pasos con GoLang ]]>
                </title>
                <description>
                    <![CDATA[ Empecemos por conocer qué es GoLang o simplemente "Go": > GoLang, también conocido como simplemente ‘Go’, es un lenguaje de programación creado por Rob Pike, Ken Thompson y Robert Griesemer en 2007, lanzado en 2009, que cuenta con el patrocinio de Google. Está diseñado para trabajar en soluciones que requieren ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/primeros-con-golang/</link>
                <guid isPermaLink="false">6593f14ce058cc03f9581595</guid>
                
                    <category>
                        <![CDATA[ Golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Mon, 15 Apr 2024 13:39:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/04/Youtube-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Empecemos por conocer qué es GoLang o simplemente "Go":</p><blockquote><em>GoLang, también conocido como simplemente ‘Go’, es un lenguaje de programación creado por Rob Pike, Ken Thompson y Robert Griesemer en 2007, lanzado en 2009, que cuenta con el patrocinio de Google. Está diseñado para trabajar en soluciones que requieren un alto rendimiento y escalabilidad, especialmente en el procesamiento de datos y las conexiones de red.</em></blockquote><h3 id="caracter-sticas-claves-de-go-">Características claves de Go:</h3><ol><li><strong>Compilado</strong>: Go se compila y genera código de máquina, lo que lo hace rápido y eficiente. Ya que nos evitamos el tiempo de interpretación de código que otros lenguajes como Javsscript o Python requieren.</li><li><strong>Tipado estático</strong>: Las variables deben declararse con un tipo específico, esto garantiza el orden y permite evitar problemas por inconsistencias a la hora de usar las variables.</li><li><strong>Programación concorrente/paralela nativa</strong>: Go tiene excelentes capacidades para manejar múltiples hilos de ejecución. Esto hace de Go eficiente y orientado a aplicaciones donde la cantidad de solicitudes sea alta.</li><li><strong>Garbage Collector Nativo</strong>: Go gestiona automáticamente la memoria, de esta manera nosotros no debemos preocuparnos por alocar o liberar la memoria como en otros lenguajes como C++.</li><li><strong>Simplista al extremo</strong>: Go elimina características complejas como clases y herencias, sobreescrituras de métodos, hasta el famoso try/catch fue removido, esto porque Go se enfoca en la velocidad y el perfomance, pero ojo es de igual forma podemos aplicar el paradigma de la programación orientada a objetos.</li></ol><h3 id="diferencias-entre-lenguajes-compilados-e-interpretados-">Diferencias entre lenguajes compilados e interpretados:</h3><ul><li><strong>Compilado</strong>: En Go, todas las instrucciones del código fuente se convierten en lenguaje de máquina antes de la ejecución. Esto proporciona mayor velocidad y por lo tanto mayor rendimiento.</li><li><strong>Interpretado</strong>: En lenguajes interpretados, como Python o JavaScript, el código se ejecuta línea por línea en tiempo real, lo cual puede tener una perdida de velocidad, pero también se gana la facilidad de realizar cambios. </li></ul><p>Recuerden no todo es ganancia o pérdida, aunque en el caso anterior me quedo con los lenguajes compilados.</p><h2 id="instalaci-n-del-ambiente">Instalación del ambiente</h2><p>Para trabajar con Go, debemos hacer lo que denominamos la instalación de la infraestructura de trabajo, que se reduce a tener el compilador disponible en nuestro computador.</p><p>Go, para recordar es un proyecto Open Source y multiplataforma, es decir lo podemos usar en Windows, Linux o Mac. &nbsp;Tenemos procesos de instalación para cada uno de los sistemas operativos, sólo debemos acceder al sitio web de Go (<a href="https://go.dev">https://go.dev</a>) y buscar el correspondiente a nuestro sistema operativo.</p><p>Veamos los pasos para cada uno de ellos:</p><ul><li>Windows. &nbsp;Siempre simple, es sólo hacer la descarga del programa de instalación y seguir el paso a paso de asistente hasta finalizar.</li><li>Linux. Lo instalamos vía línea de comandos, desde una terminal, ejecutando el comando:</li></ul><pre><code>sudo snap install go --classic</code></pre><ul><li>Mac. Es bajar el archivo .pkg y realizar el proceso de desempaquetado.</li></ul><p>Finalizado el proceso, podemos comprobar si todo está bien, ejecutando el comando:</p><pre><code>go version</code></pre><p>Este comando debe mostrar algo como esto:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2024/04/image-1.png" class="kg-image" alt="image-1" width="341" height="74" loading="lazy"></figure><p>Al llegar a este punto, ya tenemos todo lo necesario para iniciar nuestro camino con Go.</p><h2 id="hola-mundo-en-go">Hola Mundo en Go</h2><p>Como siempre, cuando iniciamos en un lenguaje de programación, lo tradicional es que iniciemos con un primer programa, el cual llamamos "Hola Mundo', donde tenemos ese primer contacto y ya nos comenzamos a empoderar de la nueva herrramienta de desarrollo. </p><p>En el caso de Go, no será la excepción, empecemos con este "Hola Mundo", el cual sigue a continuación:</p><figure class="kg-card kg-code-card"><pre><code>package main

import "fmt"

func main() {
	// De esta forma comentamos el código
	fmt.Println("Hola Mundo!")
}
</code></pre><figcaption></figcaption></figure><p>Revisemos las líneas de código anteriores y destaquemos algunas características:</p><ul><li>Existe la importación de un paquete llamado <code>main</code>, el cual define la estructura de la aplicación, todo programa Go usa este paquete.</li><li>La función <code>main</code>, es el punto de inicial o de entrada de nuestra aplicación. A partir de allí desarrollamos nuestra aplicación.</li><li>Luego tenemos las importaciones, Go trabaja con paquetes, de entrada él tiene lo que se denomina la librería standard, que ya nos provee de una serie de funcionalidades interesantes (<a href="https://pkg.go.dev/std">https://pkg.go.dev/std</a>), en esta caso de esa librería, tomamos a "fmt" para realizar operaciones de escritura en la pantalla.</li></ul><p>Con ello ya tenemos nuestra primera aplicación Go ejecutandose!</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/D30UWujqftU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Primeros pasos con Go - Tutorial" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="tipos-de-datos-en-go">Tipos de Datos en Go</h2><p>En Go podemos manejar datos de los tipos primitivos, así como en cualquier lenguaje de programación, entremos en detalle:</p><h3 id="tipo-de-datos-n-mericos-enteros-">Tipo de datos númericos enteros:</h3><p>En el caso de los números enteros, en Go, podemos decir si vamos a manejar con signo positivo o negativo, o simplemente los positivos. Además tienen la particularidad, de definir el rango, de esta forma podemos ser óptimos y usar los recursos de memoria que realmente necesitamos.</p><p><strong>Enteros con signo</strong>:</p><ul><li><strong><code>int8</code></strong>: Puede almacenar valores desde -128 hasta 127. Esto se denominan enteros de 8 bits.</li><li><strong><code>int16</code></strong>: Almacena enteros con signo de 16 bits, con un rango de -32,768 a 32,767.</li><li><strong><code>int32</code></strong> (también conocido como <code>rune</code>): Utilizado para caracteres Unicode y tiene un rango de -2,147,483,648 a 2,147,483,647.</li><li><code><strong>int64</strong></code>: Maneja números enteros con signo de 64 bits, con un rango de -9,223,372,036,854,775,808 a 9,223,372,036,854,775,807.</li><li><strong><code>int</code></strong>: Su tamaño depende de la arquitectura subyacente (32 o 64 bits).</li></ul><p><strong>Enteros sin signo, o sólo valores positivos.</strong></p><ul><li><strong><code>uint8</code></strong> (también conocido como <code>byte</code>): Representa enteros sin signo de 8 bits, con valores entre 0 y 255.</li><li><code><strong>uint16</strong></code>: Números desde 0 a 65,535. Almacena enteros sin signo de 16 bits.</li><li><strong><code>uint32</code></strong>: Utilizado para números grandes sin signo, con un rango de 0 a 4,294,967,295.</li><li><strong><code>uint64</code></strong>: Almacena enteros sin signo de 64 bits, con un rango de 0 a 18,446,744,073,709,551,615.</li><li><strong><code>uint</code></strong>: Su tamaño también depende de la arquitectura (32 o 64 bits).</li></ul><h3 id="tipos-de-datos-n-meros-decimales-o-de-punto-flotante-">Tipos de datos números decimales o de punto flotante:</h3><p>En el caso de números decimales, no tenemos tanta variedad de tipos, sólo tenemos una diferencia y es por la arquitectura donde se ejecuta el programa (32 o 64 bits), pero vale la pena conocer sus características:</p><p><strong><code>float32</code></strong>:</p><ul><li>Representa números de punto flotante de <strong>32 bits</strong>.</li><li>Puede almacenar valores con precisión hasta <strong>7 dígitos decimales</strong>.</li><li>Es adecuado para cálculos donde la precisión no necesita ser extremadamente alta.</li></ul><p><strong><code>float64</code></strong>:</p><ul><li>Es el tipo de punto flotante más común en Go.</li><li>Utiliza <strong>64 bits</strong> para representar números de punto flotante.</li><li>Ofrece mayor precisión que <code>float32</code>, con capacidad para almacenar hasta <strong>15 dígitos decimales</strong>.</li></ul><p>Es común usar <code><strong>float64</strong></code><strong> </strong>por su mayor precisión a la hora de almacenar datos (15 decimales!) </p><h3 id="otros-tipos-de-datos-primitivos">Otros tipos de datos primitivos</h3><p>En Go, podemos manejar:</p><ul><li><code><strong>string</strong></code><strong>: </strong>Con este tipo de datos podemos almacenar cadenas alfanuméricas, para definir sus valores, basta usar comillas dobles ("") o comillas simples (''), el compilador las reconoce de igual forma.</li><li><strong><code>bool</code>: &nbsp;</strong>A partir de este tipo de dato podemos manejar valores lógicos: true o false, los cuales son muy útiles cuando manejamos condiciones.</li><li><code><strong>struct:</strong></code><strong> </strong>Este tipo de dato es muy útil, debido a que con el podemos definir datos personalizados. En Go no hay clases, pero con struct, es posible crear una suerte de interface o clase de sólo propiedades. De esta forma podemos una entidad con sus campos y manejarla de forma agrupada.</li></ul><h3 id="tipos-de-datos-compuestos">Tipos de datos compuestos</h3><p>En Go podemos los siguientes tipos de datos que son combinaciones o mezcla de tipos de datos primitivos:</p><ul><li>Arreglos: En un tipo de datos que representa una colección de elementos del mismo tipo. La longitud de un array es fija y se especifica al declararlo, a diferencia de otros lenguajes donde declaramos arreglos sin tamaño, en Go debemos indicar el tamaño al declararlo.</li></ul><p>Ejemplo:</p><pre><code>var miArray [5]int // Un array de 5 enteros</code></pre><p>En Go, no es posible mezclar los tipos de datos de los valores que son parte de un arreglo, como por ejemplo si podemos hacer en Javascript.</p><ul><li>Slices: Son arreglos sin tamaño, es decir, si hay posibilidad de definir arreglos sin tamaño pero Go, los denonima como slides. Varía un poco su forma de declararlos.</li></ul><p>Ejemplo:</p><pre><code>paises := []string{"Venezuela","Brasil","Chile"}</code></pre><ul><li>Mapas: Son un tipo de datos usado por excelencia, se definen como lista basada en el par: clave-valor, es decir cada valor está identificado por un índice personalizado (a diferencia de los arreglos que son identificado por un índice numérico secuencial).</li></ul><p>Algunas características de los maps:</p><ul><li>Las <strong>claves</strong> deben ser <strong>únicas</strong> y <strong>comparables</strong> (por ejemplo, strings, ints, etc.). </li><li>Los <strong>valores</strong> pueden ser de cualquier tipo y se define al declararlo. </li><li>Para declarar un <strong>mapa</strong>, utilizamos la sintaxis <code>map[TipoClave]TipoValor</code>.</li><li>Los mapas no siguen un orden, ni mantienen el orden de la declaración, hay que tener eso en cuenta.</li></ul><p>Ejemplo:</p><pre><code>miMapa := map[string]string{
   "clave1": "valor1",
   "clave2": "valor2",
   "clave3": "valor3",
}
</code></pre><p>Para agregar nuevos valores al mapa, basta apenas indicar la nueva clave y su valor como sigue:</p><pre><code>miMapa["clave4"] = "Valor4"</code></pre><p>Para borrar una de las propiedades del mapa usamos el método <code>delete</code>:</p><pre><code>delete(miMapa, "clave1")</code></pre><p>En el caso anterior estariamos borrando la clave <code>clave1</code> de <code>miMapa</code> .</p><p>Adicionalmente en Go, tenemos la posibilidad gestionar las variables, a nivel de memoria, este tipo de datos se denomina <strong>Punteros. </strong>En realidad los punteros no son variables sino referencias a variables, esto es especialmente útil para poder reutilizar espacios de memoria por ejemplo al usar parámetros (que por defecto hacen una copia y consumen nuevamente los recursos).declarar</p><p>Veamos como se usa un puntero sobre una variable numerica:</p><pre><code>package main

import "fmt"

func main() {
    var miVariable int = 10
    var miPuntero *int = &amp;miVariable

    fmt.Println("Valor de miVariable:", miVariable)
    fmt.Println("Valor de miPuntero (dirección de memoria):", miPuntero)
    fmt.Println("Valor apuntado por miPuntero:", *miPuntero)
}
</code></pre><p>Observa que si pedimos el valor del puntero, lo que vamos a obtener es la dirección de memoria de la caja o variable. Este tipo de datos es super útil cuando queremos tener control de la gestión de memoria.</p><h2 id="declaraci-n-de-variables-en-go">Declaración de variables en Go</h2><p>En Go tenemos 2 formas de declarar variables, veamos en detalle:</p><ul><li>Declaración formal:</li></ul><pre><code>var nombreVariable &lt;tipo de datos&gt;</code></pre><ul><li>Inferencia del tipo a partir del valor inicial:</li></ul><pre><code>var nombreVariable = &lt;Valor&gt;</code></pre><p>La variable toma el tipo de dato del valor indicado.</p><p>Adicionalmente tenemos una forma abreviada de declarar variables:</p><pre><code>nombreVariable := &lt;Valor&gt;</code></pre><p>De lo anterior, Go infiere de igual forma el tipo de dato a partir del valor indicado.</p><p>Puedes profundizar en el uso de los tipos de datos y declaración de variables en el siguiente video:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/ZcFgnVfTuD8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Go (Golang): Tipos de Datos, Variables, Arreglos y Slices explicados!" name="fitvid1"></iframe>
          </div>
        </div>
      </figure><h2 id="condicionales-en-go">Condicionales en Go</h2><p>En Go, similar a otros lenguajes de programación, usamos las palabras reservadas <code>if</code>, <code>else</code>, <code>switch</code>, para poder evaluar expresiones y ejecutar código de forma condicional, es decir ejecutar código cuando se cumple o no cumple alguna condición. </p><p>Una característica interesante de Go, es que las condiciones no necesitan parentesis para ser evaluadas, a diferencia de otros lenguajes de programación donde es obligatorio encerrar entre paréntesis la condición, así sea simple.</p><p>Ejemplos:</p><ol><li>Condición simple.</li></ol><pre><code>package main

import (
	"fmt"
)

func main() {
	var userInput int
	fmt.Print("Ingresa un valor: ")
	fmt.Scan(&amp;userInput)

	if userInput &gt; 10 {
		fmt.Print("Valor ingresado mayor a 10: ")
	}
}</code></pre><p>2. Condicional evaluando cuando se cumple y cuando no se cumple la condición</p><pre><code>package main

import (
	"fmt"
)

func main() {
	var userInput int
	fmt.Print("Ingresa un valor: ")
	fmt.Scan(&amp;userInput)

	if userInput &gt; 10 {
		fmt.Print("Valor ingresado mayor a 10: ")
	} else {
		fmt.Print("Valor ingresado es menor o igual 10: ")
	}
}
</code></pre><p>3. Condicionales anidados, donde evaluamos múltiples condiciones:</p><pre><code>package main

import (
	"fmt"
)

func main() {
	var userInput int
	fmt.Print("Ingresa un valor: ")
	fmt.Scan(&amp;userInput)

	if userInput &gt; 10 {
		fmt.Print("Valor ingresado mayor a 10: ")
	} else if userInput &lt; 10 {
		fmt.Print("Valor ingresado es menor a 10: ")
	} else {
		fmt.Print("Valor ingresado es 10: ")
	}
}
</code></pre><p>Veamos ahora como funciona el comando <code>switch</code>, el cual nos permite de forma fácil evaluar múltiples condiciones, permitiendo ejecutar bloques de código. Sigue un ejemplo:</p><pre><code>package main

import (
	"fmt"
)

func main() {
	var fruta string

	fmt.Print("Ingresa un valor alfanumérico: ")
	fmt.Scan(&amp;fruta)

	switch fruta {
	case "manzana":
		fmt.Println("Has ingresado 'manzana'.")
	case "banana":
		fmt.Println("Has ingresado 'banana'.")
	case "pera":
		fmt.Println("Has ingresado 'pera'.")
	default:
		fmt.Println("No reconozco ese valor.")
	}
}
</code></pre><h2 id="lazos-en-go">Lazos en Go</h2><p>Es tarea cotidiana, dentro del desarrollo de software, que existan procesos o bloques de código que necesitamos ejecutar más de una vez, Go implementa las estructuras de repetición usando sólo la palabra reservada <code>for</code>.</p><p>La sintaxis general del for es la siguiente:</p><pre><code>for [Instrucción Inicial]; [Condición]; [Instrucción Posterior] {
    // Acción a realizar en cada iteración
}</code></pre><p>Veamos como implementamos algnos procesos de repetición:</p><ol><li>Repita para, donde definimos la cantidad de veces que el proceso se va a repetir. Por ejemplo queremos ejecutar la suma de los primeros 100 numeros. </li></ol><pre><code>package main

import "fmt"

func main() {
    sum := 0

    for i := 0; i &lt;= 100; i++ {
        if i%2 != 0 {
            sum += i
        }
    }

    fmt.Printf("La suma de los números impares entre 0 y 100 es: %d\n", sum)
}
</code></pre><p>Observa que se definen:</p><ul><li>Instrucción Inicial: Se asigna el valor 0 al contador.</li><li>Condición: Se verifica si ya llegamos al número 100.</li><li>Instrucción Posterior: Avanzamos al siguiente número.</li></ul><p>2. Ahora simulemos lo que en otros lenguajes conocemos como for-each, usando <code>for</code></p><pre><code>package main

import (
	"fmt"
)

func main() {

	var m map[string]string = map[string]string{
		"clave1": "valor1",
		"clave2": "valor2",
		"clave3": "valor3",
		"clave4": "valor4",
	}

	for k, v := range m {
		fmt.Println(k, v)
	}

}
</code></pre><p>3. Implementando un repita mientras usando <code>for</code>, en Go no tenemos while o do-while, pero podemos representar la acción de la siguiente forma:</p><pre><code>package main

import (
	"fmt"
	"strings"
)

func main() {
	var fruta string

	for {
		fmt.Print("Ingresa una fruta: ")
		fmt.Scan(&amp;fruta)

		// Convertimos la entrada a minúsculas para evitar problemas de capitalización
		fruta = strings.ToLower(fruta)

		if fruta == "naranja" {
			fmt.Println("¡Has ingresado una naranja! El bucle se detiene.")
			break
		} else {
			fmt.Println("No es una naranja. Inténtalo nuevamente.")
		}
	}
}
</code></pre><p>Observa que usamos el for sin otros parámetros:</p><pre><code>for {
}</code></pre><p>Mediante lo anterior generamos un bucle infinito y es nuestra responsabilidad a lo interno validar la condición de salida. Es este punto el que no define si es un repita mientras o un repita hasta, recordando que la diferencia entre ellos es que el repita mientras se puede o no ejecutar, pues la condición se debe evaluar al inicio del proceso y el repita hasta al menos se ejecuta una vez. El código anterior es un repita hasta si lo analizamos en detalle porque se lee la fruta al menos una vez, veamos como seria en forma de repita mientras.</p><pre><code>package main

import (
	"fmt"
	"strings"
)

func main() {
	fruta := "banana"

	for {

		if fruta == "naranja" {
			fmt.Println("¡Has ingresado una naranja! El bucle se detiene.")
			break
		} else {
			fmt.Println("No es una naranja. Inténtalo nuevamente.")
		}

		fmt.Print("Ingresa una fruta: ")
		fmt.Scan(&amp;fruta)

		// Convertimos la entrada a minúsculas para evitar problemas de capitalización
		fruta = strings.ToLower(fruta)

	}
}
</code></pre><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/QgQRm37CiS0?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Explorando Go: Mapas, Condicionales y Bucles" name="fitvid2"></iframe>
          </div>
        </div>
      </figure><p><br>Go, es un lenguaje altamente demandado actualmente, usado por empresas de gran tamaño, así que es una herramienta super atractiva de aprender, para más contenidos puedes verificar mis perfiles en:</p><ul><li>Freecodecamp.org: <a href="https://www.freecodecamp.org/espanol/news/author/leonardo/">https://www.freecodecamp.org/espanol/news/author/leonardo/</a></li><li>Linkedin: <a href="https://www.linkedin.com/in/leonardo-castillo-4911571a/">https://www.linkedin.com/in/leonardo-castillo-4911571a/</a></li><li>Youtube: <a href="https://www.youtube.com/channel/UCBltbOgO-DUW_SVa2zSg3Yg"></a><a href="https://www.youtube.com/leonardocastillo79">https://www.youtube.com/channel/UCBltbOgO-DUW_SVa2zSg3Yg</a></li><li>Twitter: <a href="https://twitter.com/ljcl79">https://twitter.com/ljcl79</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Consumiendo APIs con React Query ]]>
                </title>
                <description>
                    <![CDATA[ Actualmente, las aplicaciones web, destacan por tener bien diferenciadas las capas: frontend, backend y datos. La comunicación entre el frontend (o interface) y el backend (o capa que implementa la lógica de negocio), es realizada a través de lo que se denomina APIs, generalmente del tipo REST. Esta es una ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/consumiendo-apis-con-react-query/</link>
                <guid isPermaLink="false">65dde46f1222b503eefb0d61</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Tue, 05 Mar 2024 15:17:42 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/03/Captura-de-tela-de-2024-03-04-16-43-16.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Actualmente, las aplicaciones web, destacan por tener bien diferenciadas las capas: frontend, backend y datos. La comunicación entre el frontend (o interface) y el backend (o capa que implementa la lógica de negocio), es realizada a través de lo que se denomina APIs, generalmente del tipo REST. Esta es una tarea crítica y cotidiana.</p><p>Si deseas profundizar más en cuanto a los conceptos de APIs y como es posible realizar la implementación de una capa de backend te invito a revisar este artículo, acá en freeCodeCamp en Español: <a href="https://www.freecodecamp.org/espanol/news/construyendo-una-api-rest-con-node-fastify-sequelize-postgresql/"><strong>Construyendo una API REST con Node + Fastify + Sequelize + PostgreSQL</strong></a><strong>.</strong></p><p>Cuando implementamos aplicaciones usando ReactJS, para realizar este proceso de consumo podemos usar las funciones especiales que la librería ofrece: useState y useEffect. Si deseas verificar en forma práctica como realizar este consumo, puedes revisar este material <a href="https://www.freecodecamp.org/espanol/news/consumiendo-una-api-rest-con-react-js/"><strong>Consumiendo APIs con ReactJS: Aprende useEffect y useState</strong></a><strong> </strong>que tenemos disponible aquí en freeCodeCamp en Español.</p><p>Ahora bien, el proceso anterior puede ser un poco extenso, es por ello en las próximas líneas vamos a mostrar un paquete que tiene como propósito facilitar y simplificar el consumo de APIs REST desde React. El nombre del paquete es: <a href="https://tanstack.com/query/latest">React Query o como es conocido actualmente: TanStack Query.</a></p><h2 id="-c-mo-instalar-react-query">¿Cómo instalar React Query?</h2><p>Para realizar la instalación del paquete, hacemos uso de la NPM, con el siguiente comando:</p><pre><code>npm i @tanstack/react-query</code></pre><p>En caso de utilizar <code>yarn</code> como gestor de paquetes puedes usar el comando:</p><pre><code>yarn add @tanstack/react-query</code></pre><p>Si usas bun, el paquete ya tienen soporte, entonces la instalación se realiza con el siguiente comando:</p><pre><code>bun add @tanstack/react-query</code></pre><p>Instalado el paquete, lo siguiente es entender los pasos que debemos ejecutar para hacer su implementación, estos los podemos resumir de esta forma:</p><h2 id="habilitando-el-uso-del-paquete-en-la-aplicaci-n">Habilitando el uso del paquete en la aplicación</h2><p>Para dejar disponible a React Query, debemos envolver los componentes que necesiten consumir datos, su comportamiento o uso es similar a context API, en nuestro caso vamos a suponer que estamos en una aplicación pequeña, que tiene una barra de navegación <em>(Nav)</em>, un banner o <em>Hero</em>, una Galería de imágenes (<em>Gallery</em>) y un pie o <em>Footer</em>, por lo que vamos a realizar este proceso a nivel de la definición de la aplicación.</p><p>Importamos los componentes necesarios:</p><pre><code>import { QueryClientProvider, QueryClient } from '@tanstack/react-query';</code></pre><p>Creamos una instancia del cliente QueryCliente:</p><pre><code>const queryClient = new QueryClient();</code></pre><p>Luego envolvemos la aplicación quedando el código del componente principal de esta forma:</p><pre><code>const queryClient = new QueryClient();

function App() {

  return (
    &lt;QueryClientProvider client={queryClient}&gt;
    &lt;div style={{display:'flex', width:'100vw', flexDirection:'column'}}&gt;
      &lt;NavBar&gt;&lt;/NavBar&gt;
      &lt;Hero&gt;&lt;/Hero&gt;
      &lt;Gallery&gt;&lt;/Gallery&gt;
      &lt;Footer&gt;&lt;/Footer&gt;
    &lt;/div&gt;
    &lt;/QueryClientProvider&gt;
  )
}</code></pre><p>Con el código anterior, ya podemos hacer uso de React Query, en cualquiera de los componentes envueltos.</p><h2 id="consumiendo-una-api-rest">Consumiendo una API REST</h2><p>Para hacer uso de React Query, lo primero es hacer las importaciones necesarias dentro del componente, en este caso importamos el hook <em>useQuery</em> de esta forma:</p><pre><code>import { useQuery } from '@tanstack/react-query';</code></pre><p>Luego le definimos la configuración necesaria a useQuery colocando los siguientes parámetros:</p><ul><li><strong>queryKey</strong>, nombre o identificador de la consulta a realizar</li><li><strong>queryFn</strong>, es la promesa encargada de procesar la consulta, importante tener en cuenta que se debe esperar a la ejecución, React Query hace eso por nosotros, entonces no debemos colocar await.</li></ul><p>Al usar el hook useQuery, él nos retorna una serie de variables superútiles, veamos esto con un código de ejemplo:</p><pre><code>  const { isLoading, data, isError, error, isSuccess } = useQuery(
            {
                queryKey: ['productos'],
                queryFn: async () =&gt; {
                    const res = await fetch('https://apiexpress-x7sl.onrender.com/productos');
                    return res.json();
                }
            });</code></pre><p>Observemos que recibimos un objeto con propiedades que desestructuramos en la misma línea, revisemos cada una de ellas:</p><ul><li><strong>isLoading</strong>, es una variable booleana (true, false) que nos indica si la consulta está cargando datos.</li><li><strong>data</strong>, son los datos retornados por la consulta, que estarán disponibles luego de la carga, es decir, cuando isLoading pase a ser <em>false</em>.</li><li><strong>isError</strong>, otra variable booleana (true/false) que nos indica si hubo algún error a la hora de ejecutar la consulta.</li><li><strong>error</strong>, almacena cualquier error que haya sucedido, importante para poder dar respuesta al usuario en caso de que no haya podido realizarse la consulta.</li><li><strong>isSuccess</strong>, variable booleana que va a estar en true cuando la consulta sea realizada con éxito.</li></ul><p>Además de las anteriores, existen otras variables que nos dan aún más información y que puedes consultar en la <a href="https://tanstack.com/query/v4/docs/framework/react/reference/useQuery">documentación oficial del paquete</a>.</p><h2 id="aplicando-usequery-en-un-componente-real">Aplicando useQuery en un componente real</h2><p>Vamos a construir un componente que haga uso de &nbsp;React Query, de acuerdo a lo indicado anteriormente. Este componente retornará un mensaje, en caso de que esté cargando los datos, un mensaje de error en caso de suceder algún problema y mostrará la data de productos consultados (porque estamos consultando una API de productos).</p><p>Veamos el componente:</p><pre><code class="language-Javascript">import { useQuery } from '@tanstack/react-query';
import Product from '../Product/Product';



const Gallery = () =&gt; {
    
    const { isLoading, data, isError, error } = useQuery(
            {
                queryKey: ['productos'],
                queryFn: async () =&gt; {
                    const res = await fetch('https://apiexpress-x7sl.onrender.com/productos');
                    return res.json();
                }
            });
    
    if (isLoading)
        return (
            &lt;&gt;
                &lt;h2&gt;Cargando productos...&lt;/h2&gt;
            &lt;/&gt;
        )

    if (isError) {
        return (
            &lt;&gt;
                &lt;h2&gt;{error.message}&lt;/h2&gt;
            &lt;/&gt;
        )
    }
  return (
    &lt;&gt;
        &lt;div className="grid place-items-center w-full bg-base-200"&gt;
            &lt;div className="max-w-5xl py-24 content-center justify-center"&gt;
                &lt;h1 className="text-4xl  text-center font-bold"&gt;Our Services&lt;/h1&gt;
                &lt;div className="grid mt-12 md:grid-cols-3 grid-cols-1 gap-8"&gt;
            
            {
                data?.map((producto) =&gt; {
                    return &lt;Product key={producto._id} product={producto}&gt;&lt;/Product&gt;
                })
            }
            &lt;/div&gt;
         &lt;/div&gt;
        &lt;/div&gt;
    &lt;/&gt;
  )
}

export default Gallery</code></pre><p>Nótese que no se usan estados ni efectos secundarios (<em>useState</em> o <em>useEffect</em>), el código está completamente limpio y legible, características positivas de usar React Query para esta tarea tan crítica.</p><p>En este vídeo, puedes seguir el paso a paso de la aplicación de los conceptos anteriormente estudiados. </p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/OJMat-Buon4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Cómo usar react query para el consumo de APIs en aplicaciones reactJS | Tutorial práctico" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p>Si tienes alguna duda, me puedes mandar un mensaje a mi <a href="https://www.linkedin.com/in/leonardo-castillo-4911571a/">LinkedIn</a>, con gusto te respondo.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Javascript para Backend - Comparando Node / Deno / Bun ]]>
                </title>
                <description>
                    <![CDATA[ Cuando trabajamos con JavaScript, lo primero que se nos viene a la mente es un navegador, aunque esta percepción ha cambiado, debido a que JavaScript ha dejado de ser un lenguaje de la web, para convertirse en un lenguaje de programación maduro, con impacto y con gran performance del lado ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/javascript-para-backend-comparando-node-deno-bum/</link>
                <guid isPermaLink="false">65534941bcc2a003e70e262f</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Mon, 26 Feb 2024 16:51:57 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/02/fondo.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Cuando trabajamos con JavaScript, lo primero que se nos viene a la mente es un navegador, aunque esta percepción ha cambiado, debido a que JavaScript ha dejado de ser un lenguaje de la web, para convertirse en un lenguaje de programación maduro, con impacto y con gran performance del lado del servidor. Esto sumado a su facilidad en cuanto a sintaxis, lo han hecho una herramienta muy interesante para la implementación de capas de backend. </p><p>Así es, JavaScript ha entrado en la competencia en el área de backend, gracias a que existen entornos de ejecución que proveen características interesantes que hacen atractivo a JavaScript para el desarrollo de APIs por ejemplo.</p><p>Conozcamos algunos de los protagonistas de JavaScript para Backend:</p><h2 id="node-o-nodejs">Node o NodeJS</h2><p>Node nació en el año 2009, de la mano de Ryan Dalh, con la idea de poder ejecutar JavaScript fuera de los navegadores. Entre sus características más resaltantes está:</p><ul><li><strong>La ejecución no bloqueante:</strong> para las operaciones de entrada y salida (ejemplo archivos, base de datos o integraciones), Node no se queda esperando, sino que continúa el flujo y al tener respuesta ejecuta una función de callback, lo que permite a Node hacer múltiples operaciones al mismo tiempo. </li><li><strong>Programación orientada a eventos</strong>, el cual es un paradigma de programación que permite ejecutar código a partir de eventos, como pueden ser acciones de usuarios o respuesta de ejecución de operaciones. Lo cual permite ejecutar múltiples acciones en simultáneo, ya que se crea un hilo separado por ejecución, el cual queda escuchando y reaccionando a los eventos.</li><li><strong>Alto Rendimiento</strong>: Gracias al motor V8 de Google Chrome, Node.js puede ejecutar código JavaScript de forma eficiente y rápida.</li><li><strong>Arquitectura Escalable</strong>: Node tiene la característica de que puede crecer en la medida que sea necesario, ya que por su gestión de eventos e hilos, sumado a su naturaleza asincrónica, es posible atender múltiples peticiones de forma simultáneas. Con la adición de recursos de hardware la aplicación escala sin ningún problema.</li><li><strong>Gestión de Paquetes con NPM</strong>: Node.js tiene disponible lo que se denomina el repositorio de paquetes que agregan funciones, el cual conocemos como NPM o Node Package Manager.</li><li><strong>Comunidad Activa</strong>: Una de las fortalezas más interesantes de un software es cuando tiene una comunidad activa importante, ¿por qué? Porque esto garantiza primero que el software es usado, luego que el software va a ser mejorado y que es probado continuamente. Node tiene una comunidad muy grande, por lo cual esto es una característica fundamental.</li><li><strong>Multiplataforma</strong>: Node.js puede ser utilizado para desarrollar aplicaciones para diferentes plataformas, incluyendo servidores web, aplicaciones de escritorio y móviles.</li></ul><p>Ahora comentemos algunos puntos que podrían ser mejorados en NodeJS:</p><ul><li>Soporte nativo a Typescript. Para usar Typescript en Node es necesario hacer una serie de instalaciones y configuraciones que ya deberían venir realizadas disponibles al instalar.</li><li>Mejora en la gestión de paquetes adicionales. Actualmente, los modulos adicionales o lo que conocemos como node_modules, generan una carga en general excesiva y a veces innecesaria.</li></ul><p>Con una estimativa de 30 millones de websites usando NodeJs como plataforma de backend, es por ahora el rey de los entornos de ejecución para JavaScript del lado del servidor.</p><p>Acá puedes apreciar algunas características de forma práctica de NodeJS</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/BA1-NU5edwQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Introducción a Node.js: Guía para principiantes" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="deno">Deno</h2><p>El segundo entorno de ejecución, también fue creado por Ryan Dalh, como has podido apreciar Deno es la inversión del nombre Node 😲. En su lanzamiento, en el año 2018, el creador indicó que Deno llegaba para corregir las cosas que no se realizaron correctamente en Node, es decir que Deno nació algunos puntos que Node aún no tiene o apenas lanzó recientemente. La primera versión estable oficial de Deno fue lanzada en 2020. </p><p>Deno está construido bajo Rush, un lenguaje que conocido por ser seguro y con alto performance. Al igual que NodeJS, Deno trabaja con el <em>eventloop</em> o el paradigma de programación en eventos, con ello el performance es similar, así como las aplicaciones que se pueden construir son escalables. </p><h3 id="caracter-sticas-o-diferenciales-de-deno">Características o diferenciales de Deno</h3><ul><li><strong>Seguridad y permisos, </strong>Deno es estricto en cuanto al acceso a los recursos, es decir, debe indicarse de manera explícita el uso de lectura y escritura de archivos, acceso a la red o acceso a las variables de ambiente (enviroment), de esta forma, nuestro código actua de forma menos arriesgada, ya que es necesario liberar los permisos mediante opciones de ejecución.</li><li><strong>Typescript nativo, </strong>desde que lo instalas, Deno tiene la capacidad de ejecutar código Typescript, sin necesidad de realizar alguna configuración. Es posible parametrizar la generación, sólo si queremos cambiar los ajustes por defecto.</li><li><strong>Importación de paquetes eficiente,</strong> Deno no tiene node_modules, incluso si importas paquetes de la npm (que es una maravilla), la gestión de las importaciones se pueden hacer directamente de una url, por lo que no es necesario instalar previamente, al importar, si el paquete no está disponible será descargado.</li><li><a href="https://deno.com/kv"><strong>DenoKV,</strong></a> es una base de datos que se encuentra en fase beta, pero que próximamente será parte del kernel estable de Deno, la cual nos permite sin necesidad de usar otra herramienta, hacer uso de una base de datos tipo Clave-Valor (NoSQL), lo cual facilita el trabajo en cuanto a manejar una capa de datos sencilla.<br>Sistemas de permisiones</li></ul><p>Acá podemos ver mediante ejemplos prácticos, las características o diferenciales que Deno posee:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/ISa1-Af-bl0?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Deno - Entorno avanzado para ejecución de Javascript en el Backend" name="fitvid1"></iframe>
          </div>
        </div>
      </figure><h2 id="bun">Bun</h2><p>El más reciente en torno de ejecución de JavaScript para Backend es Bun, creado en 2021 y lanzada su primera versión estable en 2024. Bun está construido en el lenguaje de bajo nivel Zig, con lo cual logra una eficiencia y una velocidad importante.</p><h3 id="caracter-sticas-o-diferenciales-de-bun">Características o diferenciales de Bun</h3><ul><li><strong>Velocidad de ejecución o performance, </strong>ha llamado poderosamente la atención por la velocidad de ejecución, la cual es sorprendentemente mayor que la de Deno y Node. </li><li><strong>Compatibilidad con paquetes de node (npm)</strong>, Es compatible con la mayoría de paquetes de Node, por lo que es muy simple levantar en Bun una aplicación hecha en Node, ya que el grado compatibilidad es alto, por ejemplo paquetes como ExpressJS o creación de aplicaciones React con Vite funcionan sin ningún problema en Bun.</li><li><strong>Typescript y JSX nativos,</strong> es posible implementar código TS sin ninguna configuración adicional, e incluso construir código JSX sin tener que agregar alguna libreria. </li></ul><p>Bun aún está bastante joven, pero parece tener un futuro promisorio. En el siguiente video podrás observar algunos ejemplos de uso práctico de Bun.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/2NddtqsNAuw?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="¿Qué es bun y cómo usarlo para ejecutar javascript para backend?" name="fitvid2"></iframe>
          </div>
        </div>
      </figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Nociones básicas de Javascript ]]>
                </title>
                <description>
                    <![CDATA[ A continuación encontraras explicación a temas básicos o fundamentales del lenguaje Javascript, siendo que este lenguaje, es el referente actual del mercado, es interesante e importante manejar los conceptos iniciales. Por qué nace Javascript? En el año 1995, la www (World Wide Web) o lo que conocemos comunmente denominamos internet ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/tipos-de-datos-en-javascript/</link>
                <guid isPermaLink="false">65a69eea51a59a0455c92bca</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Sat, 03 Feb 2024 04:24:25 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/02/Captura-de-tela-de-2024-02-02-08-25-32.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A continuación encontraras explicación a temas básicos o fundamentales del lenguaje Javascript, siendo que este lenguaje, es el referente actual del mercado, es interesante e importante manejar los conceptos iniciales.</p><h2 id="por-qu-nace-javascript">Por qué nace Javascript?</h2><p>En el año 1995, la www (World Wide Web) o lo que conocemos comunmente denominamos internet de forma errada, iniciaba su auge, con la posibilidad de compartir de forma fácil contenido (para aquel momento, la forma común era a través de CD/DVDs y el medio común eran las revistas). Este contenido era estático pero de forma fácil llegaba a nuestras pantallas. Sólo que en la medida que el contenido fue creciendo, se hizo necesario mejorar la interactividad de la comunicación, de forma tal que el usuario no solo fue un receptor de contenidos, en su lugar se convirtió en un actor importante del proceso, con el envío de datos.</p><p>Así nace Javascript, como un lenguaje orientado a mejorar la usabilidad de los sitios web, a permtir que las interfaces tomaran dinamismo y que la comunicación entre cliente y sitios web fuese más simple e incluso más divertida.</p><p>Pero el punto no quedó allí, Javascript fue creciendo, al inicio era normal que un navegador como Internet Explorer o Netscape Navigator (los navegadores que existían y dominaron el inicio de la WWW), no soportase el uso de Javascript, pero su crecimiento fue tal, que en la medida que la WWW evolucionaba, Javascript lo hacía también, convirtiendose en parte fundamental de estos programas.</p><p>Ahora mismo Javascript, ya es un lenguaje de programación maduro, con impacto no sólo en la plataforma web, sino que se ha convertido en un lenguaje multifacético, que permite trabajar del lado de la interfaz (web o frontend) y del lado del servidor (backend), además tenemos Javascript &nbsp;para desarrollo móvil (React Native por ejemplo) y Javascript para desarrollo de aplicaciones Desktop o Cliente/Servidor (Electron).</p><p>Por todo lo anterior (un muy breve resumen) es importante conocer los aspectos básicos de Javascript, que se indican a continuación.</p><h2 id="tipos-de-datos-en-javascript">Tipos de Datos en Javascript</h2><p>Javascript como lenguaje de programación, tiene interesantes características que lo hacen versatil y funcional, una de ellas es como gestiona sus tipos de datos. En Javascript los tipos no se definen, es decir hablamos que es un lenguaje de tipado débil, pero eso no significa que no tenga tipos, por ello es fundamental conocer cuales de ellos están disponibles:</p><ul><li><strong><strong>String </strong></strong>para gestión variables con valores alfanuméricos y cualquier otro carácter.</li><li><strong><strong>Number </strong></strong>para representar los números ya sean enteros o decimales.</li><li><strong><strong>BigInt</strong></strong>, usado para representar números enteros.</li><li><strong><strong>Boolean</strong></strong> para manipular los valores lógicos (true y false)</li><li><strong><strong>Object,</strong></strong> con el fin de poder gestionar y representar objetos, conocidos por sus propiedades y métodos</li><li><strong><strong>Symbol</strong></strong>, son un nuevo tipo de tipo de datos primitivo introducido en la versión ES6 del lenguaje. Se utilizan para representar valores únicos que se pueden utilizar como identificadores o claves en los objetos. También se utilizan para crear propiedades y métodos privados en las clases.</li><li><strong><strong>Null</strong></strong>, El valor nulo representa la ausencia intencional de cualquier valor de objeto. Existe como tipo de dato primitivo de JavaScript y se trata como falso para las operaciones booleanas.</li><li><strong><strong>undefined, </strong></strong>es un valor primitivo automáticamente asignado a las variables que solo han sido declarados o a los argumentos formales para los cuales no existe argumentos reales. Es decir si no asignamos ningún valor a una variable, está tendrá ese valor <em><em>undefined</em></em>.</li><li><strong><strong>Function</strong></strong>, es un tipo de dato especial usado para reconocer cuando trabajamos sobre un función, que es un fragmento de código que realiza una tarea específica y devuelve un valor.</li></ul><h3 id="como-obtener-el-tipo-de-dato-de-un-campo">Como obtener el tipo de dato de un campo</h3><p>En Javascript como comentamos al anteriormente, la gestión de tipos de datos es débil, no es necesario indicar el tipo de dato cuando declaramos una variable, por ello es importante saber como determinar el tipo de dato a una variable declarada. Este tipo de dato será automáticamente atribuido por Javascript a partir del valor asignado.</p><p>Para realizar esta tarea podemos usar la función <code>typeof</code> , como sigue a continuación:</p><pre><code>console.log(typeof 42);// Tipo de dato esperado: "number"

console.log(typeof 'texto');// Tipo de dato esperado: "string"

console.log(typeof true);// Tipo de dato esperado: "boolean"

console.log(typeof varSinValor);// Tipo de dato esperado: "undefined"</code></pre><p>Un punto importante a recordar es que Javascript hace conversión automática de los tipos, a partir de un valor asignado, pero existen formas para asignar tipos de forma implicita, esto lo logramos usando clases como:</p><p><strong><strong>Number, </strong></strong>con esta clase conseguimos transformar un valor numérico en un tipo de dato explicito Number, cuando el valor no es un número válido será retornado el valor especial NaN.</p><p>Ejemplo de uso:</p><pre><code> let numeroExplicito = Number('234');</code></pre><p><strong><strong>String</strong></strong>, a partir de la declaración de esta clase, el valor pasado por argumento será definido como tipo string forma explicita.</p><p>Ejemplo de uso:</p><pre><code>let cadenaExplicita = String(123);</code></pre><p><strong><strong>Boolean</strong></strong>, usando esta clase podemos definir un valor como tipo de dato lógico, recordando que al hacer conversión automática, el valor 0 se toma como false y cualquier otro valor se toma como true.</p><p>Ejemplo de uso: </p><pre><code>let valorLogicoExplicito = Boolean(0);//tomará el valor false</code></pre><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/YKyL9QqwT3M?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Tipos de Datos en JavaScript: Todo lo que necesitas saber" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="gesti-n-de-variables-en-javascript">Gestión de variables en Javascript</h2><p>Anteriormente revisamos que tipos de datos podemos manejar en Javascript, de forma tal que los datos que manejemos en nuestros programas serán clasificados dentro de alguno de esos tipos, ahora bien estos datos, cómo son manejados?, de que forma serán almacenados? </p><p>Los lenguajes de programación en general, usan la memoria del computador como pequeñas cajas, en donde cada caja corresponde a un dato (que tiene su tipo), ese espacio o caja en la memoria se denomina variable. A través de las variables podemos mantener, crear, actualizar y borrar los datos en la memoria y es lo que va a permitir que podamos desarrollar programas genéricos, preparados para distintos escenarios y valores.</p><p>En Javascript las variables se manejan haciendo de tres palabras reservadas: <code>var</code>, <code>const</code> y <code>let</code>. Cada una de ellas tiene particularidades</p><h3 id="hablemos-de-var">Hablemos de var</h3><p>La palabra reservada <em><em>var </em></em>permite definir una variable (o espacio de memoria) que va a estar disponible en todo el sistema, a partir del punto que sea definida y esta variable es posible cambiarla en cualquier momento, veamos un ejemplo:</p><pre><code>var nombre = 'Diego';

function primeraFuncion() {
    console.log('La variable nombre esta disponible en la primera funcion. Nombre:' + nombre);
}

function segundaFuncion() {
    console.log('La variable nombre esta disponible en la segunda funcion. Nombre:' + nombre);
}

console.log('La variable nombre está disponible en el cuerpo principal del programa. Nombre:' + nombre);

//Llamadas a las funciones
primeraFuncion();
segundaFuncion();</code></pre><p>En el ejemplo anterior, la variable <code>nombre</code> está disponible en todo el programa, porque está declarada al inicio del programa.</p><p>Si ejecutamos el código vamos a obtener el siguiente resultado:</p><p>La variable nombre está disponible en el cuerpo principal del programa. Nombre:Diego<br>La variable nombre esta disponible en la primera funcion. Nombre:Diego<br>La variable nombre esta disponible en la segunda funcion. Nombre:Diego</p><p>Ahora si cambiamos la declaración de la variable, y la colocamos dentro de <code>primeraFuncion</code> y ejecutamos nuestro código, vamos a obtener un error.</p><pre><code>function primeraFuncion() {
    var nombre = 'Diego';
    console.log('La variable nombre esta disponible en la primera funcion. Nombre:' + nombre);
}

function segundaFuncion() {
    //Error porque la variable nombre no está disponible en este ámbito o alcance  
    console.log('La variable nombre esta disponible en la segunda funcion. Nombre:' + nombre);
}

//Error porque la variable nombre no está disponible en este ámbito o alcance
console.log('La variable nombre está disponible en el cuerpo principal del programa. Nombre:' + nombre);
primeraFuncion();
segundaFuncion();</code></pre><p>¿Por qué sucede ese error? Sucede por el alcance de la variable, cuando declaramos la variable dentro de <code>primeraFuncion</code> su alcance es sólo dentro de esa función, al intentar llamarla fuera de esa función, la variable no existe, por ello el error.</p><p>Veamos ahora cuál es el comportamiento de una variable declarada con <code>var</code> en cuanto a modificar su valor: cuando se declara de esta forma, es posible modificar su valor sin problema, esto puede ser bueno pero a la vez puede generarnos problemas si no estamos conscientes de su alcance, veamos el primer código pero ahora modificando el valor de la variable en algunas de las funciones:</p><pre><code>var nombre = 'Diego';

function primeraFuncion() {
    nombre = 'Leonardo';
    console.log('La variable nombre esta disponible en la primera funcion. Nombre:' + nombre);
}

function segundaFuncion() {
    console.log('La variable nombre esta disponible en la segunda funcion. Nombre:' + nombre);
}

console.log('La variable nombre está disponible en el cuerpo principal del programa. Nombre:' + nombre);
primeraFuncion();
segundaFuncion();</code></pre><p>El resultado de la ejecución del código anterior es:</p><p>La variable nombre está disponible en el cuerpo principal del programa. Nombre:Diego<br>La variable nombre esta disponible en la primera funcion. Nombre:Leonardo<br>La variable nombre esta disponible en la segunda funcion. Nombre:Leonardo</p><p>Observamos como la variable fue modificada en la primera función, y de allí en adelante la variable ya queda con ese valor, si en la segunda función eso no era lo esperado, se puede producir un comportamiento no esperado, sin generar errores de ejecución.</p><h3 id="hablemos-de-let">Hablemos de let</h3><p>Con la palabra reservada <em><em>let </em></em>también podemos declarar variables en nuestras aplicaciones <em><em>Javascript</em></em>. Si en nuestro ejemplo inicial cambiamos por <em><em>var</em></em> por <em><em>let</em></em>, no vamos a obtener ninguna diferencia, pues el alcance de la variable es global. Ahora donde se comporta <code>let</code> de manera diferente? Cuando definimos variables dentro de bloques (recordando que un bloque puede ser el código dentro de un condicional <code>if</code>o dentro del estructura de repetición <code>while</code>o <code>for</code>) el comportamiento va a ser diferente. Veamos el siguiente ejemplo:</p><pre><code>function mostrandoDiffVarYLet(nombre) {
    //Alcance de la función  
    if (nombre == 'Diego') {
        //Alcance del bloque.    
        var profesion = 'Escritor';
        let apellido = 'Castillo';
        console.log('Dentro del bloque. Profesión:' + profesion);
        console.log('Dentro del bloque. Apellido:' + apellido);
    }
    console.log('Fuera del bloque. Profesión:' + profesion); //En esta línea va a generarse un error  
    //porque apellido fue declarado dentro del alcance del bloque  
    console.log('Fuera del bloque. Apellido:' + apellido);
}
mostrandoDiffVarYLet('Diego');</code></pre><p>En la ejecución del código anterior, al intentar usar la variable <code>apellido</code> fuera del bloque condicional, se genera un error. De esta forma vemos que la variable declara con <code>var</code> se mantiene disponible durante el ámbito o alcance de la función, mientras que la variable declara con <code>let</code> se mantiene solo dentro del alcance del bloque.</p><p>Nosotros podemos usar <code>let</code> en lugar de <code>var</code> en ese ejemplo, simplemente movemos la declaración al alcance de la función y la variable <code>profesion</code> se va a comportar como queremos y sólo haciendo uso de la palabra <code>let</code> .</p><p>En cuanto a modificar o no el valor de una variable declarada con <code>let</code> , cambia con respecto a declararla con <code>var</code> ? No cambia, es posible modificar su valor siempre y cuando esté dentro del ámbito o alcance. Veamos el ejemplo ahora ajustado, usando <code>let</code> para todas las variable y actualizando su valor.</p><pre><code>function mostrandoDiffVarYLet(nombre) {
    //Alcance de la función  
    let profesion = 'Escritor';
    if (nombre == 'Diego') {
        //Alcance del bloque.    
        let apellido = 'Castillo';
        console.log('Dentro del bloque. Profesión:' + profesion);
        console.log('Dentro del bloque. Apellido:' + apellido);
    }
    //Actualizamos su valor  
    profesion = 'Corredor de F1';
    console.log('Fuera del bloque. Profesión:' + profesion);
    //En esta línea va a generarse un error.  
    //porque apellido fue declarado dentro del alcance del bloque.  
    //La dejamos comentada  
    //console.log('Fuera del bloque. Apellido:'+apellido);
}
mostrandoDiffVarYLet('Diego');</code></pre><p>Al ejecutar el código anterior, obtenemos lo siguiente:</p><p>Dentro del bloque. Profesión:Escritor<br>Dentro del bloque. Apellido:Castillo<br>Fuera del bloque. Profesión:Corredor de F1</p><h3 id="hablemos-de-const">Hablemos de const</h3><p>Cuando usamos la palabra reservada <code>const</code> le indicamos a Javascript dos cosas:</p><ol><li>Nuestra variable es para usarse dentro del ámbito o alcance del bloque, es decir se comporta como <code>let</code></li><li>Nuestro valor se define al declarar la variable y no se cambia en ningún otro punto del sistema. Si se intenta cambiar vamos a obtener un error.</li></ol><p>Veamos esto con un ejemplo:</p><pre><code>function mostrandoDiffVarYLetYConst(nombre) {  
    //Alcance de la función  
    if (nombre == 'Diego') {    
        //Alcance del bloque.    
        var profesion = 'Escritor';    
        let apellido = 'Castillo';    
        const edad = 13;    
        console.log('Dentro del bloque. Profesión:'+profesion);    
        console.log('Dentro del bloque. Apellido:'+apellido);    
        console.log('Dentro del bloque. Edad:'+edad);    
        //Esta línea va a generar un error   
         //No es posible cambiar el valor    
         edad = 14;  
    }  
    profesion = 'Corredor de F1';  
    console.log('Fuera del bloque. Profesión:'+profesion);  
    //En estas líneas se va a generar errores.  
    //porque apellido y edad fueron declarados dentro del alcance del bloque  
    console.log('Fuera del bloque. Apellido:'+apellido);  
    console.log('Fuera del bloque. Edad:'+edad);
}

mostrandoDiffVarYLetYConst('Diego');</code></pre><p>Observando el código anterior, vemos que al intentar modificar el valor de <code>edad</code> se genera un error, porque declaramos, le dijimos a Javascript que esa variable en memoria debe permanecer con su valor inicial, al intentarse cambiar, Javascript sigue lo definido, protege la variable y marca un error.</p><p>Luego observamos que tanto const como let, su ámbito o alcance es de bloque, es decir, son validas dentro de una función, o dentro de un condicional o dentro de un bloque de repetición, de acuerdo a como sean declarados.</p><p>Para el caso de <code>const</code>, existen un conjunto de variables que denominamos estructuradas, que posible que cambiemos su "valor", dejo entre comillas esto, porque en realidad, sigue el comportamiento anteriormente descrito, pero vamos a tener la sensación que es posible cambiar su valor, cuando la realidad es que estas variables se manejan por referencia, es decir su valor corresponde a su posición o referencia en la memoria, la cual al ser <code>const</code> sigue siendo constante, lo que sucede es que por ser estructurados vamos a acceder de forma diferente, veamos esto en detalle, revisanod Arreglos y Objetos en Javascript</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/7uJBNKXlYkM?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Alcance o ámbito de variables en Javascript" name="fitvid1"></iframe>
          </div>
        </div>
      </figure><h2 id="manejo-de-arreglos-en-javascript">Manejo de arreglos en Javascript</h2><p><br>Los arreglos o <em>arrays</em>, son estructuras que nos permiten manipular de forma eficientes datos que por su naturaleza son repetibles, es decir que se pueden agrupar y se manipulados como una variable única. Ejemplo de ello: los nombres de productos, las notas de un alumno, las ciudades de un país. Imaginemos que sea necesario crear una variable para almacenar por ejemplo las ciudades de un país, sería una gestión ineficiente y que muy posiblemente nos llevaría a errores.</p><p>Es por ello que los lenguajes de programación implementan esta estructura de datos llamada <em>arrays</em>, y Javascript no es la excepción.</p><p>Veamos como declarar un arreglo y sus operaciones básicas:</p><h3 id="declaraci-n-de-un-arreglo-o-array">Declaración de un arreglo o array</h3><p>En Javascript podemos declarar una variable de tipo array, de las siguientes formas:</p><p><strong>Declaración a partir del constructor de la clase</strong></p><pre><code>//Declara un arreglo vacio
const arreglo = new Array();

//Declara un arreglo con elemento iniciales
const arreglo1 = new Array('Elemento 1','Elemento 2');

//Declara un arreglo con un tamaño definido pero sin elementos definidos
const arreglo2 = new Array(2);</code></pre><p><strong>Declaración en notación literal</strong></p><pre><code>//Declara un arreglo vacio
const arreglo = [];

//Declara un arreglo con elemento iniciales
const arreglo1 = ['Elemento 1','Elemento 2'];</code></pre><p>Los arreglos pueden almacenar datos de distintos tipos: primitivos y estructurados. Esto significa que podemos tener arreglos de números, cadenas de caracteres, datos lógicos, así como arreglos de objetos y arreglos dentro de arreglos, esto es super interesante y poderoso.</p><p>En buena práctica mantener los arreglos con tipos de datos similares, pero es importante saber que Javascript acepta arreglos con diferentes tipos de datos, por lo que un arreglo como el que sigue sería totalmente válido:</p><pre><code>const arregloMultiple = [3, 5, 9, 'Hola', true];</code></pre><p>Sólo que mi recomendación es evitar este tipo de arreglos, para mantener buenas prácticas y un código mejor estructurado.</p><h3 id="m-todos-de-arreglos">Métodos de arreglos</h3><p>Uno de los puntos de más interesante de la gestión de arreglos, es su dinamismo en cuanto a los datos, es decir nosotros podemos: agregar, actualizar y borrar elementos a un arreglo de forma fácil.</p><p><strong>Como accedemos o actualizamos un arreglo?</strong></p><p>Lo hacemos a través de su posición, porque cada uno de los elementos de un arreglo posee una posición o indice única, comenzando desde la posición 0, por lo que el primer elemento de un arreglo siempre estará en esta posición.</p><pre><code>const arreglo = ['Primer elemento', 'Segundo elemento'];

//Para mostrar el primer elemento ejecutamos:
console.log(arreglo[0]);</code></pre><p></p><p><strong>Agregando elementos:</strong></p><p>A un arreglo podemos agregarle elementos en su inicio o en su final, para ello nos aprovechamos de 2 métodos que Javascript nos provee:</p><ul><li><strong>push()</strong>, nos permite agregar elementos al final de un arreglo.</li></ul><pre><code>const arreglo = ['Primer elemento', 'Segundo elemento'];

//Agregando al final
arreglo.push('Tercer elemento');

//El arreglo ahora será: ['Primer elemento', 'Segundo elemento','Tercer elemento'];</code></pre><ul><li><strong>unshift()</strong>, nos permite agregar elementos al inicio de un arreglo</li></ul><pre><code>const arreglo = ['Primer elemento', 'Segundo elemento'];

//Agregando al final
arreglo.unshift('Elemento antes del primero');

//El arreglo ahora será: ['Elemento antes del primero','Primer elemento', 'Segundo elemento'];</code></pre><p><strong>Eliminando elementos:</strong></p><p>Podemos eliminar elementos tanto al inicio como al final, para ello usamos los siguientes métodos:</p><ul><li><strong>pop() </strong>nos permite borrar o extraer elementos del final del arreglo, el elemento extraido lo podemos pasar a una variable si fuese necesario.</li></ul><pre><code>const arreglo = ['Primer elemento', 'Segundo elemento','Tercer elemento'];

//Extrayendo del final
const elemento = arreglo.pop();

//El arreglo ahora será: ['Primer elemento', 'Segundo elemento'];
</code></pre><ul><li><strong>shift() </strong>nos permite borrar o extraer elementos del inicio del arreglo, al igual que el caso anterior, el elemento extraido lo podemos pasar a una variable.</li></ul><pre><code>const arreglo = ['Primer elemento', 'Segundo elemento'];

//Extrayendo del inicio
const elemento = arreglo.shift();

//El arreglo ahora será: ['Segundo elemento'];</code></pre><p><strong>Actualizando el valor de un elemento</strong></p><p>Para ello lo que definimos es un nuevo valor en la posición del elemento que deseamos actualizar, veamos un ejemplo:</p><pre><code>const arreglo = ['Primer elemento', 'Segundo elemento'];

arreglo[1] = 'Segundo elemento actualizado';</code></pre><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/2HqTNHn7gA8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Aprende los fundamentos de los arreglos y sus métodos básicos en JavaScript" name="fitvid2"></iframe>
          </div>
        </div>
      </figure><h3 id="otros-m-todos-de-uso-regular-de-los-arreglos-">Otros métodos de uso regular de los arreglos:</h3><p>Existe una cantidad interesante de métodos, que es importante conocer, de igual forma te dejo acá el link a la <a href="https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array#m%C3%A9todos_de_instancia">documentación de la MDN </a>con todos los métodos disponibles.</p><p>Veamos los métodos que en particular más uso cuando trabajo con arreglos:</p><ul><li><strong>filter()</strong>, nos permite obtener un nuevo arreglo filtrado, es decir, aplicamos una condición y será creado un nuevo arreglo con sólo aquellos elementos que cumplan la condición indicada, <code>filter</code> no modifica el arreglo original. Importante tener esto en cuenta.</li></ul><pre><code>const arreglo = [1, 3, 5, 7, 9, 11];

//Queremos solo los elementos menores a 6

const arregloFiltrado = arreglo.filter((e) =&gt; e &lt; 6);
//arregloFiltrado será: [1, 3, 5]</code></pre><p></p><ul><li><strong>findIndex(), </strong>nos retorna la posición del primer elemento que cumpla la condición indicada. Es útil cuando conocemos el valor pero necesitamos la posición, para realizar algún proceso.</li></ul><pre><code>const arreglo = ['Primer elemento', 'Segundo elemento','Tercer elemento'];

//Obteniendo la posición del elemento
const posicion = arreglo.findIndex((e) =&gt; e === 'Segundo elemento');
//Posición tendrá el valor 1, que es la posición del elemento buscado</code></pre><ul><li><strong>slice(),</strong> permite que obtegamos un pedazo (slice) o subarreglo de un arreglo, interesante e importante cuando deseamos realizar algún proceso sobre sólo un parte del arreglo.</li></ul><pre><code>const arreglo = ['1 elemento', '2 elemento','3 elemento','4 elemento'];

//Extrayendo un pedazo
const subArreglo = arreglo.slice(1,2);

//De esta forma subArreglo será: ['2 elemento','3 elemento']
//Podemos también solo indicar el inicio y obtendremos el resto del arreglo
const subArreglo2 = arreglo.slice(2);
//De esta forma subArreglo2 será: ['3 elemento','4 elemento'];</code></pre><ul><li><strong>include()</strong>, permite determinar si un elemento del arreglo corresponde con el parámetro pasado, de esta forma podemos evaluar si dentro del array existe este valor.</li></ul><pre><code>const arreglo = ['1 elemento', '2 elemento','3 elemento','4 elemento'];

//Verificamos si el valor existe, esto no va a retornar true o false
const existeValor = arreglo.includes('1 eleme1nto');

//Mostramos el valor de la verificación
console.log(existeValor);</code></pre><p>Además de estos métodos, existen una cantidad bien importante de otros métodos que nos permiten realizar múltiples operaciones sobre los arrays, puedes consultar <a href="https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array#m%C3%A9todos_de_instancia" rel="noopener">este enlace de la MDN</a>, con la explicación de cada uno de ellos.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/LIJadLrc9mA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Profundizando en Métodos de Arreglos" name="fitvid3"></iframe>
          </div>
        </div>
      </figure><h2 id="funciones-en-javascript">Funciones en Javascript</h2><p>Cuando hablamos de funciones, nos viene a la mente aquel concepto matemático, donde a partir de una entrada obtenemos un resultado procesado.</p><p>En programación el concepto de <em>función </em>tiene cierta similitud, en cuanto a que tenemos una entrada (parámetros) y obtenemos un resultado (retorno de la función), adicionalmente tenemos que decir que una función es un bloque código, que podemos ejecutar tantas veces necesitemos, de tal forma que hablamos de reutilización de recursos.</p><h3 id="qu-es-una-funci-n-en-javascript">Qué es una función en Javascript</h3><p>Con la premisa anteriormente indicada, hablemos ahora de funciones en Javascript, las cuales serán bloques de código que recibiran parámetros y retornaran un resultado. Así es, tan simple como ello, ahora vemos un ejemplo de código:</p><pre><code>function nombreDeLaFuncion(parametro1, parametro2) {
  // Código que realiza una tarea
  return resultado;
}</code></pre><p>Esa es la anatomía de una función, lleva un nombre (puede que a veces no 😎), recibe un conjunto de parámetros, que buscan generalizar o cubrir la mayor cantidad de comportamientos de acuerdo al proceso a implementar y al final retorna un resultado, el cual pudiera ser simplemente un return sin ningun resultado.</p><h3 id="que-tan-complejo-puede-ser-el-cuerpo-de-la-funci-n">Que tan complejo puede ser el cuerpo de la función?</h3><p>Desarrollemos siempre de forma tal que sea simple el cuerpo, si este es complejo se puede dividir en otras funciones que serán <em>llamadas</em> o <em>invocadas</em> desde una función principal.</p><p>De lo anterior, debemos diferenciar 2 momentos en la vida de una función:</p><ul><li>Declaración: es cuando definimos su código: parámetros, cuerpo y resultado.</li><li>Ejecución, llamada o invocación: se refiere al caso en que dentro de nuestro código en otro punto, colocamos su nombre y le pedimos al interprete de Javascript que realice la ejecución de su código.</li></ul><pre><code>//Declaración o definición
function nombreDeLaFuncion(parametro1, parametro2) {
  // Código que realiza una tarea
  return resultado;
}

//Ejecución
nombreDeLaFuncion(valor1, valor2);</code></pre><h3 id="cu-l-es-la-importancia-de-las-funciones">Cuál es la importancia de las funciones?</h3><p>Cuando desarrollamos, en general debemos buscar ser óptimos, es decir que podamos delegar el trabajo al computador, es allí donde las funciones cobran importancia, por ser ese bloque de código que podemos llamar <em>n</em> veces con diferentes argumentos y la función ejecutará el proceso retornando ese resultado.</p><p>Para organizar el código siempre es importante aplicar el concepto de: <em>divide y venceras,</em> las funciones nos permite organizar el código de tal forma que podamos delegar a ella una tarea, en caso de ser necesario algún ajuste o mantenimiento, el cambio será realizado sólo en el cuerpo de la función.</p><p>La escalabilidad, al tener organizado nuestro código, es posible hacer crecer las caracteristicas de nuestra aplicación, será menos complicado y más factible poder extender las funcionalidades, por ello usar funciones es fundamental, no sólo en Javascript, la realidad es que se deb usar este concepto en general en el desarrollo de software.</p><h3 id="retornos-de-las-funciones">Retornos de las funciones</h3><p>Un aspecto bien interesante de las funciones, es que además de realizar un proceso, son capaces de retornar un resultado, este resultado puede ser tan simple como: verdadero, un número, una cadena de caracteres o pudiese ser tan complejo como un arreglo o lista, un objeto, una lista de objetos e incluso una función (Las llamadas funciones de orden superior).</p><p>Al ser tan flexible la gestión de funciones, se hace un recurso super interesante de aprender y de usar en nuestros desarrollos de forma constante.</p><h3 id="como-declaramos-una-funci-n-en-javascript">Como declaramos una función en Javascript?</h3><p>Existen múltiples formas declarar funciones y cada una tiene sus particularidades, veamos cuales son:</p><h3 id="declaraci-n-de-funci-n">Declaración de función</h3><p>Es la forma más simple y tradicional de declarar o definir una función, su anatomía sigue a continuación:</p><pre><code>function &lt;nombreFuncion&gt; (&lt;parametro1&gt;,&lt;parametro2&gt;,...) {
  ...Bloque de código

  return &lt;resultado&gt;;
}</code></pre><h3 id="expresi-n-de-funci-n">Expresión de función</h3><p>En Javascript tenemos la particularidad de que podemos tratar las funciones como una variable cualquiera, entonces tomando esta afirmación, veamos como podemos expresar una función:</p><pre><code>//Observa que se declara similar a una variable
const &lt;nombreFuncion&gt; = function(&lt;parametro1&gt;,&lt;parametro2&gt;,...) {
  return &lt;resultado&gt;;
}</code></pre><h3 id="funci-n-de-flecha-o-arrow-function">Función de flecha o arrow function</h3><p>Similar al caso anterior, una función de flecha se declara como una expresión de función, pero con la particularidad de no tener que usar la palabra reservada <em>function, </em>además de tener un par de características adicionales:</p><ul><li>Si es posible realizar el proceso en una sola línea, no es necesario usar { } ni la palabra reservada <em>return</em>.</li><li>No tiene contexto de variables</li></ul><pre><code>//Es similar a una expresión de función
const &lt;nombreFuncion&gt; = (&lt;parametro1&gt;,&lt;parametro2&gt;,...) =&gt; {
  ...
  return &lt;resultado&gt;;
}</code></pre><p>Arrow function en una línea</p><pre><code>//Es similar a una expresión de función
const &lt;nombreFuncion&gt; = (&lt;parametro1&gt;,&lt;parametro2&gt;,...) =&gt; &lt;resultado&gt;;</code></pre><h3 id="iife-immediately-invoked-function-expression-">IIFE (Immediately Invoked Function Expression)</h3><p>Son funciones que no tienen nombre (anónimas) y que se ejecutan al momento de declararse, a diferencia de las anteriores, que separan los procesos de declaración y de ejecución. Su sintaxis sigue la siguiente anatomía:</p><pre><code>( function () {
    let texto = 'Esta es una función de tipo IIFE';
    return `Función con resultado: ${texto}`
})()</code></pre><h3 id="function-constructor">Function constructor</h3><p>Otra particularidad que Javascript nos ofrece es tener un tipo de dato para identificar las funciones, el tipo Function como lo vimos en el texto de <a href="https://medium.com/p/a08b0bfe70f3" rel="noopener noreferrer">Tipos de Datos de Javascript</a>.</p><p>Ahora bien, cómo declaramos una función siguendo esta sintaxis?, veamos:</p><pre><code>const &lt;nombreFuncion&gt; = new Function('parametro1', 'parametro2', ... , 
   'return &lt;Codigo de la funcion&gt;'
);</code></pre><p>Es importante destacar en este tipo de declaración, que tanto los parámetros (los que sean necesarios) y el código de la función se pasan como cadena de caracteres (encerrados entre comillas) y es la clase Function la que a partir de esos datos construye y devuelve una variable de tipo Function</p><p>Ahora que sabemos como declarar funciones, usemos la forma más útil en cada caso según nuestras necesidades!</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/CXiKt9_QdgU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="JavaScript: Guía Completa de Funciones | Declaración, Expresión, Arrow y Autoinvocadas" name="fitvid4"></iframe>
          </div>
        </div>
      </figure><p>En Javascript también podemos hacer gestión de otro tipo de datos super interesante que es el objeto, mapa o diccionario, veamos un poco sobre el.</p><h2 id="objetos-mapas-o-diccionarios-">Objetos, mapas o diccionarios.</h2><p>Como tipo de dato estructurado, un objeto, mapa o diccionario, sigue la siguiente anatomía o estructura:</p><pre><code>const objeto = {
	//Propiedades, de la forma nombre: valor
}</code></pre><p>Con lo anterior, podemos inferir que un objeto se compone de diferentes propiedades, cada una con su valor específico, siendo estas propiedades de cualquier de los tipos de datos definimos (y revisados al inicio del documento) en Javascript, veamos entonces un ejemplo:</p><pre><code>const persona = {
    nombre: 'Leonardo',
    edad: 39,
    profesion: 'Desarrollador de software',
    casado: true
}</code></pre><p>Observa que en la definición anterior, usamos tipos de datos como: cadenas, números y lógicos o booleanos, en este caso nuestro objeto representa a una persona, por lo que la flexibilidad que permite este tipo de dato, es enorme.</p><p>Ahora veamos un ejemplo más complejo:</p><pre><code>const persona = {
    nombre: 'Leonardo',
    edad: 39,
    profesion: 'Desarrollador de software',
    casado: true,
    hijo: {
        nombre: 'Diego',
        edad: 15,
        hobbies: ['Artes marciales','Fútbol','Lectura']
    }
}</code></pre><p>En este ejemplo asociamos un objeto dentro de un objeto, de forma tal que se pueden crear relaciones a lo interno de la definición, dejando la gestión de datos de una forma super eficiente y aún más fléxible. Incluso percibe que agregamos una propiedad de tipo arreglo de cadena de caracteres. </p><p>Y será que podemos hacerlo aún más complejo, veamos este otro ejemplo:</p><pre><code>const persona = {
    nombre: 'Leonardo',
    edad: 39,
    profesion: 'Desarrollador de software',
    casado: true,
    familia: [
        {
            nombre: 'Diego',
            edad: 15,
            hobbies: ['Artes marciales','Fútbol','Lectura'],
            parentesco: 'hijo'
        },
        {
            nombre: 'Isveli',
            edad: 34,
            peliculasFavoritas: ['Una esposa de mentira','Harry Potter'],
            parentesco: 'esposa'
        },
        {
            nombre: 'Julia',
            edad: 64,
            profesion: 'Docente'
        }
    ]   
}</code></pre><p>Observa que ahora tenemos una propiedad de tipo arreglo de objetos 🤪</p><p>Con los objetos podemos gestionar los datos de forma eficiente, flexible y poderosa, es por excelencia el tipo de dato usado, en conjunto con los arreglos para la gestión de datos en nuestras aplicaciones.</p><h3 id="formas-de-acceder-a-las-propiedades-de-un-objeto">Formas de acceder a las propiedades de un objeto</h3><p>Cuando estamos en código, podemos hacer uso de las propiedades de la siguiente forma:</p><pre><code>//Tomando el ejemplo anterior, acceder a la propiedad nombre
console.log(persona.nombre);</code></pre><p>Ahora bien que pasa si por alguna razón tenemos una propiedad con espacios, ejemplo de una declaración de ese tipo:</p><pre><code>const persona = {
    nombre: 'Leonardo',
    'direccion de la persona': 'Planeta tierra',
    edad: 39
}</code></pre><p>Tomando la primera definición de acceso a las propiedades, nos encontrariamos con un &nbsp;problema ya que esto no es válido:</p><pre><code>//No es válido
console.log(persona.direccion de la persona);</code></pre><p>Entonces como resolvemos?, Javascript tiene para todo una solución :), veamos como se debe acceder entonces a este tipo de propiedad:</p><pre><code>//Válido
console.log(persona['direccion de la persona']);</code></pre><p>Lo cual también es valido si usamos el primer ejemplo:</p><pre><code>//Válido
console.log(persona.['nombre']);</code></pre><h3 id="m-todos-para-manejar-objetos-en-javascript">Métodos para manejar objetos en Javascript</h3><p>Existen un par métodos que son muy usados cuando trabajamos con objetos, para acceder a sus propiedades y sus valores, estos son:</p><ul><li><strong>Object.keys(objeto), </strong>pasandole a este método un objeto, el nos devuelve un arreglo con cada una de las propiedades definidas para el objeto. Tomando el último ejemplo, la ejecución de este método nos retorna lo siguiente:</li></ul><pre><code>console.log(Object.keys(persona));
// [ "nombre", "direccion de la persona", "edad", "profesion", "casado", "familia" ]</code></pre><ul><li>Object.values(objeto), en este caso el método funciona similar al anterior, pero retornando los valores de cada propiedad. Incluso, mostrando los arreglos como valores. Siguiendo el mismo ejemplo:</li></ul><pre><code>console.log(Object.values(persona));
//La salida será:
/*
[ "Leonardo", "Planeta tierra", 39, "Desarrollador de software", true, [
    {
      nombre: "Diego",
      edad: 15,
      hobbies: [ "Artes marciales", "Fútbol", "Lectura" ],
      parentesco: "hijo",
    }, {
      nombre: "Isveli",
      edad: 34,
      peliculasFavoritas: [ "Una esposa de mentira", "Harry Potter" ],
      parentesco: "esposa",
    }, {
      nombre: "Julia",
      edad: 64,
      profesion: "Docente",
    }
  ] ]
  */</code></pre><p></p><h3 id="como-agregamos-actualizamos-o-removemos-propiedades-de-un-objeto">Como agregamos, actualizamos o removemos propiedades de un objeto?</h3><ul><li>Para agregar una propiedad a un objeto basta con definirla, de acuerdo a los visto anteriormente:</li></ul><pre><code>const persona = {
	nombre: 'Sirius'
};

//Agregamos una nueva propiedad
persona.genero = 'Gato';
</code></pre><ul><li>Para actualizar, seguimos la línea del ejemplo anterior, es simplemente atribuir un nuevo valor a la propiedad:</li></ul><pre><code>const persona = {
	nombre: 'Sirius'
};

//Agregamos una nueva propiedad
persona.genero = 'Gato';
//Actualizamos las propiedades
persona.nombre = 'Sirius Black';
persona.genero = 'Gato Siames';
</code></pre><ul><li>Para eliminar o remover una propiedad hacemos uso del método <code>delete</code></li></ul><pre><code>const persona = {
	nombre: 'Sirius'
};

//Agregamos una nueva propiedad
persona.genero = 'Gato';
//Removemos la propiedad
delete persona.genero;

console.log(persona.genero); //Resultado undefined
</code></pre><p>Javascript es muy versatil, como vimos al inicio, da para usar en web, backend, desarrollo móvil y hasta desarrollo desktop, así que a colocar las manos en el código que Javascript tiene muchos desafíos interesantes.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Construyendo una API REST con Node + Fastify + Sequelize + PostgreSQL ]]>
                </title>
                <description>
                    <![CDATA[ En las próximas líneas vamos a describir los pasos para realizar la implementación de una API REST que realizará las funciones de la capa de backend y será responsable de la gestión de datos de un sistema de gestiṍn de turnos o tickets.  Si deseas profundizar en cuanto a ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/construyendo-una-api-rest-con-node-fastify-sequelize-postgresql/</link>
                <guid isPermaLink="false">65590ef2bcc2a003e70e2db7</guid>
                
                    <category>
                        <![CDATA[ api rest ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node ]]>
                    </category>
                
                    <category>
                        <![CDATA[ fastify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ sequelize ]]>
                    </category>
                
                    <category>
                        <![CDATA[ postgresql ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Sat, 30 Dec 2023 02:29:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/12/btmJOTjifO9r_2560_1440.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>En las próximas líneas vamos a describir los pasos para realizar la implementación de una API REST que realizará las funciones de la capa de backend y será responsable de la gestión de datos de un sistema de gestiṍn de turnos o tickets. </p><p>Si deseas profundizar en cuanto a como funciona una API REST, sus elementos y particularidades, puedes consultar <a href="https://www.freecodecamp.org/espanol/news/consumiendo-una-api-rest-con-react-js/">este artículo de freecodecamp.org</a>, que describe de forma detallada los conceptos anteriormente mencionados.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/12/image-2.png" class="kg-image" alt="image-2" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/12/image-2.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2023/12/image-2.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/2023/12/image-2.png 1024w" sizes="(min-width: 720px) 720px" width="600" height="400" loading="lazy"></figure><p>Cuando construimos una API REST, debemos identificar que datos para gestionar, en nuestro caso, vamos a tomar como ejemplo un sistema de gestión de tickets, que tendría un modelo de datos similar al que vemos en el siguiente diagrama:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/12/image.png" class="kg-image" alt="image" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/12/image.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2023/12/image.png 659w" width="600" height="400" loading="lazy"><figcaption>Modelo entidad-relación. Sistema de turnos/tickets</figcaption></figure><p>Tomando en cuenta lo indicado, a partir del modelo entidad-relación anterior, es importante identificar los elementos que van a actuar como componentes de nuestra API REST.</p><h2 id="elementos-componentes-de-una-api-rest">Elementos componentes de una API REST</h2><p>Vamos a identificar 3 componentes principales en la construcción de una API REST, estos son: </p><ul><li>Modelos, que serán las representación de las entidades que iremos a gestionar en la API, es la abstracción de los datos del mundo real al mundo digital.</li><li>Controladores, responsables de implementar las operaciones a realizar sobre los modelos, a veces denominada capa de lógica de negocios.</li><li>Rutas, definiciones de como nuestra API puede interactuar con el mundo exterior, es decir a través de las rutas definimos la comunicación entre la implementación que vamos a realizar con los servicios o herramientas que irán a consumir lo que realicemos.</li></ul><p>Para construir esta API vamos a usar NodeJS, que es el entorno de ejecución de javascript para backend. </p><h3 id="definiendo-a-nodejs">Definiendo a NodeJS</h3><p>Según la web oficial de <a href="https://nodejs.org/es/about" rel="noopener ugc nofollow">nodejs.org</a>, la definición de Node es:</p><blockquote><em><em>Ideado como un entorno de ejecución de JavaScript orientado a eventos asíncronos, Node.js está diseñado para crear aplicaciones network escalables.</em></em></blockquote><p>En el siguiente material tendrás algunas orientaciones básicas de como funciona y como realizar la instalación del mismo.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/BA1-NU5edwQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Introducción a Node.js: Guía para principiantes" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p> </p><h3 id="qu-es-fastify">Qué es Fastify</h3><p>Partiendo de los elementos indicados como componentes de una API, vamos a explicar que paquetes vamos a utilizar para la implementacion de cada uno de ellos. Comencemos por el responsable de las rutas o mejor en forma general, por la comunicación y transporte de datos.</p><p>Es a través de este proceso (comunicación y transporte) que nuestra API REST &nbsp;va a recibir y enviar datos a quien esté consumiendo el servicio. Esto se hace a través de rutas.</p><p>Para esta tarea tenemos varias opciones, como usar las clases nativas de NodeJS, usar el framework expressjs o &nbsp;en nuestro caso usar el framework fastify. Te preguntaras por qué Fastify?. Varias razones: simplicidad, rapidez y escabilidad del framework.</p><p>Primero vamos a crear nuestro proyecto node, con la siguiente instrucción:</p><pre><code>npm init</code></pre><p>Ahora avancemos a la instalación de fastify, esta muy simple, sólo ejecutanos el siguiente comando y con ello ya en nuestro proyecto podemos usar sus métodos y funcionalidades, pero comencemos por crear el proyecto:</p><pre><code>npm install fastify</code></pre><p>Ahora procedemos a construir el esqueleto de nuestra API.</p><h2 id="construyendo-los-elementos-de-nuestra-api-rest">Construyendo los elementos de nuestra API REST</h2><p>Empecemos por el archivo principal, el cual será: <code>index.js</code> El contenido del archivo <code>index.js</code> será:</p><pre><code>import Fastify from 'fastify';

const fastify = Fastify({
    logger: true
});

// Ruta inicial
fastify.get('/', async function handler (req, res) {
    return { hello: 'world' };
});
  
// Ejecución de la aplicación
try {
    await fastify.listen({ port: 3000 });
} catch (err) {
    fastify.log.error(err);
    process.exit(1);
}</code></pre><p>Con lo anterior, ya tenemos en pocas líneas, una API funcional, que solo tiene una ruta, pero que en esencia está completa. Ahora pasemos a la extensión de sus rutas.</p><h3 id="definiendo-las-rutas-b-sicas">Definiendo las rutas básicas</h3><p>Procedemos entonces a definir las rutas que nuestra API va a implementar, en este caso creamos un nuevo archivo llamado <code>routes.js</code>, en el cual definimos los CRUDs de cada uno de nuestros modelos, siendo que CRUD será (C-reate, R-ead, U-pdate, D-elete), en español serán las operaciones de Inserción, Lectura, Actualización y Borrado.</p><p>El archivo quedará de esta forma: </p><pre><code>const rutas = [
    {
        method: "POST",
        url: "/usuarios",
        handler: async (req, res) =&gt; {
            res.status(200).send({status: 'OK - POST'});
        }
    },
    {
        method: "GET",
        url: "/usuarios",
        handler: async (req, res) =&gt; {
            res.status(200).send({status: 'OK - GET'});
        }
    },
    {
        method: "PUT",
        url: "/usuarios/:id",
        handler: async (req, res) =&gt; {
            res.status(200).send({status: 'OK - PUT'});
        }
    },
    {
        method: "DELETE",
        url: "/usuarios/:id",
        handler: async (req, res) =&gt; {
            res.status(200).send({status: 'OK - DELETE'});
        }
    }
]

export default rutas;</code></pre><p>Observa que algunas rutas tienen <code>:/id</code>, esto lo explicaremos en detalle más adelante. </p><p>Con las rutas ya definidas, el siguiente paso será definir los modelos, notese que en el archivo anterior, dejamos la propiedad <code>handler</code> apuntando a una función básica que siempre retorna OK.</p><pre><code>handler: (req,res) =&gt; {
	res.status(200).send('OK');
}</code></pre><p>Esto significa que por ahora no tenemos ningún proceso asociado a las rutas, pero hemos dejado ya la estructura hecha. La implementación será realizada más adelante.</p><p>Incorporemos las rutas a nuestro archivo principal <code>index.js</code>, quedando este de la siguiente forma:</p><pre><code>import Fastify from 'fastify';
import rutas from './rutas.js';
import cors from '@fastify/cors';

const fastify = Fastify({logger:true});
await fastify.register(cors, {

});

//POST - Inserciones                C-REATE
//GET - Consulta                    R-EAD
//PUT, PATCH - Actualizaciones      U-PDATE
//DELETE - Borrado                  D-ELETE

fastify.get("/",async function(req, res) {
    return { hello: 'world'};
});

//Incorporación de las rutas
rutas.forEach((ruta) =&gt; {
    fastify.route(ruta);
});

try {
    fastify.listen({port:3500});
} catch(erro) {
    console.log(erro);
}
</code></pre><p>En el siguiente material podrás visualizar el paso de la construcción que hemos hecho hasta ahora.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/DuUMepfA0Z8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="API REST con NodeJS + Fastify: Crea tu primera API RESTful desde cero" name="fitvid1"></iframe>
          </div>
        </div>
      </figure><h2 id="modelos">Modelos</h2><p>Acá vamos a definir los datos que nuestra API procesará, tomemos la primera entidad "Usuarios" y definamos su modelo, para ello nos vamos a apoyar en <code>sequelize</code>, que nos permite hacer una definición que luego se reflejará en la base de datos, esto debido a que Sequelize actua como un ORM (Object Relation Mapping).</p><h3 id="pero-qu-es-un-orm-object-relation-mapping">Pero qué es un ORM - Object Relation Mapping?</h3><p>Tomando lo que <a href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping">https://en.wikipedia.com</a> nos indica, podemos citar lo que es un ORM:</p><blockquote>Un ORM (Object-Relational Mapping) es una técnica de programación que permite la conversión de datos entre una base de datos relacional y un lenguaje de programación orientado a objetos. En otras palabras, un ORM es una herramienta que ayuda a simplificar la traducción entre los dos paradigmas: objetos y tablas de bases de datos relacionales. Puede utilizar definiciones de clases (modelos) para crear, mantener y proporcionar acceso completo a los datos de los objetos y su persistencia en la base de datos.</blockquote><h3 id="instalando-sequelizer">Instalando Sequelizer</h3><p>Para realizar la instalación de Sequelizer, necesitamos ejecutar sólo este comnpm install sequelizerando:</p><pre><code>npm install sequelizer</code></pre><p>Ejecutando lo anterior, tenemos ya disponible el paquete en nuestro proyecto, es momento de conectar nuestra base de datos, para ello creamos un nuevo archivo llamado <code>db.js</code> y colocamos los parámetros de conexión correspondientes:</p><pre><code class="language-Javascript">/* eslint-disable require-jsdoc */
import Sequelize from 'sequelize';

/*
Clase de conexión a la base de datos
*/
class DBInstance {
	constructor() {
		const dbCfg = {
			user: '&lt;usuario_base_datos&gt;',
			host: '&lt;ip_o_dns_servidor&gt;',
			database: '&lt;nombre_base_datos&gt;',
			password: '&lt;clave_usuario_base_datos&gt;',
			port: 5432,
		};
		this.sequelize = new Sequelize(dbCfg.database, dbCfg.user, dbCfg.password, {
			host: dbCfg.host,
			dialect: 'postgres',
			logging: false,
		});
	}
}

export default new DBInstance().sequelize;
</code></pre><p>Con lo anterior, ya tenemos conexión a nuestra base de datos. Ajustamos nuestro archivo <code>index.js</code> para importar este proceso de conexión y dejarlo ejecutandose cuando sea iniciada la API. Quedando su código de esta forma:</p><pre><code>import Fastify from 'fastify';
import db from './db.js';
import rutas from './rutas.js';
import cors from '@fastify/cors';

const fastify = Fastify({logger:true});
await fastify.register(cors, {

});

//POST - Inserciones                C-REATE
//GET - Consulta                    R-EAD
//PUT, PATCH - Actualizaciones      U-PDATE
//DELETE - Borrado                  D-ELETE

// eslint-disable-next-line no-unused-vars
fastify.get('/',async function(req, res) {
	return { hello: 'world'};
});

//Incorporación de las rutas
rutas.forEach((ruta) =&gt; {
	fastify.route(ruta);
});

async function database() {
	try {
		await db.sync();
		console.log('Conectado a la base de datos');
	} catch(e) {
		console.log(e);
	}
}

try {
	fastify.listen({port:3500});
	database();
} catch(erro) {
	console.log(erro);
}</code></pre><p>Observa que hemos creado una función asincrona llamada <code>database()</code> y luego al iniciar la API, la invocamos para que se realice la conexión y sincronización del modelo.</p><p>Ahora procedemos a crear el modelo, recordando que un modelo es la representación de los datos que vamos a gestionar del mundo real en nuestra API. </p><p>Comencemos por el modelo de Usuarios:</p><pre><code>import sequelize from "sequelizer";

const User = sequelize.define("User", {
    id : {
        type: sequelize.STRING,
        allowNull: false,
        primaryKey: true,
    },
    fullname: {
        type: sequelize.STRING,
        allowNull: false,
    },
    password: {
        type: sequelize.STRING,
        allowNull: false,
    },
});

export default User;</code></pre><p>Observa que cada propiedad del objeto que define el modelo, corresponde con el nombre del campo que definimos en nuestro modelo de entidad-relación al inicio del documento, el nombre de cada propiedad representa cada campo, así que debemos tener cuidado con el nombre que le indiquemos, mi recomendación usar letras minusculas, no usar caracteres especiales.</p><p>En el caso de password, vamos usar un paquete adicional, para poder encriptar el contenido de la misma, recordando que por seguridad, los datos de claves no se deben guardar de forma plana o legible en la base de datos.</p><p>Para lograr que nuestro password quede guardado de forma segura, hacemos uso del paquete <code>bcrypt</code>, el cual nos permite codificar el valor enviado por el usuario y luego con un método propio del paquete podemos validar si clave es la guardada, de esta forma el modelo de Usuarios queda implementado con el siguiente código:</p><figure class="kg-card kg-code-card"><pre><code>import sequelize from 'sequelize';
import bcrypt from 'bcrypt';
import db from '../db.js';


const UserModel = db.define('users', {
  id: {
    type: sequelize.STRING,
    allowNull: false,
    primaryKey: true,
  },
  fullname: {
    type: sequelize.STRING,
    allowNull: false,
  },
  password: {
    type: sequelize.STRING,
    allowNull: false,
  },
}, {
  hooks: {
    beforeCreate: (user) =&gt; {
      const salt = bcrypt.genSaltSync();
      user.password = bcrypt.hashSync(user.password, salt);
    },
  },
});

UserModel.prototype.validPassword = function(password) {
  return bcrypt.compareSync(password, this.password);
};

export default UserModel;</code></pre><figcaption>Modelo de Usuarios</figcaption></figure><p>Para el caso del modelo de tiendas o comercios, siguiendo lo definido anteriormente, el modelo queda representado con el siguiente código:</p><figure class="kg-card kg-code-card"><pre><code>import sequelize from 'sequelize';
import db from '../db.js';

const StoreModel = db.define('stores', {
  id: {
    type: sequelize.INTEGER,
    autoIncrement: true,
    allowNull: false,
    primaryKey: true,
  },
  store_name: {
    type: sequelize.STRING,
    allowNull: false,
  },
  category_store: {
    type: sequelize.STRING,
    allowNull: false,
  },
});


export default StoreModel;</code></pre><figcaption>Modelo de Tiendas/Comercios</figcaption></figure><p>Algunas diferencias notorias, en el caso de id, usamos una propiedad dentro del objeto <code>id</code> que es <code>autoIncrement</code>, la cual define que el campo en la base de datos será del valor automático e incremental, de esta forma la base de datos asignará el id correspondiente. Otro punto interesante es el tipo de datos, en el caso de <code>id</code> usamos el tipo <code>INTEGER</code>, para definir que es un número entero.</p><p>Pasemos ahora al modelo de Tickets o Turnos, hacemos la implementación, recordando que este modelo se relaciona con Usuarios y Tiendas, de forma que un <em>Usuario </em>tiene N <em>Tickets </em>y una Tienda también tiene N <em>Tickets,</em> de esta forma el modelo implementado será: </p><figure class="kg-card kg-code-card"><pre><code>import sequelize from 'sequelize';
import db from '../db.js';
import UserModel from './Users.js';
import StoreModel from './Stores.js';

const TicketModel = db.define('tickets', {
  id: {
    type: sequelize.STRING,
    allowNull: false,
    primaryKey: true,
  },
  date_time: {
    type: 'TIMESTAMP',
    allowNull: false,
  },
  status: {
    // eslint-disable-next-line new-cap
    type: sequelize.ENUM(['Creado', 'Confirmado', 'Atendido', 'Cancelado',
      'En progreso']),
    allowNull: false,
  },
  observation: {
    type: sequelize.TEXT,
    allowNull: false,
  },
  end_date_time: {
    type: 'TIMESTAMP',
    allowNull: false,
  },
});

UserModel.hasMany(TicketModel, {
  foreignKey: {
    name: 'user_id',
    type: sequelize.STRING,
    allowNull: false,
  },
});

StoreModel.hasMany(TicketModel, {
  foreignKey: {
    name: 'store_id',
    type: sequelize.INTEGER,
    allowNull: false,
  },
});


export default TicketModel;
</code></pre><figcaption>Modelo de Turnos o Tickets</figcaption></figure><p>En este modelo tenemos algunas particularidades que valen la pena analizar:</p><ul><li>Tenemos nuevos tipos de datos: <code>TEXT</code>para almacenar grandes cantidad de datos (mayores a 255 caracteres), <code>ENUM</code> para definir un conjunto de valores cerrados para un campo en particular. <code>TIMESTAMP</code> para definir campos de tipo fecha/hora.</li><li>Relaciones, en este modelo le decimos a <code>sequelize</code> que queremos relacionar los modelos en este segmento de código:</li></ul><pre><code>UserModel.hasMany(TicketModel, {
  foreignKey: {
    name: 'user_id',
    type: sequelize.STRING,
    allowNull: false,
  },
});

StoreModel.hasMany(TicketModel, {
  foreignKey: {
    name: 'store_id',
    type: sequelize.INTEGER,
    allowNull: false,
  },
});</code></pre><p>Observa que se indica que el modelo Usuarios (<code>UserModel</code>) tiene muchos Tickets con la instrucción: <code>hasMany</code>, lo mismo sucede en el caso de Tiendas (<code>StoreModel</code>), le indicamos que va a tener múlples Tickets.</p><p>Con ellos hemos finalizado los modelos a implementar, ahora pasemos a la lógica de negocios.</p><h2 id="controladores">Controladores</h2><p>Estos elementos tan importantes a la hora de construir nuestra API REST, son los responsables de implementar lo que denominamos la lógica de negocios, es decir ellos reciben los datos enviados por quienes consumen el servicio o la API, los procesan, envian a la capa de datos cuando corresponde y retornan una respuesta. </p><p>De acuerdo con lo anterior, entonces en los controladores donde vamos a implementar el famoso CRUD (Create, Read, Update and Delete), el cual ya citamos anteriormente. Ahora bien, definamos la anatomía de un controlador:</p><pre><code>import Model from "../models/Model.js";

class ModeloController {
    constructor() {

    }
    
    //Creación de registros
    async create (req, res)  {
    }
    
    //Consulta de todos los registros del modelo
    async getAll(req, res) {
    }

	//Consulta de un registro en particular
    async getOne(req, res) {
    }
    
    //Actualización de un registro
    async update(req, res) {
    }
    
    
    //Borrado de un registro
    async delete(req, res) {
    }
    
 }
    </code></pre><p>Notemos que el controlador implementar como comentarmos anteriormente cada una de las operaciones del CRUD, que la API va a realizar para cada uno de los modelos construidos, de esta forma, vamos a tener que crear un controlador para usuarios, otro para tiendas y uno más para los tickets o turnos.</p><p>En este material realizamos la implementación de los modelos y parte de los controladores:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/pn7Vyj3w2wc?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Creando capas de modelos y controladores en una API REST con NodeJS, Fastify, PostgreSQL y Sequelize" name="fitvid2"></iframe>
          </div>
        </div>
      </figure><p>Ahora para cada modelo que hemos definido en nuestra API, construimos sus controladores, quedando su código de esta forma:</p><ul><li>UsersController</li></ul><pre><code>/* eslint-disable require-jsdoc */
import UserModel from '../models/Users.js';

class UsersController {
  constructor() {

  }

  async create(req, res) {
    try {
      const userModel = await UserModel.create(req.body);

      if (userModel) {
        res.status(200).send({status: true, id: userModel.id});
      }
    } catch (e) {
      res.status(500).send({error: e});
    }
  }

  async getAll(req, res) {
    try {
      const where = {...req.query};
      const usuarios = await UserModel.findAll({where});
      res.status(201).send(usuarios);
    } catch (err) {
      res.status(500).send(
          {message: err.message || 'Error al consultar usuarios'},
      );
    }
  }

  async getOne(req, res) {
    try {
      const {id} = req.params;

      const userModel = await UserModel.findByPk(id);

      if (userModel) {
        res.status(201).send(userModel);
      } else {
        res.status(404).send(
            {message: 'Registro no encontrado'},
        );
      }
    } catch (err) {
      res.status(500).send(
          {message: err.message || 'Error al consultar el usuario'},
      );
    }
  }

  async update(req, res) {
    try {
      const {id} = req.params;

      const userModel = await UserModel.update(req.body,
          {where: {id}});

      if (typeof (userModel[0]) !== 'undefined' &amp;&amp; userModel[0] === 1) {
        res.status(201).send({
          status: true,
        });
      } else {
        res.status(404).send(
            {message: 'Registro no encontrado'},
        );
      }
    } catch (err) {
      res.status(500).send(
          {message: err.message || 'Error al actualizar el usuario'},
      );
    }
  }

  async delete(req, res) {
    try {
      const {id} = req.params;

      const userModel = await UserModel.destroy({where: {id}});

      if (userModel) {
        res.status(201).send({
          status: true,
        });
      } else {
        res.status(404).send(
            {message: 'Registro no encontrado'},
        );
      }
    } catch (err) {
      res.status(500).send(
          {message: err.message || 'Error al intentar borrar un usuario'},
      );
    }
  }
}

export default new UsersController();
</code></pre><p>Del código anterior podemos resaltar algunas caracteristicas:</p><ul><li>Todos los métodos son asincronos, es decir por ser operaciones de base de datos, estos procesos deben esperar resultados y recordamos que Javascript <em>no espera a nadie </em>por lo tanto usamos las palabras reservadas <code>async</code> e <code>await</code> para lograr ese objetivo.</li><li>Encerramos el procesamiento o la lógica de cada operación entre las palabras <code>try</code> y <code>catch</code> de forma tal que cualquier error, lo podamos capturar y retornar a quien nos consume los servicios.</li><li>En cada uno de los métodos de nuestros controladores, nos vamos a apoyar en métodos de <em>Sequelize</em>, que nos van a facilitar el acceso y la gestión de los datos a nivel de base de datos. En la creación usamos <code>create</code>, en la búsqueda usamos <code>findAll</code>, para conseguir un registro en particular usamos <code>findByPk</code>, para actualizar usamos <code>update</code> y para borrar usamos <code>destroy</code>.</li><li>En las operaciones que envuelven acceso a un registro en particular usamos del objeto <code>req</code> la propiedad <code>params</code>, es decir usamos <code>req.params</code> para obtener el id del registro. Este parámetro lo definimos en la ruta.</li><li>Cuando hacemos operaciones de envio de datos, sea en la creación o en la actualización, el cuerpo o data la recibimos en lo que se denomina el <code>body</code> de la requisición, esto se obtiene con <code>req.body</code>.</li></ul><p>Veamos como ha quedado el controlador de tiendas:</p><pre><code>import StoreModel from '../models/Stores.js';


/* eslint-disable require-jsdoc */
class StoreController {
  constructor() {

  }

  async create(req, res) {
    try {
      const storeModel = await StoreModel.create(req.body);

      if (storeModel) {
        res.status(201).send({status: true, id: storeModel.id});
      }
    } catch (e) {
      res.status(500).send(e);
    }
  }

  async getAll(req, res) {
    try {
      const where = req.query;

      const tiendas = await StoreModel.findAll({where});
      res.status(201).send(tiendas);
    } catch (err) {
      res.status(500).send(
          {message: err.message || 'Error al consultar tiendas'},
      );
    }
  }

  async getOne(req, res) {
    try {
      const {id} = req.params;

      const storeModel = await StoreModel.findByPk(id);
      if (storeModel) {
        res.status(201).send(storeModel);
      } else {
        res.status(404).send(
            {message: 'Registro no encontrado'},
        );
      }
    } catch (err) {
      res.status(500).send(
          {message: err.message || 'Error al consultar la tienda'},
      );
    }
  }

  async update(req, res) {
    try {
      const {id} = req.params;
      const data = {...req.body};

      delete data.id;

      const storeModel = await StoreModel.update(data,
          {where: {id}});

      if (typeof (storeModel[0]) !== 'undefined' &amp;&amp; storeModel[0] === 1) {
        res.status(201).send({
          status: true,
        });
      } else {
        res.status(404).send(
            {message: 'Registro no encontrado'},
        );
      }
    } catch (err) {
      res.status(500).send(
          {message: err.message || 'Error al actualizar la tienda'},
      );
    }
  }

  async delete(req, res) {
    try {
      const {id} = req.params;

      const storeModel = await StoreModel.destroy({where: {id}});

      if (storeModel) {
        res.status(201).send({
          status: true,
        });
      } else {
        res.status(404).send(
            {message: 'Registro no encontrado'},
        );
      }
    } catch (err) {
      res.status(500).send(
          {message: err.message || 'Error al intentar borrar una tienda'},
      );
    }
  }
}

export default new StoreController();</code></pre><p>El controlador es bien similar al de usuarios, implementado en la clase <code>UserController</code>, solo en el método de actualización (<code>update</code>), agregamos está linea: <code>delete data.id;</code> para evitar que sea modificar el id interno del registro.</p><p>El controlador de Turnos, sigue a continuación, este controlador también tiene la protección para evitar que el campo autonumérico sea modificado en el método de actualización (<code>update</code>). </p><pre><code>import TicketModel from '../models/Tickets.js';

/* eslint-disable require-jsdoc */
class TicketController {
	constructor() {

	}

	async create(req, res) {
		try {
			const ticketModel = await TicketModel.create(req.body);

			if (ticketModel) {
				res.status(201).send({status: true, id: ticketModel.id});
			}
		} catch (e) {
			res.status(500).send(e);
		}
	}

	async getAll(req, res) {
		try {
			const where = req.query;

			const tickets = await TicketModel.findAll({where});
			res.status(201).send(tickets);
		} catch (err) {
			res.status(500).send(
				{message: err.message || 'Error al consultar tickets'},
			);
		}
	}

	async getOne(req, res) {
		try {
			const {id} = req.params;

			const ticketModel = await TicketModel.findByPk(id);
			if (ticketModel) {
				res.status(201).send(ticketModel);
			} else {
				res.status(404).send(
					{message: 'Registro no encontrado'},
				);
			}
		} catch (err) {
			res.status(500).send(
				{message: err.message || 'Error al consultar el ticket'},
			);
		}
	}

	async update(req, res) {
		try {
			const {id} = req.params;
			const data = {...req.body};
			delete data.id;

			const ticketModel = await TicketModel.update(data,
				{where: {id}});

			if (typeof (ticketModel[0]) !== 'undefined' &amp;&amp; ticketModel[0] === 1) {
				res.status(201).send({
					status: true,
				});
			} else {
				res.status(404).send(
					{message: 'Registro no encontrado'},
				);
			}
		} catch (err) {
			res.status(500).send(
				{message: err.message || 'Error al actualizar el ticket'},
			);
		}
	}

	async delete(req, res) {
		try {
			const {id} = req.params;

			const ticketModel = await TicketModel.destroy({where: {id}});

			if (ticketModel) {
				res.status(201).send({
					status: true,
				});
			} else {
				res.status(404).send(
					{message: 'Registro no encontrado'},
				);
			}
		} catch (err) {
			res.status(500).send(
				{message: err.message || 'Error al intentar borrar un ticket'},
			);
		}
	}
}

export default new TicketController();
</code></pre><p>Con esto hemos finalizado los controladores.</p><h2 id="rutas-actualizadas">Rutas actualizadas</h2><p>Ahora es momento de revisar como llamamos todo lo implementado en nuestras rutas, recordando que al inicio generamos las rutas para el módelo de usuarios, pero llamando a métodos genéricos en lugar de nuestros controladores, con ellos implementados, podemos ajustar el código, el cual queda de la siguiente forma:</p><pre><code>import UsersController from './controllers/Users.js';
import StoreController from './controllers/Stores.js';
import TicketController from './controllers/Tickets.js';

const rutas = [
	{
		method: 'POST',
		url: '/usuarios',
		handler: UsersController.create,
	},
	{
		method: 'GET',
		url: '/usuarios',
		handler: UsersController.getAll,
	},
	{
		method: 'GET',
		url: '/usuarios/:id',
		handler: UsersController.getOne,
	},
	{
		method: 'PUT',
		url: '/usuarios/:id',
		handler: UsersController.update,
	},
	{
		method: 'DELETE',
		url: '/usuarios/:id',
		handler: UsersController.delete,
	},

	/* Rutas de tiendas */
	{
		method: 'POST',
		url: '/tiendas',
		handler: StoreController.create,
	},
	{
		method: 'GET',
		url: '/tiendas',
		handler: StoreController.getAll,
	},
	{
		method: 'GET',
		url: '/tiendas/:id',
		handler: StoreController.getOne,
	},
	{
		method: 'PUT',
		url: '/tiendas/:id',
		handler: StoreController.update,
	},
	{
		method: 'DELETE',
		url: '/tiendas/:id',
		handler: StoreController.delete,
	},

	/* Rutas de turnos */
	{
		method: 'POST',
		url: '/turnos',
		handler: TicketController.create,
	},
	{
		method: 'GET',
		url: '/turnos',
		handler: TicketController.getAll,
	},
	{
		method: 'GET',
		url: '/turnos/:id',
		handler: TicketController.getOne,
	},
	{
		method: 'PUT',
		url: '/turnos/:id',
		handler: TicketController.update,
	},
	{
		method: 'DELETE',
		url: '/turnos/:id',
		handler: TicketController.delete,
	},
];

export default rutas;</code></pre><p>Interesante que nuestro archivo raiz <code>index.js</code> no sufre ninguna alteración, esto debido al grado de encapsulamiento y desacoplamiento que hemos implementado.</p><p>Con esto ya tenemos una API Rest funcional, implementada con NodeJS, Fastify, Sequelize y PostgreSQL.</p><p>Puedes verificar la implementación de los controladores y de todos sus métodos en este material:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/vvlWMU7mbiA?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Construyendo una API RESTful con Node, Fastify, Sequelize y Postgres: Parte 3 - Finalizando el CRUD" name="fitvid3"></iframe>
          </div>
        </div>
      </figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Consultando bases de datos relacionales: Funciones de Agregado en SQL: Cómo Usar GROUP BY con COUNT, SUM, AVG, MAX, MIN y Más ]]>
                </title>
                <description>
                    <![CDATA[ El mundo de las bases de datos relacionales o bases de datos SQL es maravilloso, sólo que a veces nos puede resultar complejo, por no tener conocimiento de todas las herramientas que tenemos a disposición. Hagamos un repaso rápido de que es una base de datos y sus tipos. Una ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/funciones-de-agregado-en-sql-como-usar-sum-avg-max-min-y-mas/</link>
                <guid isPermaLink="false">6551fad4bcc2a003e70e2406</guid>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Fri, 24 Nov 2023 01:26:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/11/A5K5Id6BHKIj_2560_1440.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>El mundo de las bases de datos relacionales o bases de datos SQL es maravilloso, sólo que a veces nos puede resultar complejo, por no tener conocimiento de todas las herramientas que tenemos a disposición.</p><p>Hagamos un repaso rápido de que es una base de datos y sus tipos. Una base de datos es una herramienta que nos permite almacenar datos de forma permanente. Existen 2 tipos de bases de datos que difieren en su forma de almacenar y recuperar los datos pero cumplen el objetivo anteriormente descrito, veamos un poco en detalle esto.</p><h2 id="tipos-de-bases-de-datos-">Tipos de bases de datos:</h2><ul><li><em>Relacionales o SQL</em>. Los datos se almacenan en tablas y campos, donde una tabla puede tener múltiples campos y estos campos almacenan un dato, que puede ser cadena de caracteres, textos (cadenas con más de 255 caracteres), números (enteros y decimales), fecha, fecha y hora, datos binarios. Cada herramientas de gestión de base de datos SQL puede tener algunos tipos de campos adicionales. </li><li><em>NoSQL o no solo SQL.</em> Los datos se van a almacenar de acuerdo a como la herramienta lo implemente, siendo las más usadas: colecciones y documentos, par registros de la forma llave-valor, columnas anchas (donde se generan tablas y campos pero con la ventaja de cada registro puede tener diferentes campos), grafos. </li></ul><p>De acuerdo la necesidad que se tenga, se selecciona el tipo de base de datos a utilizar. Nuestro foco en este artículo es el de las bases de datos relacionales o SQL. Para ello vamos a partir de un ejemplo que tenemos pre-construido, donde la base de datos posee 3 tablas, relacionadas entre sí, de acuerdo con el siguiente módelo: entidad-relación (es el modelo usado por excelencia para representar bases de datos SQL):</p><h3 id="base-de-datos-de-ejemplo-">Base de datos de ejemplo:</h3><p>En nuestro caso partimos de una base de datos que simula un sistema de gestión de tickets o turnos, que está compuesto de 3 tablas, que representan las 3 entidades envueltas:</p><ul><li><em>users</em>, es la tabla que nos permite representar los usuarios o clientes que van a solicitar turnos o tickets.</li><li><em>stores</em>, almacena los datos de las tiendas o comercios que ofrecen los servicios y que desean tener control de los turnos o tickets de sus clientes.</li><li><em>tickets</em>, tabla que almacena los turnos o tickets de los clientes en las tiendas o comercios.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-2.png" class="kg-image" alt="image-2" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/11/image-2.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-2.png 659w" width="659" height="390" loading="lazy"><figcaption>Modelo entidad-relación de una base de datos de ejemplo</figcaption></figure><p>De acuerdo al modelo anterior, podemos entender que un usuario (registro en la tabla users) puede tener n turnos, en este caso hablamos de una relación <em>1 -&gt; N</em>, luego podemos observar que una tienda (registro en la tabla stores) va a disponer o habilitar n turnos, entonces la relación será de la misma forma: <em>1 -&gt; N</em>. Luego es posible observar que un usuario va a n turnos en n tiendas, por lo tanto la relación de usuarios con tiendas será <em>N -&gt; M.</em> </p><p>Puedes obtener la base de datos de ejemplo para práctica en el siguiente link:<br><a href="https://github.com/ljcl79/practica-joins">https://github.com/ljcl79/practica-joins</a></p><h2 id="como-realizar-consultas-sobre-una-base-de-datos-sql"><strong>Como realizar consultas sobre una base de datos SQL</strong></h2><p>Para realizar una consulta sobre una base de datos SQL, recordemos que usamos el comando <code>SELECT</code>, el cual nos retorna los campos de los registros que cumplan la condición de búsqueda. La anatomía de un <code>SELECT</code> sigue a continuación:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-3.png" class="kg-image" alt="image-3" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/11/image-3.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-3.png 903w" sizes="(min-width: 720px) 720px" width="903" height="285" loading="lazy"></figure><p>En este video podrás encontrar un mejor detalle de como realizar consultas básicas, hacemos uso de la misma base de datos del documento actual.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/oRiZ5OHCjx8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Dominando SQL: Cómo usar SELECT en 1, 2 y 3 tablas con filtros WHERE" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p>Con el contexto anterior, podemos avanzar un nivel en cuanto a consultas de base de datos SQL, es el uso de JOINs. Los JOINs nos permites relacionar tablas por campos, que generalmente corresponden a campos que identifican registros en la tabla principal y referencia a un campo en la tabla destino. </p><p>Por ejemplo en el modelo que estamos usando como ejemplo, tenemos las relaciones indicadas anteriormente:</p><ul><li>Tabla <code>users</code> por el campo <code>id</code> está relacionado con la tabla <code>tickets</code> por el campo <code>user_id</code>.</li><li>Tabla <code>stores</code> por el campo <code>id</code> está relacionado con la tabla <code>tickets</code> por el campo <code>store_id</code></li></ul><p>Ahora pensemos en como hacer una consulta que relacione las tablas, ejemplo, saber que usuarios tienen turnos, ya sea realizados o agendados. Para ello podemos aplicar el uso de JOIN, en este caso veamos la siguiente figura:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-4.png" class="kg-image" alt="image-4" width="426" height="247" loading="lazy"><figcaption>JOINs visualizado a partir de la herramienta: https://sql-joins.leopard.in.ua/</figcaption></figure><p>En el caso anterior la tabla A será la tabla <code>users</code> y la tabla B será la tabla <code>tickets</code>, la consulta a ejecutar para obtener que usuarios tienen turnos, como indicamos anteriormente será:</p><pre><code class="language-SQL">SELECT * FROM users A INNER JOIN tickets B ON A.id = B.user_id;</code></pre><p>Observando la gráfica podemos obtener los distintos tipos de JOINs que SQL permite realizar:</p><p><em>INNER JOIN</em></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-4.png" class="kg-image" alt="image-4" width="426" height="247" loading="lazy"><figcaption>Tomado de: https://sql-joins.leopard.in.ua/</figcaption></figure><p><em>LEFT JOIN</em></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-5.png" class="kg-image" alt="image-5" width="426" height="247" loading="lazy"><figcaption>Tomado de: https://sql-joins.leopard.in.ua/</figcaption></figure><p><em>RIGHT JOIN</em></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-6.png" class="kg-image" alt="image-6" width="426" height="247" loading="lazy"><figcaption>Tomado de: https://sql-joins.leopard.in.ua/</figcaption></figure><p><em>FULL JOIN</em></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-7.png" class="kg-image" alt="image-7" width="426" height="247" loading="lazy"></figure><p>Para profundizar sobre el tema de JOINs puedes acceder a este artículo de freeCodeCamp.org en español: <a href="https://www.freecodecamp.org/espanol/news/tutorial-de-uniones-en-sql/">https://www.freecodecamp.org/espanol/news/tutorial-de-uniones-en-sql/</a>.</p><p>De igual forma, en el siguiente video podrás entrar en detalle de como realizar consultas usando JOINs.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/6wWjiWjb2dU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="🎯 Domina los JOINs en SQL: Práctica con un Sistema de Turnos | Tutorial Completo 🎯" name="fitvid1"></iframe>
          </div>
        </div>
      </figure><h2 id="consultas-a-realizar">Consultas a realizar</h2><p>Con el modelo anterior indicado, pensemos en algunas consultas que serán interesantes realizar:</p><ul><li>Cantidad de usuarios con ticket por día en un periodo</li><li>Promedio de turnos por día en un periodo</li><li>Cantidad de turnos mayor o menor por dia.</li><li>Cantidad de turnos ejecutados mayor o menor por dia.</li><li>Cantidad de turnos por Tienda por día en un periodo</li><li>Top 10 de las tiendas con mayor cantidad de turnos ejecutados.</li></ul><p>Todas las consultas anteriores tienen un elemento en común, es que necesitan agruparse para poder hacer las operaciones de cálculo, para ello, SQL tiene un palabra reservada que podemos ejecutar en nuestras queries: <code>GROUP BY</code>, que adicionalmente se puede filtrar usando la palabra reserva <code>HAVING</code>.</p><p>Además de ello, de las consultas podemos entender que debemos conseguir el valor mayor, el valor menor, el promedio por ejemplo, todo lo anterior se denomina, funciones de agregado, veamos esto en detalle.</p><h3 id="agrupando-los-resultados-por-campos">Agrupando los resultados por campos</h3><p>Cuando realizamos una consulta, podemos definir que los resultados se agrupen por los valores de un campo, de esta forma, luego podemos obtener valores como conteo, suma o promedio de valores, incluso obtener el mayor o el menor valor de un campo . Adicionalmente cuando agrupamos podemos filtrar esos resultados agrupados aplicando los criterios anteriomente indicados (conteo, suma, etc);</p><p>Anatamía de un <code>GROUP BY</code> con <code>HAVING</code>:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-11.png" class="kg-image" alt="image-11" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/11/image-11.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2023/11/image-11.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-11.png 1116w" sizes="(min-width: 720px) 720px" width="1116" height="206" loading="lazy"></figure><h3 id="ordenando-los-resultados">Ordenando los resultados</h3><p>Al momento de realizar consultas, es posible querer que estas se ordenen por algun campo, siendo los criterios disponibles: de forma ascendente y de forma descendente. Esto nos permite obtener los datos con una visual definida de tal forma que sea facil ubicar los datos y también poder comparar sus valores.</p><p>Para ordenar una consulta simplemente agregamos al final de ella, las palabras reservadas <code>ORDER BY</code> y luego el o los campos los cuales deseamos ordenar, siendo posible ordenar por multiples campos y cada campo tenga su criterio (ascendente o descendente).</p><h3 id="limitando-los-resultados">Limitando los resultados</h3><p>En alguna oportunidades queremos solo obtener el primer registro de la consulta (aunque sea haya retornado una cantidad mayor de registros), o por ejemplo queremos obtener el top 10 de registros con cierta condición, para ello usamos cada herramienta de base de datos posee una palabra reservada, en nuestro caso, usando MySQL o PostgreSQL, &nbsp;la palabra reservada será <code>LIMIT</code>.</p><p>Más adelante usaremos tanto el orden como la limitación para ajustar nuestras consultas.</p><h2 id="funciones-de-agregado">Funciones de agregado</h2><p>Estas son funciones de SQL (Si SQL así como un lenguaje de programación tienen funciones, lo que lo haces aún más poderoso 😎), que realizan un calculo, en base a un campo o expresión de campos, sobre un conjunto de datos y retornan un resultado.</p><p>Tomando en cuenta lo indicado en punto anterior, las funciones de agregado generalmente van acompañadas de las palabras reservadas <code>GROUP BY</code> y en algunos casos de la palabra <code>HAVING</code> (estas trabajan en conjunto se mencionó anteriormente), pero esto no es regla, es decir no es necesario ello. Entendido esto, podemos obtener una única fila como resultado de una consulta con una función de agregado, o una fila por cada grupo generado en la query (usando <code>GROUP BY</code>)</p><p><strong>Anatomía de las funciones de agregado</strong></p><p>Las consultas o <code>SELECT</code> usando funciones de agregado van a seguir la siguiente forma:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-8.png" class="kg-image" alt="image-8" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/11/image-8.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2023/11/image-8.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-8.png 1012w" sizes="(min-width: 720px) 720px" width="1012" height="222" loading="lazy"></figure><p>Ejemplo de consultas que retornarian un único resultado:</p><ul><li>Día del primer turno registrado</li><li>Día del último turno registrado</li><li>Cantidad de turnos procesados por el sistema</li><li>Si le agregamos a la tabla de tickets, los minutos de cada turno ejecutado, podemos obtener la suma de tiempo ejecutado.</li></ul><p>Estos 4 ejemplos nos van a retornar una fila de resultado solamente, porque no estamos agrupando por ningún elemento.</p><p>Ahora bien, veamos cuales son esas funciones de agregado:</p><h3 id="count">COUNT</h3><p>Nos permite hacer el conteo, o contar cuantos registros o filas que cumplen la condición de la consulta. Su sintaxis es muy simple: ]</p><figure class="kg-card kg-code-card"><pre><code>SELECT COUNT(campo o *) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; </code></pre><figcaption>La anterior consulta nos va a retornar una sola fila</figcaption></figure><p> Ahora vemos la siguiente consulta:</p><figure class="kg-card kg-code-card"><pre><code>SELECT COUNT(campo o *) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; GROUP BY &lt;campo1&gt;</code></pre><figcaption>La anterior consulta nos va a retornar una fila por cada uno de los distintos valores de campo1</figcaption></figure><h3 id="sum">SUM</h3><p>Nos permite hacer la suma de los valores del campo de los registros o filas que cumplen la condición de la consulta. Tiene sentido cuando el campo es numérico. Su sintaxis sigue a continuación:</p><pre><code>SELECT SUM(campo) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; </code></pre><p>Agrupando por algún campo queda:</p><pre><code>SELECT SUM(campo) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; GROUP BY &lt;campo1&gt;</code></pre><h3 id="avg">AVG</h3><p>Esta función calcula el promedio de los valores del campo de los registros o filas que cumplen la condición de la consulta, se entiende como el promedio a la suma de los valores dividido entre la cantidad de registros, sería algo como SUM/COUNT. Sólo funciona si el campo es numérico. La sintaxis es:</p><pre><code>SELECT AVG(campo) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; </code></pre><p>Agrupando por algún campo queda:</p><pre><code>SELECT AVG(campo) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; GROUP BY &lt;campo1&gt;</code></pre><h3 id="max">MAX</h3><p>La función MAX, nos permite calcular el máximo o mayor valor de un conjunto de registros o filas que cumplen la condición de la consulta (siempre comento esto porque estas funciones se pueden o no combinar con condiciones de consulta). A diferencia de las anteriores, es posible usarla en cualquier tipo de dato que sea ordenable, por ejemplo, textos y números. La usamos de la siguiente forma es:</p><pre><code>SELECT MAX(campo) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; </code></pre><p>Agrupando por algún campo queda:</p><pre><code>SELECT MAX(campo) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; GROUP BY &lt;campo1&gt;</code></pre><h3 id="min">MIN</h3><p>La función MIN, nos permite calcular el menor valor o el valor mínimo de un conjunto de registros o filas devueltos a realizar una consulta, esta puede tener o no condiciones. Como su antecesora (MAX), es posible usarla en cualquier tipo de dato que sea ordenable, caso más usado: textos y números. Para ejecutarla, lo indicamos de la siguiente forma:</p><pre><code>SELECT MIN(campo) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; </code></pre><p>Agrupando por algún campo queda:</p><pre><code>SELECT MIN(campo) FROM tabla1 INNER JOIN tabla2 ON (tabla1.key = tabla2.key) WHERE &lt;condicion&gt; GROUP BY &lt;campo1&gt;</code></pre><h2 id="implementando-las-consultas-solicitadas-en-sql">Implementando las consultas solicitadas en SQL</h2><p>Vamos a realizar el código SQL para cada una de las consultas planteadas anteriormente, para ello vamos a usar lo que hemos aprendido: Funciones de agregado, agrupamiento y relacionamiento. </p><ul><li>Cantidad de usuarios con ticket por día en un periodo. Definamos ese periodo para el ejemplo como el mes de julio de 2023.</li></ul><p>En este caso, lo que requerimos es cantidad de registros, es contar por lo tanto la función de agregado que vamos a usar es <code>COUNT</code>, adicionalmente nos piden que ese conteo, sea realizado por día, es decir debemos agrupar los datos por el día del turno. </p><p>Con lo anterior la consulta a construir será:</p><pre><code>SELECT COUNT(*) FROM users INNER JOIN tickets ON (users.id = tickets.user_id) WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' GROUP BY date_time </code></pre><p>La consulta está correcta de sintaxis, pero el campo date_time es de tipo fecha y hora, por lo tanto, esta agrupando siempre que el ticket fuese realizado en el mismo día y a la misma hora, lo cual no es lo solicitado, para ello vamos a hacer uso de una función especial que nos retorne la fecha solamente en el caso de un campo de tipo <code>datetime</code>, en ese caso la función es <code>DATE,</code> luego es necesario que ademas del conteo, mostremos el día, por lo que la consulta queda:</p><pre><code>SELECT DATE(date_time), COUNT(*) FROM users INNER JOIN tickets ON (users.id = tickets.user_id) WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' GROUP BY DATE(date_time)</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-9.png" class="kg-image" alt="image-9" width="359" height="309" loading="lazy"><figcaption>Resultado obtenido de la consulta anterior.</figcaption></figure><ul><li>Promedio de turnos por día en un periodo</li></ul><p>Para esta consulta debemos hacer uso de la función <code>AVG</code>, ya que lo nos piden es el promedio de turnos por dia, en un periodo, en ese caso, tomamos la consulta anterior, y sobre esa consulta aplicamos un concepto que se denomina SUBSELECT. Cuando hacemos uso de SUBSELECT debemos encerrar entre () la consulta original y luego le damos un nombre, observa como queda la consulta:</p><pre><code>SELECT AVG(total) FROM 
(SELECT DATE(date_time), COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' GROUP BY DATE(date_time)) as t1</code></pre><p>El texto <code>as t1</code> es el que nos permite indicarle a SQL que ese SUBSELECT que está entre parentesis: <code>(SELECT DATE(date_time), COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' GROUP BY DATE(date_time))</code></p><p>Otro punto interesante es que le dimos un nombre al valor de conteo, para poder luego obtener su promedio, eso lo hicimos de esta forma: <code>COUNT(*) as total</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-10.png" class="kg-image" alt="image-10" width="215" height="103" loading="lazy"><figcaption>Este es el resultado de la consulta de promedio</figcaption></figure><ul><li>Cantidad de turnos mayor o menor por dia.</li></ul><p>En este caso debemos usar las funciones de MAX y MIN, vamos a realizar 2 consultas para ello. Para obtener el día con mayor cantidad de turnos esperados (es decir sin importar su status), hacemos la siguiente consulta:</p><pre><code>SELECT MAX(t1.total) FROM 
(
SELECT DATE(date_time) as fecha, COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' GROUP BY DATE(date_time)
) as t1;</code></pre><p>La consulta anterior nos devuelve la cantidad máximas de turnos, pero no obtenemos el día, lo cual sería importante, por lo que en ese caso podemos hacer uso de otra herramienta que no es función de agregado, pero que es básica para cualquier consulta, el ordenar, luego si solo queremos un registro, nos apoyamos en otra herramientas disponible que es la de limitar como vimos anteriormente.</p><p>Como queda nuestra consulta para obtener el día com mayor cantidad de turnos, usando orden y limitación:</p><pre><code>SELECT DATE(date_time) as fecha, COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' 
GROUP BY DATE(date_time) 
ORDER BY 2 DESC LIMIT 1</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-12.png" class="kg-image" alt="image-12" width="250" height="126" loading="lazy"><figcaption>Ahora obtenemos tanto el día como la cantidad</figcaption></figure><p>Para obtener la cantidad menor de turnos en un día usamos las mismas consultas, sólo ajustando las funciones de agregado o el criterio de orden.</p><pre><code>SELECT MIN(t1.total) FROM 
(
SELECT DATE(date_time) as fecha, COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' GROUP BY DATE(date_time)
) as t1;</code></pre><p>Observa que en este caso cambiamos <code>MAX</code> por <code>MIN</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-15.png" class="kg-image" alt="image-15" width="250" height="126" loading="lazy"><figcaption></figcaption></figure><p>Ahora usando orden y limitación:</p><pre><code>SELECT DATE(date_time) as fecha, COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' 
GROUP BY DATE(date_time) 
ORDER BY 2 ASC LIMIT 1</code></pre><p>Para este caso solo cambiamos es &nbsp;<code>DESC</code> por <code>ASC</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-16.png" class="kg-image" alt="image-16" width="250" height="126" loading="lazy"></figure><ul><li>Día con mayor o menor cantidad de turnos ejecutados</li></ul><p>Esta consulta es similar a la anterior, solo que en este caso vamos a agregar una condición del valor del campo status, que sea igual a realizado, observa como quedan las consultas:</p><p>Primero veamos el día con mayor cantidad de turnos realizados o ejecutados:</p><pre><code>SELECT MAX(t1.total) FROM 
(
SELECT DATE(date_time) as fecha, COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' AND status = 'Realizado' GROUP BY DATE(date_time)
) as t1;</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-17.png" class="kg-image" alt="image-17" width="250" height="126" loading="lazy"></figure><p>Recordando que si queremos la fecha, usamos orden y limitación:</p><pre><code>SELECT DATE(date_time) as fecha, COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' AND status = 'Realizado'
GROUP BY DATE(date_time) 
ORDER BY 2 DESC LIMIT 1</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-18.png" class="kg-image" alt="image-18" width="250" height="126" loading="lazy"></figure><p>Ahora veamos el día con menor cantidad de turnos realizados o ejecutados:</p><pre><code>SELECT MIN(t1.total) FROM 
(
SELECT DATE(date_time) as fecha, COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' AND status = 'Realizado' GROUP BY DATE(date_time)
) as t1;</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-20.png" class="kg-image" alt="image-20" width="250" height="126" loading="lazy"></figure><p>Usando orden y limitación, obtenemos tanto la cantidad como la fecha que sucedió:</p><pre><code>SELECT DATE(date_time) as fecha, COUNT(*) as total FROM users INNER JOIN tickets ON (users.id = tickets.user_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' AND status = 'Realizado'
GROUP BY DATE(date_time) 
ORDER BY 2 ASC LIMIT 1</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-21.png" class="kg-image" alt="image-21" width="250" height="126" loading="lazy"></figure><ul><li>Cantidad de turnos por Tienda por día en un periodo</li></ul><p>Veamos ahora este ejemplo, donde la función de agregado es la de conteo, pero debemos agrupar por 2 campos, en este será la fecha del turno y el nombre de la tienda, los campos están en 2 tablas diferentes pero esto no será problema para nuestra consulta. Veamos como queda:</p><pre><code>SELECT store_name, DATE(date_time) as fecha, COUNT(*) as total FROM stores INNER JOIN tickets ON (stores.id = tickets.store_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' GROUP BY store_name, DATE(date_time)
</code></pre><p>En la consulta apreciamos que el agrupamiento se realizó por el nombre de la tienda y la fecha del turno: <code>GROUP BY store_name, DATE(date_time)</code></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-22.png" class="kg-image" alt="image-22" width="383" height="257" loading="lazy"></figure><ul><li>Top 10 de las tiendas con mayor cantidad de turnos ejecutados.</li></ul><p>En este caso, analicemos la consulta, vamos a necesitar contar los turnos, función COUNT, luego debemos agruparlos por cada tienda, de esta forma obtenemos la cantidad por tienda, luego aplicamos un criterio de orden descendente, para que las primeras sean las tiendas de mayor cantidad de turnos y luego lo limitamos a 10. Excelente!, veamos la query.</p><pre><code>SELECT store_name, COUNT(*) as total FROM stores INNER JOIN tickets ON (stores.id = tickets.store_id) 
WHERE date_time &gt;= '2023-07-01' AND date_time &lt; '2023-08-01' GROUP BY store_name ORDER BY total DESC LIMIT 10
</code></pre><p>Resultando lo siguiente:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-23.png" class="kg-image" alt="image-23" width="383" height="257" loading="lazy"></figure><p></p><p>Veamos aquellas consultas que sabemos que no tienen agrupamiento:</p><ul><li>Día del primer turno registrado</li></ul><pre><code>SELECT MIN(date_time) FROM tickets t </code></pre><p>De forma rápida apreciamos que usando la función MIN, obtemos el valor mínimo de ese campo.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-25.png" class="kg-image" alt="image-25" width="244" height="109" loading="lazy"></figure><ul><li>Día del último turno registrado</li></ul><pre><code>SELECT MAX(date_time) FROM tickets t</code></pre><p>Acá apreciamos que logramos obtener el valor máximo del campo fecha del turno.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-24.png" class="kg-image" alt="image-24" width="244" height="109" loading="lazy"></figure><ul><li>Cantidad de turnos procesados por el sistema</li></ul><p>Consulta básica:</p><pre><code>SELECT COUNT(*) FROM tickets WHERE status = 'Realizado'</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-26.png" class="kg-image" alt="image-26" width="244" height="109" loading="lazy"></figure><ul><li>Si le agregamos a la tabla de tickets, los minutos de cada turno ejecutado, podemos obtener la suma de tiempo ejecutado.</li></ul><pre><code>SELECT SUM(tiempo) FROM tickets WHERE status = 'Realizado'</code></pre><p>Recuerden que para poder ejecutar la consulta anterior debe agregarse la columna tiempo en la tabla tickets.</p><p>Todo lo realizado hasta aquí lo puedes ver en la práctica en el siguiente video:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/QITspJAmAYg?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="Funciones de Agregado en SQL: Cómo Usar SUM, AVG, MAX, MIN y Más" name="fitvid2"></iframe>
          </div>
        </div>
      </figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Consumiendo APIs con ReactJS: Aprende useEffect y useState ]]>
                </title>
                <description>
                    <![CDATA[ Tutorial completo sobre cómo consumir APIs con ReactJS utilizando useEffect y useState! Esta es una guía paso a paso que te enseñará cómo interactuar con APIs externas en tus aplicaciones React. ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/consumiendo-una-api-rest-con-react-js/</link>
                <guid isPermaLink="false">654c3467208bc703b414b36a</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ useState ]]>
                    </category>
                
                    <category>
                        <![CDATA[ useEffect ]]>
                    </category>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Mon, 13 Nov 2023 02:43:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/11/4ujLXaujWZj3_2560_1440-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="introducci-n-a-las-apis">Introducción a las APIs</h2><p>Empecemos por conocer lo que es una API. Estas letras son el acrónimo de <strong>application programming interface</strong>, en español lo podemos traducir como Interfaz de programación de aplicaciones, su objetivo fundamental es permitir que la comunicación entre 2 o más elementos o sistemas. </p><p>Con la definición anterior, también indicar que existen diferentes tipos de API, una de las más conocidas es la API que ofrece <a href="https://learn.microsoft.com/es-es/windows/win32/api/">Windows</a> para poder acceder a los recursos del sistema operativo y construir aplicaciones sobre él. Otro tipo de API y es el foco de nuestra atención, es el tipo encargado del transporte de datos entre sistemas, siendo ampliamente utilizado actualmente.</p><h2 id="arquitecturas-de-api-para-transporte-de-datos">Arquitecturas de API para transporte de datos</h2><p>Dado su amplio uso, las APIs de transporte de datos se pueden organizar o implementar de diferentes formas, teniendo como aspectos diferenciales, el formato de datos y la forma como se definen las reglas de comunicación entre los principales puntos a definir, veamos algunos tipos de arquitectura de API para transporte de datos:</p><ol><li><em>REST (Representational State Transfer o Transferencia del estado representacional)</em>, es actualmente la arquitectura más usada, utiliza el protocolo http como base para el transporte de datos, cada petición u operación es independiente y realiza una tarea completa, es decir no hay dependencia entre operaciones.</li><li><em>GraphQL</em>, al igual que REST, esta basado en el protocolo http, pero se diferencia en la forma como realiza las operaciones, ya que estas se basan en un lenguaje de consulta y manipulación de datos, lo cual hace posible manejarlas de manera más detallada y óptima pero incrementando un poco la complejidad. </li><li><em>Websocket</em>. Es una arquitectura basada en el concepto de socket, el cual mediante el protocolo TCP, crea una conexión directa entre 2 puntos y la mantiene abierta, de forma tal que cualquier operación se ejecuta en el menor tiempo posible. Es indicada para casos en que la comunicación debe realizarse en tiempo real.</li><li><em>Webhook. </em>Es un tipo de arquitectura un poco diferente a las anteriores, el inicio del proceso se atribuye a eventos, es decir, es a partir de una acción, que el proceso se inicia, como por ejemplo, cuando un registro en el sistema está en la condición X, se ejecuta la petición a la URL y en general son respuestas a procesos, por lo que esta arquitectura se denomina API reversa o inversa.</li><li><em>RPC y gRPC</em>. Esta arquitectura actua como un proceso remoto ejecutado en ambiente local, trabaja bajo http, tcp, o udp. Se base en la definición de un servicio que es invocado por el cliente y resuelto por el servidor, a diferencia de REST, tanto como cliente como servidor comparten definiciones del servicio, es decir debe haber correspondencia en sus implementaciones, en REST cada lado implementa de su forma y es solo en el transporte de datos que debe correspondencia.</li><li><em>SOAP. </em>Mas que una arquitectura es protocolo de comunicación, basado en http pero con transporte de datos hecho en XML (eXtensible Markup Language, o en español 'Lenguaje de Marcado Extensible'), su forma de trabajo es basada en acceso a objetos y tiene un conjunto de reglas y restricciones que la hacen un poco más compleja de manejar pero posee mayor seguridad. </li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/1698457900830.gif" class="kg-image" alt="1698457900830" width="800" height="1120" loading="lazy"><figcaption>Tipos de arquitecturas más conocidos. Tomado de: https://www.linkedin.com/in/brijpandeyji/</figcaption></figure><h2 id="detallando-una-api-rest">Detallando una API REST</h2><p>Como comentamos anteriormente, una API REST es una herramienta o mecanismo que permite interconectar componentes o sistemas. Su uso actualmente se ha enfocado en ser la forma ideal para implementar procesos de gestión de datos, conocidos como CRUD (C-reate, R-ead, U-pdate y D-elete), de uso común para cualquier aplicación. </p><p>El formato común de transporte de datos este tipo de APIs es JSON (Javascript Object Notation), el cual por ser liviano y muy bien estructurado permite que podamos representar de forma simple y entendible los modelos que serán procesados.</p><p>La comunicación entre el cliente y el servicio se realiza a través de protocolo http como indicamos anteriormente y cada tipo de operación está relacionada o identificada por un verbo, veamos que significa ello.</p><p>Para saber que operación se va a realizar existen 2 elementos fundamentales que permiten definirla, la ruta o endpoint y el verbo. Cuandon realizamos operaciones sobre una API REST, estas las denominamos peticiones, solicitudes o requisiciones, recordando que quien inicia la operación es el cliente, conversando con el servicio que dispone o habilita la API. </p><h2 id="ruta-o-endpoint-de-una-petici-n-en-api-rest">Ruta o endpoint de una petición en API REST</h2><p>Es la dirección o url que se va a ejecutar cuando realicemos la petición, solicitud o requisición, de esta forma el protocolo http, identificará que servidor va a responder y que modelo o entidad es la que se va a procesar. Sigue a continuación lo que se denomina la anatomía de una ruta o endpoint.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image.png" class="kg-image" alt="image" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/11/image.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2023/11/image.png 711w" width="711" height="266" loading="lazy"><figcaption>Elementos o partes de un endpoint</figcaption></figure><h2 id="verbos-de-una-api-rest">Verbos de una API REST</h2><p>Para que esta conversación sea posible, existen lo que se denominan verbos, que son los que definen que tipo de operación se va a realizar, los verbos más comunes en una API REST son:</p><ul><li>GET: se define o identifica como el verbo que permite realizar consultas, es importante indicar que este verbo trabaja de la misma forma que en el protocolo http, donde los datos que enviamos quedan expuestos en la url. Esta operación es la R de R-ead.</li><li>POST: es el verbo que nos permite definir operaciones de creación de registros, los datos al igual que en el protocolo http, viajan por un mecanismo interno o cuerpo de la petición, no son visibles. En este caso enviamos los datos en lo que denominamos el cuerpo o <em>body</em> de la petición, generalmente se envian en formato JSON</li><li>PUT: mediante este verbo hacemos actualizaciones completas de registros, es decir, lo que enviamos va a sustituir lo que existe, generalmente en la url o endpoint de la petición se envía un identificador que permite saber sobre que registro se hace la actualización, importante tener claro este punto, ya que en REST tenemos 2 formas de actualizar registros, los datos al igual que en POST, no son visibles en el envío.</li><li>PATCH: con el uso de este verbo, podemos realizar actualizaciones de forma incremental, es decir podemos cambiar partes de registro sin afectarlo por completo, es decir son actualizaciones parciales. Los datos en este caso de igual forma viajan mediante el cuerpo o body de la petición, es decir no son visibles en el envío. En la ruta o endpoint generalmente se indica un parámetro que pemite identificar el registro a ser actualizado.</li><li>DELETE: a través de este verbo podemos indicar la operación de borrado de registros. No es necesario enviar body, ya que en la ruta o endpoint se indica un parámetro que permite identificar que registro se va a eliminar.</li></ul><p>En las definiciones anteriores se indica que para identificar un registro, la buena práctica o común implementación es indicarlo en la ruta, pero no hay una restricción técnica que sea de obligatorio cumplimiento.</p><h2 id="reactjs-y-los-hooks">ReactJS y los hooks</h2><p>Iniciamos recordando que ReactJS es una libreria que permite implementar y construir el front-end para aplicaciones web, esta libreria es de amplio uso, por estar basada en Javascript tiene una comunidad fuerte que apoya su evolución continua. </p><p>React trabaja de la forma en que la aplicación sólo comunica al navegador la página inicial, esto se denomina SPA o Single Page Application, y toda la lógica es controlada e implementada por la libreria, esto hace que el performance y la usabilidad de cara al usuario sea mucho mejor que las aplicaciones web tradicionales (basadas en html+css+JS y renderizado total).</p><p>Uno de los puntos fuertes que surgió a partir de la versión 16.8, es la implementación en base a componentes funcionales, lo cual facilitó de forma enorme la construcción e implementación, de igual manera en esa versión nacieron <em>los hooks</em>, pero que son y como funcionan?, en las siguientes lineas lo revisamos en detalle.</p><h2 id="entiendo-los-hooks-de-reactjs">Entiendo los hooks de ReactJS</h2><p>Empecemos por definir y entender que son los <em><em>Hooks</em></em>, expresémoslo en los términos más simples, <em><em>un hook es una función</em></em>, partiendo de esa premisa entonces podemos indicar que no es cualquier función, está claro ello, en ese caso podemos decir:</p><blockquote><em><em>Un hook es una función especial con un objetivo especifico, simplificar algún proceso que hasta el momento se realizaba de otra forma.</em></em></blockquote><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:700/0*H-KrMzFt_kMk2m_X.png" class="kg-image" alt="0*H-KrMzFt_kMk2m_X" width="700" height="301" loading="lazy"></figure><p>Con este concepto más amplio, podemos decir que los hooks, son funciones que simplifican procesos relacionados al ciclo de vida de componente funcionales, porque hasta la versión 16.7 de React, los componentes funcionales sólo recibían <em><em>props </em></em>y no era posible gestionar su estado, solo los componentes de clases (antigua forma de construir componentes, basada en POO ) soportaban está gestión.</p><p>Un detalle interesante, es que dada la usabilidad y potencia de los Hooks, no solo la librería base React actualmente tiene hooks, ahora otros paquetes de amplio uso también implementan hooks, tal como es el caso de <code><a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow">react-router-dom</a></code> por ejemplo.</p><h2 id="gesti-n-de-estado-usestate-en-reactjs">Gestión de estado - useState en ReactJS</h2><p>Uno de los puntos críticos y de suma importancia cuando construimos aplicaciones de una sóla página (SPA - Single Page Application) es la gestión de estado, dado que en el enfoque tradicional (html + css + js + server), la gestión es realizada mediante la gestión de sesiones en el lado del server, en el enfoque de ReactJS, la gestión es hecha a través de hooks, siendo el hook encargado de esta operación el <em>useState.</em></p><h2 id="conociendo-a-usestate">Conociendo a useState</h2><p>Este hook permite mantener el control de los estados de un componente, entiendo a los estados como las características o propiedades que definen el comportamiento y presentación del componente. Toda vez que un estado cambia, significa que el componente ha cambiado y por tanto debe actualizarse o renderizarse (es decir construirse nuevamente).</p><p>Tomemos un ejemplo práctico, una aplicación que presenta productos, en ese caso hablamos de un componente tipo "Galeria", que muestra datos y fotos de estos productos. Al iniciar no tiene productos, es decir, tenemos un estado tipo lista o arreglo, pero que está vacía al inicio, por lo tanto se presenta un mensaje de "Sin productos disponibles". Ahora mediante algún proceso (que vamos a entender más adelante), esa lista se llena con algunos datos, que debe suceder? que al recibirlos estos datos disparan un proceso de actualización del componente "Galeria".</p><p>Entendido el concepto teórico de funcionamiento de la gestión de estado, pasemos a colocar las manos en el código. Vamos a crear una aplicación con Vite y vamos a crear un componente funcional llamado <em>Galeria. </em>En este material puedes verificar como se realiza la creación de componentes en React. </p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/lPTvvi41frE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="🚀 Aprende ReactJS: Componentes y Primeros Pasos | Guía para Principiantes 🚀" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p>Para definir un estado se sigue la siguiente anatomia:</p><pre><code>//Definición de un estado
const [estado, setEstado] = useState(&lt;valor inicial&gt;);</code></pre><p>Observa que siempre que se define un estado, se invoca al hook <a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow"><code>useState</code></a> el cual nos retorna un arreglo que desestructuramos (recordar que la desestructuración está disponible en Javascript desde la versión ES2015) en 2 variables, una se refiere al estado en sí y otra es la función que permite asignar o atribuirle valor a ese estado. </p><p>Por ser de manejo especial, a los estados no le podemos asignar valor, como tradicionalmente lo hacemos en Javascript, entonces lo hacemos siempre a través de la función <em>setteadora</em>.</p><p>Por nuestra aplicación necesitar presentar múltiples productos, también va a ser necesario crear un componente para esta gestión. Es buena práctica en ReactJS, granular o despiezar de la manera más detallada la gestión de componentes, para que podamos aprovechar las características de la libreria y obtener un comportamiento óptimo de nuestras aplicaciones.</p><p>Veamos el código del &nbsp;componente <em>Galeria</em>:</p><pre><code class="language-Javascript">import React, { useState } from 'react'
import Product from '../Product/Product';

const productos = [
    {
        "_id": "652803510a598cf5573918f3",
        "nombre": "The Complete Common Core: State Standards Kit, Grade 5",
        "sku": "D82015FFBF",
        "precio": 12.88,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b006",
        "url": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f4",
        "nombre": "Flash Furniture 25''W x 45''L Trapezoid Red HP Laminate Activity Table - Height Adjustable Short Legs",
        "sku": "39F1B8A212",
        "precio": 117.26,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b00b",
        "url": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f3",
        "nombre": "The Complete Common Core: State Standards Kit, Grade 5",
        "sku": "D82015FFBF",
        "precio": 12.88,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b006",
        "url": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f4",
        "nombre": "Flash Furniture 25''W x 45''L Trapezoid Red HP Laminate Activity Table - Height Adjustable Short Legs",
        "sku": "39F1B8A212",
        "precio": 117.26,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b00b",
        "url": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f3",
        "nombre": "The Complete Common Core: State Standards Kit, Grade 5",
        "sku": "D82015FFBF",
        "precio": 12.88,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b006",
        "url": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f4",
        "nombre": "Flash Furniture 25''W x 45''L Trapezoid Red HP Laminate Activity Table - Height Adjustable Short Legs",
        "sku": "39F1B8A212",
        "precio": 117.26,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b00b",
        "url": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg"
        },
        ]
    },
];


const Gallery = () =&gt; {
    //Definición del estado
    const [listaProductos, setListaProductos] = useState([]);

    const llenarProductos = () =&gt; {
        setListaProductos([...productos]);
    }

    //Si no hay producto emitimos un mensaje
    if (listaProductos.length === 0) {
        return &lt;&gt;
        &lt;h1&gt;No hay productos disponibles&lt;/h1&gt;
        &lt;button onClick={llenarProductos}&gt;Cargar productos&lt;/button&gt;
        &lt;/&gt;
    }
    
    //Mostramos los productos
  return (
    &lt;div style={{display:'flex', width:'100%', flexWrap:'wrap'}}&gt;
    {
        listaProductos.map((p) =&gt; {
            return &lt;Product product={p}&gt;&lt;/Product&gt;
        })
    }
    &lt;/div&gt;
    
  )
}

export default Gallery</code></pre><p>Expliquemos un poco el código anterior, empecemos por definir el estado, esto se encuentra en la siguiente línea:</p><pre><code class="language-Javascript">	//Definición del estado
    const [listaProductos, setListaProductos] = useState([]);</code></pre><p>Como indicamos anteriormente, realizamos la llamada al hook <code>useState</code> el cual nos retorna 2 variables, nuestro estado <code>listaProductos</code> y su función setteadora <code>setListaProductos</code>, notese que le pasamos a useState un arreglo vacío como valor inicial, de esta forma estamos indicando que el estado va a ser de tipo arreglo.</p><p>Luego tenemos una expresión de función que nos va a servir para llenar el arreglo a partir de un evento, este evento será el click de un botón que hemos agregado al mostrar el mensaje de "No hay productos disponibles".</p><p>Observa que en esta función <a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow"><code>llenarProductos</code></a> se realiza la asignación del estado, de un modo que para quien viene de Javascript puro o vanilla, puede en principio no ser entendida, vamos explicar un poco porque se hace así:</p><pre><code>const llenarProductos = () =&gt; {
	setListaProductos([...productos]);
}</code></pre><p>Observa que se usa el <em>spread operator</em>, el cual es una característica disponible de ES2015, este operador básicamente lo que hace es crear un nuevo arreglo y lo que pasamos como: ...&lt;variable&gt;, se copia inmediamente a este nuevo arreglo, de esta forma lo que estamos realizando es la creación de un arreglo con los datos de productos, al ser un nuevo arreglo hay cambio de estado y ReactJS reacciona, actualizando el componente. Y por qué no usar el método <code>push</code> por ejemplo, porque en ese caso estariamos agregando un valor, solo que el arreglo para React no estaria modificado, por ser un tipo de dato estructurado y manejado por referencia, en esencia por ser inmutable, en este caso, para React el arreglo sigue igual, no hay cambio de estado por lo tanto no se renderiza.</p><p>Avanzando en el código nos encontramos la implementación del componente, donde lo primero que realizamos es la verificación de existencia o no de productos, si no existen se muestra un mensaje y un botón para cargar productos. En caso de existir productos, se hace un recorrido, recordando que por ser un arreglo, tenemos disponible sus métodos nativos de Javascript, entre ellos el método <a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow"><code>map</code></a>.</p><p>Recorremos el arreglo y para cada producto construimos un componente de tipo <code>Product</code>. Este componente recibe los datos de cada producto y hace una presentación en pantalla de las informaciones. Sigue a continuación el código del componente <a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow"><code>Product</code></a></p><pre><code>import React from 'react'

const Product = ({product}) =&gt; {
  return (
    &lt;div style={{display:'flex', width:'30%', border: '1px solid white', justifyContent:'center', flexDirection:'column', margin: '1rem',}}&gt;
        &lt;img src={product.imagenes[0]['url'] ?? './sinimagen.jpg'} style={{width:'100%', height:'20vh', objectFit:'contain'}}&gt;&lt;/img&gt;
        &lt;hr&gt;&lt;/hr&gt;
        &lt;div style={{height: '30vh'}}&gt;
            &lt;div&gt;
                &lt;h3 style={{overflowX:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', maxHeight:'100%', }}&gt;{product.nombre}&lt;/h3&gt;
            &lt;/div&gt;
            &lt;div style={{height: '20vh'}}&gt;
                &lt;p style={{overflow:'hidden', maxHeight:'15vh', textOverflow:'ellipsis'}}&gt;{product.descripcion}&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div style={{display:'flex'}}&gt;
            &lt;div&gt;
                Precio: {product.precio}
            &lt;/div&gt;
            &lt;div&gt;
                SKU: {product.sku}
            &lt;/div&gt;
        &lt;/div&gt;        
    &lt;/div&gt;
  )
}

export default Product</code></pre><p>Este código es simple, lo que retorna es el HTML necesario para presentar los datos de productos, le hemos hecho algunos ajustes en cuanto a CSS para presentar la información de una forma agradable. Nota que valimos si hay imágenes en el producto, de lo contrario presentamos una imagen por defecto.</p><p>Ya hemos visto como funciona la gestión de estado, solo que en este caso nuestros datos están en memoria o en el código, luego hemos tenido que hacer un clic para poder cargarlos. Avancemos en el uso de otro hook que nos va a permitir, consultar una API externa y luego cargar mediante la actualización del estado.</p><h2 id="efectos-secundarios-useeffect-en-reactjs">Efectos secundarios - useEffect en ReactJS</h2><p>Los efectos secundarios, son acciones que podemos ejecutar durante el ciclo de vida de un componente ReactJS, para ello detallemos &nbsp;cual es el ciclo de vida que ReactJS implementa:</p><ul><li>Montaje, es cuando el componente es creado.</li><li>Actualización, proceso ejecutado toda vez que un estado del componente cambia, por tanto es necesario reconstruir el componente</li><li>Desmontaje, cuando se destruye el componente, por ejemplo si nos cambiamos de vista.</li></ul><p>Analizando este ciclo, entendemos que nuestro componente es creado, solo se actualiza por un cambio de estado, el cual es generado a partir de un evento, generalmente ejecutado o invocado por el usuario y luego se destruye. Pensemos ahora que deseamos presentar nuestros productos al momento de que se cargue el componente, sólo que estos productos seran consultados a una API externa, por lo cual no se van a mostrar de inmediato, en el mejor de los casos puede que sean algunos segundos de tiempo, pero puede pasar que sea necesario un tiempo mayor, o peor aún que se genere algún error, en ese caso nuestro usuario va a estar esperando con una pantalla blanca, esto no es correcto.</p><p>Es allí en ese punto anterior, que <code>useEffect</code> y los efectos secundario entran en escena. Ahora lo anterior con <code>useEffect</code>, queda de la siguiente forma: </p><ul><li>Creamos el componente, sabiendo que no tiene datos, por lo tanto podemos informar al usuario de esto, o mejor aún podemos decirle que estamos buscando los datos.</li><li>Inmediatamente luego de creado el componente, podemos invocar a <code>useEffect</code>, el cual ejecutará un callback, donde nosotros tenemos la libertad de poder consumir la API indicada (recordando que esto se hace con promesas o async/await de Javascript).</li><li>Al llegar los datos podemos actualizar nuestro estado <code>listaProductos</code>, luego esto va a disparar la actualización del componente. Nuestro usuario va a estar atento y siempre informado de lo que sucede (usabilidad)</li></ul><p>Y como se implementan los pasos anteriormente descritos?, vayamos al código, tomando en cuenta que vamos a consumir una API que se encuentra en la siguiente URL: <a href="https://apiexpress-x7sl.onrender.com/productos">https://apiexpress-x7sl.onrender.com/productos</a>, esta API fue desarrollada por mi y es pública, así que puedes usarla para que realices tus ejercicios.</p><h2 id="implementando-el-consumo-de-una-api-con-reactjs">Implementando el consumo de una API con ReactJS</h2><p>En nuestro caso, el consumo de datos que queremos realizar es a nivel del componente <em>Galeria</em>, el cual actualmente tiene los datos en memoria, como debemos ajustar su código, primero empecemos por conocer la anatomía del useEffect:</p><pre><code>useEffect(() =&gt; {
      //Callback a ejecutar en el proceso de montaje y/o actualización
    
      return () =&gt; {
        //Callback a ejecutar en el proceso de desmontaje
      }
    }, [Arreglo de dependencias para saber cuando ejecutar el useEffect])</code></pre><p>Con lo anterior podemos percibir que el <code>useEffect</code> cumple a cabalidad el ciclo de vida de componentes, de acuerdo a lo indicado anteriormente:</p><ul><li>Ejecuta un callback (bloque de código o función) al hacer el montaje (Paso 1 del ciclo de vida) y al hacer actualizaciones (Paso 2 del ciclo de vida, esta ejecución es opcional y va a depender del arreglo de dependencias).</li><li>Puede ejecutar un callback al hacer el desmontaje (Paso 3 del ciclo de vida, este es opcional)</li></ul><p>Sólo hay una restricción en el <code>useEffect</code>, se ejecuta siempre en el proceso de montaje de acuerdo a lo anterior.</p><p>Expliquemos ahora el arreglo de dependencias:</p><ul><li>Si el arreglo no se indica, el <code>useEffect</code> se ejecuta tanto en el montaje como en cualquier cambio de estado.</li><li>Si el arreglo se indica vacio [], el <code>useEffect</code>se ejecuta solo en el proceso de montaje.</li><li>Si dentro del arreglo, indicamos algunos estados, el <code>useEffect</code> se ejecuta tanto en el montaje, como cuando esos estados indicados cambien. </li></ul><p>Con ello tenemos flexibilidad para indicar, como queremos que sea ejecutado este efecto secundario.</p><p>Veamos como queda nuestro código luego de implementado el <code>useEffect</code>:</p><pre><code>import React, { useState, useEffect } from 'react'
import Product from '../Product/Product';



const Gallery = () =&gt; {
    //Definición del estado
    const [listaProductos, setListaProductos] = useState([]);

    useEffect(() =&gt; {
      const obtenerDatos = async () =&gt; {
        const res = await fetch('https://apiexpress-x7sl.onrender.com/productos');
        const data = await res.json();
        setListaProductos([...data]);
      }

      obtenerDatos();

    }, []);
    

    //Si no hay producto emitimos un mensaje
    if (listaProductos.length === 0) {
        return &lt;&gt;
        &lt;h1&gt;Cargando productos&lt;/h1&gt;
        &lt;/&gt;
    }
    
    //Mostramos los productos
  return (
    &lt;div style={{display:'flex', width:'100%', flexWrap:'wrap'}}&gt;
    {
        listaProductos.map((p, i) =&gt; {
            return &lt;Product key={i} product={p}&gt;&lt;/Product&gt;
        })
    }
    &lt;/div&gt;
    
  )
}

export default Gallery</code></pre><p>Expliquemos los cambios que hemos realizado:</p><ul><li>Ya no es necesario tener el arreglo de productos, ni el botón para cargar datos, ni el handler para el clic <code>llenarProductos</code>, con esto nuestro código ha quedado más simple.</li><li>Luego hemos implementado el <code>useEffect</code> de la siguiente forma:</li></ul><pre><code>useEffect(() =&gt; {
      const obtenerDatos = async () =&gt; {
        const res = await fetch('https://apiexpress-x7sl.onrender.com/productos');
        const data = await res.json();
        setListaProductos([...data]);
      }

      obtenerDatos();

    }, []);</code></pre><p>Tenemos una función de callBack llamada <code>obtenerDatos</code>, la cual de forma asincronica, se conecta con la API (<a href="https://apiexpress-x7sl.onrender.com/productos">https://apiexpress-x7sl.onrender.com/productos</a>) y obtiene una respuesta, luego pedimos solo los datos en formato JSON, tal como indicamos que la API los retornaba, un ejemplo de los datos se puede ver en la siguiente imagen:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-1.png" class="kg-image" alt="image-1" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/11/image-1.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2023/11/image-1.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-1.png 1074w" sizes="(min-width: 720px) 720px" width="1074" height="699" loading="lazy"><figcaption>Ejemplo de retorno de la API que estamos usando</figcaption></figure><p>Estos datos nos han quedado disponibles en la variable: <code>data</code>, ahora esta variable que es un arreglo de objetos, se lo asignamos al estado <code>listadoProductos</code>, con lo que explicamos anteriormente, este cambio de estado dispara la actualización del componente <em>Galeria</em>, y con ello hemos podido lograr el objetivo planteado, consumir datos via API y usando <code>useState</code> y <code>useEffect.</code></p><p>En el siguiente video podrás apreciar la construcción de todo lo explicado anteriormente.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/TTEb_AkGMEc?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="🌐 Consume APIs con ReactJS: Aprende useEffect y useState | Tutorial Completo 🌐" name="fitvid1"></iframe>
          </div>
        </div>
      </figure> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
