El desarrollo web moderno exige el uso de tecnologías que no solo sean eficientes, sino que también ofrezcan una gran flexibilidad para adaptarse a los cambios rápidos en las especificaciones del proyecto. Hoy en día, existen varias pilas de tecnologías que combinan diferentes componentes para crear aplicaciones web, pero la elección de las herramientas adecuadas es crucial para garantizar un proceso de desarrollo ágil y la creación de aplicaciones robustas y escalables. La pila FARM, que combina FastAPI, React y MongoDB, ha demostrado ser una opción poderosa y eficiente para el desarrollo web completo (Full Stack).

Cada componente de la pila FARM cumple con un propósito específico y, juntos, proporcionan una solución altamente optimizada para el desarrollo de aplicaciones web. FastAPI, como marco backend, es conocido por su rapidez y eficiencia, gracias a su uso de Python y su integración con el estándar de autenticación mediante JSON Web Tokens (JWT). React, por otro lado, se encarga del frontend, utilizando una arquitectura basada en componentes que permite crear interfaces de usuario altamente interactivas y escalables. Finalmente, MongoDB, una base de datos NoSQL, se integra perfectamente con FastAPI para manejar grandes cantidades de datos sin sacrificar la velocidad o la flexibilidad.

Uno de los mayores beneficios de la pila FARM es su capacidad para adaptarse a aplicaciones con especificaciones fluidas. Las aplicaciones web modernas requieren un sistema de backend flexible que pueda manejar datos dinámicos y cambios constantes en los requisitos funcionales. FastAPI permite una rápida definición de modelos de datos utilizando Pydantic, mientras que MongoDB proporciona una estructura de base de datos flexible para almacenar datos no estructurados, permitiendo la rápida iteración y modificación de la aplicación.

El uso de JWT para la autenticación añade una capa de seguridad robusta, lo cual es crucial para las aplicaciones web modernas que manejan información sensible. Con React, se pueden implementar fácilmente características avanzadas como la autenticación basada en tokens, integrando seamless el frontend con el backend mediante hooks y contextos. Esta integración entre frontend y backend facilita un flujo de trabajo continuo y fluido que ahorra tiempo de desarrollo y mejora la experiencia del desarrollador.

Un aspecto importante de la pila FARM es su enfoque en la escalabilidad y la capacidad de realizar tareas en segundo plano. FastAPI, combinado con Beanie —una biblioteca ODM para MongoDB— permite a los desarrolladores definir modelos que mapean a colecciones de MongoDB y realizar tareas en segundo plano con facilidad. Esto es particularmente útil para aplicaciones que requieren la integración de servicios de terceros, ya que permite manejar procesos asíncronos sin interrumpir el flujo principal de la aplicación.

Otro punto fuerte de esta pila es su compatibilidad con herramientas modernas de desarrollo como Vite y Next.js, que mejoran la experiencia tanto en la creación del frontend como en el despliegue de la aplicación. Vite permite un entorno de desarrollo extremadamente rápido y eficiente, mientras que Next.js proporciona una potente infraestructura para el renderizado en el servidor y la creación de aplicaciones web escalables. Estas tecnologías, combinadas con el uso de FastAPI y MongoDB, permiten la creación de aplicaciones rápidas, escalables y fáciles de mantener.

Es importante entender que la pila FARM no es la única opción disponible para los desarrolladores, pero sus componentes están diseñados para funcionar juntos de manera óptima, reduciendo la necesidad de configuraciones complicadas o integraciones manuales entre distintas tecnologías. La facilidad de uso, la flexibilidad y la escalabilidad son sus puntos fuertes, y estas características la hacen especialmente adecuada para aplicaciones web modernas que deben evolucionar y adaptarse rápidamente a los cambios del mercado.

Al elegir la pila FARM, los desarrolladores no solo están eligiendo herramientas individuales poderosas, sino un conjunto completo y eficiente de tecnologías que trabajan juntas para acelerar el proceso de desarrollo. Ya sea que se trate de un proyecto de pequeña escala o de una aplicación más compleja, la combinación de FastAPI, React y MongoDB ofrece una base sólida para construir aplicaciones web rápidas, seguras y escalables.

¿Cómo integrar la validación de formularios y la autenticación en una aplicación React?

En este proceso, se debe delegar el manejo del formulario a la función onSubmitForm. Los dos campos del formulario, uno para el nombre de usuario y otro para la contraseña, son casi idénticos:

javascript
Usuario {errors.username && {errors.username.message} }

Las partes resaltadas en el código corresponden al registro de los campos con el Hook useForm, lo que permite a React Hook Form (RHF) conocer cuáles campos se esperan y qué errores están asociados a cada uno de ellos. De este modo, los campos se registran en el formulario a través de esta sintaxis con el operador spread. Al estar los errores asociados a los campos, es posible mostrarlos de manera inmediata junto a los campos respectivos, lo que mejora la experiencia del usuario.

La misma estructura se repite para el campo de la contraseña:

javascript
Contraseña {errors.password &&
{errors.password.message} }

El botón de envío también se incluye de manera intuitiva, como se observa en el código final:

javascript
Iniciar sesión

El código completo que ilustra esta configuración está disponible en el repositorio del libro. Con esta configuración, el formulario está listo y completamente manejado por React Hook Form junto con la validación proporcionada por Zod. Si se ingresa un dato que no cumple con los criterios de validación (por ejemplo, un nombre de usuario o una contraseña con menos de cuatro caracteres), el formulario mostrará un mensaje de error junto al campo correspondiente.

Una vez que el formulario de inicio de sesión esté listo, se debe crear un contexto de autenticación que permita al usuario mantenerse logueado. Este proceso, que implica la creación de un contexto en React y el almacenamiento del JWT, es muy similar al cubierto en el Capítulo 6, "Autenticación y Autorización". En este capítulo, nos centraremos en las partes clave del código.

Contexto de Autenticación y Almacenamiento del JWT

En esta sección, se conectará el formulario creado previamente, impulsado por RHF, con la API de Contextos de React. El procedimiento para definir un contexto de React ya fue explicado en detalle en el Capítulo 4, "Introducción a FastAPI". Aquí, se aplicará ese conocimiento para crear un contexto similar que mantenga el estado de autenticación de la aplicación:

  1. Crear una nueva carpeta en el directorio /src y nombrarla contexts. Dentro de esta carpeta, crear un archivo nuevo llamado AuthContext.jsx y definir el proveedor:

javascript
import { createContext, useState, useEffect } from 'react';
import { Navigate } from 'react-router-dom';
export const AuthContext = createContext();
export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [jwt, setJwt] = useState(localStorage.getItem('jwt') || null); const [message, setMessage] = useState("Por favor, inicie sesión");

El contexto que se crea es relativamente simple y contiene un par de variables de estado necesarias para el flujo de autenticación: el nombre de usuario (su presencia o ausencia indicará si el usuario está autenticado), el JWT y un mensaje auxiliar que, en este caso, solo sirve para la depuración e ilustración. Los valores iniciales se configuran en null para el nombre de usuario, una cadena vacía para el JWT, y un mensaje genérico de "Por favor, inicie sesión".

  1. Luego, se debe agregar un useEffect que se ejecute cuando se cargue el contexto o cuando se recargue la página:

javascript
useEffect(() => {
const storedJwt = localStorage.getItem('jwt'); if (storedJwt) { setJwt(storedJwt); fetch(`${import.meta.env.VITE_API_URL}/users/me`, { headers: { Authorization: `Bearer ${storedJwt}`, }, }) .then(res => res.json()) .then(data => { if (data.username) { setUser({user: data.username}); setMessage(`¡Bienvenido de nuevo, ${data.username}!`); } else { localStorage.removeItem('jwt'); setJwt(null); setUser(null); setMessage(data.message); } }) .catch(() => { localStorage.removeItem('jwt'); setJwt(null); setUser(null); setMessage('Por favor, inicie sesión o registrese'); }); } else { setJwt(null); setUser(null); setMessage('Por favor, inicie sesión o registrese'); } }, []);

Este useEffect verifica si existe un JWT en el almacenamiento local. Si está presente, realiza una llamada a la API de FastAPI para verificar si el JWT puede devolver un usuario válido. Si la respuesta es positiva, el nombre de usuario se establece y el JWT se guarda en el contexto. Si el JWT está expirado, ha sido alterado o es inválido, se elimina del almacenamiento local y se restablecen las variables de estado a null, mostrando un mensaje de error al usuario. Este ciclo de verificación solo se ejecuta una vez durante el primer renderizado debido a que el array de dependencias está vacío.

Implementación de la Función de Inicio de Sesión

La función de inicio de sesión residirá dentro del contexto para mayor simplicidad, aunque podría estar en un archivo separado. El flujo del inicio de sesión es el siguiente:

  1. El usuario proporciona su nombre de usuario y contraseña.

  2. Se realiza una llamada fetch al backend.

  3. Si la respuesta tiene un estado HTTP 200 y se devuelve el JWT, se establece en el almacenamiento local y en el contexto, y el usuario es autenticado.

  4. Si la respuesta no devuelve un estado HTTP 200, significa que la información de inicio de sesión no fue aceptada, y en ese caso, tanto el JWT como el nombre de usuario se invalidan en el contexto.

Para implementar la función de inicio de sesión, se debe realizar lo siguiente:

javascript
const login = async (username, password) => {
const response = await fetch(`${import.meta.env.VITE_API_URL}/users/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username, password }), }); const data = await response.json(); if (response.ok) { setJwt(data.token);
localStorage.setItem('jwt', data.token);
setUser(data.username); setMessage(`Inicio de sesión exitoso: bienvenido ${data.username}`); } else { setMessage('Error en el inicio de sesión: ' + data.message); } };

Este proceso de autenticación es crucial para que los usuarios puedan interactuar de forma segura con la aplicación, manteniendo su estado de autenticación a lo largo de las sesiones.