JSX es la piedra angular que une el concepto de React. Se trata de una extensión de JavaScript que permite escribir una mezcla de código HTML y JavaScript, lo cual facilita la creación de interfaces de usuario interactivas. A pesar de que a primera vista parece una sintaxis nueva, JSX en realidad se compila en JavaScript puro mediante herramientas como Babel, lo que permite que el navegador interprete este código de forma eficiente. En esencia, JSX permite a los desarrolladores escribir código similar a HTML dentro de JavaScript, lo que mejora la legibilidad y facilita el mantenimiento del código.
React tiene dos funciones esenciales que son visibles en el archivo main.jsx de un proyecto Vite recién creado. Si inspeccionas el archivo, notarás que se importan dos paquetes. React es responsable de manejar funcionalidades como JSX, mientras que ReactDOM se encarga de realizar las operaciones sobre el DOM real. El concepto clave en React es "declarativo", lo que significa que el desarrollador puede describir el UI y los flujos de datos asociados. React, a su vez, se encarga de gestionar cómo lograr esa funcionalidad mediante mecanismos de optimización y actualización eficientes.
La creación de una página o aplicación en React comienza con los elementos React, los cuales son los bloques de construcción más pequeños. Un ejemplo sencillo de un elemento sería el siguiente:
Este código parece un elemento HTML <h1>, pero también es JavaScript. Ambas observaciones son correctas, porque JSX permite crear elementos React que se insertan en el árbol del DOM virtual de React, el cual difiere del HTML real. A través de un proceso denominado "diferenciación" (diffing), React se encarga de mantener el DOM real sincronizado con el DOM virtual, lo que optimiza el rendimiento de la aplicación. Además, los elementos React son inmutables; una vez creados, no se pueden modificar directamente. Sin embargo, pueden ser reemplazados por nuevos elementos.
Es crucial recordar que todo componente React, incluyendo el archivo App.jsx —que es el único componente en este ejemplo básico—, debe devolver solo un único elemento: un elemento div o un fragmento, que es un contenedor vacío. Este principio se debe aplicar incluso cuando se trabajan con estructuras más complejas, ya que React necesita un solo nodo raíz para trabajar de manera eficiente.
Un ejemplo de cómo crear componentes en React sería el siguiente:
En este código, se declara un array con datos de coches. Luego, dentro de la declaración return, se utiliza la función map de JavaScript para iterar sobre este array. Cada elemento del array es referenciado como el, y posteriormente se utiliza para renderizar una lista de coches cuyo precio es menor o igual al presupuesto especificado. Además, en el ejemplo se observa el uso del atributo key en la lista de elementos, una propiedad única que React necesita para identificar y manejar eficientemente las modificaciones del DOM cuando los elementos cambian.
Lo más importante a recordar es que, al crear una lista de elementos en React, cada elemento debe tener una clave única, que permitirá a React gestionar la actualización y el orden de los elementos de manera más eficiente.
El siguiente paso es profundizar en los componentes. Estos son piezas reutilizables de la interfaz de usuario (UI). En React, los componentes son funciones que devuelven fragmentos de UI escritos en JSX. Los componentes permiten que el código sea modular y reutilizable, lo que facilita la composición de la interfaz de usuario.
Para ilustrar esto, se puede dividir una aplicación en varias partes o componentes, como el encabezado, el cuerpo y el pie de página. Cada uno de estos componentes es independiente y puede ser importado en el componente principal, App.jsx, para ensamblar la aplicación completa. Por ejemplo, el componente Header podría ser el encargado de mostrar el título de la página:
Un aspecto importante al trabajar con componentes es la organización de archivos. Los componentes deben guardarse en archivos con extensión .jsx o .js, y sus nombres deben estar en mayúsculas, siguiendo una convención en React para diferenciarlos de elementos HTML. Además, es recomendable organizar los componentes en una carpeta components dentro de la estructura del proyecto.
Es fundamental planificar cuidadosamente qué partes de la aplicación pueden abstraerse como componentes. Este enfoque modular no solo hace que el código sea más limpio y fácil de mantener, sino que también permite la reutilización de componentes en diferentes partes de la aplicación, lo cual optimiza el tiempo de desarrollo.
Al construir la aplicación, debes recordar que React no proporciona un lenguaje de plantillas dedicado para realizar iteraciones sobre arrays u otras estructuras. En cambio, puedes usar las herramientas estándar de JavaScript, como la función map para recorrer listas, el operador ternario para condicionales, y las plantillas literales para interpolar cadenas de texto.
Al usar estas características, no solo construyes una aplicación eficiente, sino que también aprendes a dominar el ecosistema JavaScript, aprovechando sus características más avanzadas dentro del entorno React. El resultado es una experiencia de desarrollo ágil y optimizada, que permite crear interfaces ricas e interactivas de manera efectiva.
¿Cómo Funciona la Autenticación y Autorización en Aplicaciones React con JWT?
Hasta ahora, es probable que hayas trabajado con aplicaciones de una sola página (SPA, por sus siglas en inglés), pero sin haber explorado algunas funcionalidades avanzadas. Sin embargo, una SPA no se limita a una sola URL. Si navegas a tu cuenta de Gmail, notarás que la URL cambia con cada acción que realizas, lo cual refleja el cambio dinámico de los contenidos en la página sin necesidad de recargarla por completo. Este tipo de navegación es posible gracias a las soluciones de enrutamiento en las SPAs, y uno de los paquetes más populares y maduros para este propósito es React Router.
El enrutador de React permite que la aplicación cargue diferentes componentes dependiendo de la ruta que se active. Por ejemplo, cuando se accede a la ruta /about, la aplicación carga un componente específico, como About.jsx, dentro del componente principal App.jsx, reemplazando cualquier otro componente previamente cargado. Este tipo de estructuras son esenciales para aplicaciones modernas y reactivas, que necesitan cambiar el contenido sin alterar la URL de manera significativa.
React es tan popular que tiene un ecosistema diverso de herramientas y bibliotecas que te permitirán mejorar la experiencia de desarrollo y de usuario. Además de Tailwind CSS, puedes integrar casi cualquier framework de UI o CSS, ya sea de manera directa o mediante una versión optimizada para React, como Bootstrap o Ant Design. Para mejorar la experiencia del usuario, bibliotecas como Framer Motion permiten la implementación de animaciones sutiles, mientras que herramientas como React Hook Form aceleran el desarrollo de formularios. Para problemas complejos de manejo de estado, Redux es una de las bibliotecas más utilizadas, aunque existen alternativas más especializadas, como Recoil, Zustand o React Query.
En el contexto de la autenticación y autorización, las aplicaciones deben permitir la verificación de la identidad de un usuario (autenticación) y asegurarse de que el usuario autenticado tiene permisos para realizar ciertas acciones (autorización). Estos conceptos son fundamentales y complejos, pero con el enfoque adecuado, se pueden integrar eficientemente en aplicaciones modernas utilizando tecnologías como FastAPI en el backend y React en el frontend.
Una de las formas más comunes de implementar autenticación en aplicaciones web modernas es a través de JSON Web Token (JWT). El protocolo HTTP, por naturaleza, es sin estado, lo que significa que no tiene memoria entre solicitudes. Esto implica que, si deseas mantener un estado entre diferentes solicitudes, necesitas un mecanismo que pueda recordar ciertos datos entre sesiones, como la identidad del usuario o sus preferencias. Para lograr esto, una de las soluciones más utilizadas es JWT.
¿Qué es un JSON Web Token (JWT)?
JWT es un estándar para estructurar una cadena de texto que contiene información sobre el usuario de manera segura. A pesar de que existen múltiples maneras de mantener la identidad de un usuario en una aplicación, JWT es ampliamente adoptado por su facilidad de integración y flexibilidad en aplicaciones frontend como React, Vue.js o Angular, y en aplicaciones móviles que consumen APIs REST.
Un JWT consta de tres partes:
-
Header (Encabezado): Contiene los metadatos del token, como el algoritmo de firma utilizado y el tipo de token.
-
Payload (Carga útil): Esta es la parte más relevante, ya que contiene los datos esenciales para la autenticación. Entre estos se incluyen el ID del usuario, la fecha y hora en que se emitió el token, la fecha de expiración del mismo y otros campos opcionales, como el nombre de usuario y los roles del usuario.
-
Signature (Firma): Es la parte más importante, ya que garantiza que los datos del token no han sido alterados. Esta firma se genera a partir del encabezado y la carga útil, y cualquier modificación en estas partes invalidaría el token.
La firma actúa como una prueba de que los datos contenidos en el JWT no han sido manipulados. Por ejemplo, si alguien intenta cambiar el nombre de usuario de "John" a "Rita" en el token, también tendría que modificar la firma para que coincida, pero este cambio invalidaría el token. Esta propiedad asegura que el token sea auténtico y que no haya sido alterado por un tercero.
Una de las ventajas de usar JWT es que elimina la necesidad de enviar credenciales de usuario (como email y contraseña) en cada solicitud posterior. Una vez que el usuario se autentica y se genera el token, este puede ser utilizado para autenticar todas las solicitudes sin necesidad de volver a verificar las credenciales en cada interacción.
Integración de JWT con React
Integrar JWT en una aplicación React implica trabajar con varios de sus componentes clave, como Hooks, Context y React Router. A través de estos, puedes gestionar el estado de la autenticación y permitir el acceso a las rutas protegidas basadas en el estado del token.
-
React Context se utiliza para compartir el estado de autenticación entre componentes sin necesidad de pasar props manualmente a través de todos los niveles de la jerarquía de componentes.
-
React Hooks permiten manejar el estado y efectos secundarios en el frontend, facilitando la integración del token en las peticiones API.
-
React Router se puede utilizar para proteger rutas y permitir o denegar el acceso a ciertas páginas dependiendo de si el usuario está autenticado o no.
Es importante recordar que la autenticación no solo implica verificar la identidad del usuario, sino también controlar lo que este usuario puede hacer una vez autenticado. Esto es donde entra la autorización, que define qué operaciones un usuario puede realizar en tu aplicación, basándose en su rol, privilegios o permisos. Por ejemplo, un usuario con privilegios de "admin" podría acceder a configuraciones que un usuario estándar no podría.
Cuando se implementa JWT en una aplicación, la clave está en no solo almacenar el token de manera segura, sino también en validarlo correctamente antes de permitir el acceso a rutas o recursos sensibles. Esto implica verificar que el token no esté expirado y que sea válido antes de autorizar cualquier acción.
Importancia de la Seguridad
Es crucial entender que, aunque JWT es una forma segura de autenticar usuarios, no debe tomarse a la ligera. La forma en que gestionas los tokens, tanto en el frontend como en el backend, juega un papel fundamental en la seguridad de tu aplicación. Los tokens deben almacenarse en un lugar seguro, como en el almacenamiento local del navegador (localStorage) o en cookies HttpOnly, para evitar que puedan ser accedidos por scripts maliciosos.
Además, siempre debes implementar un mecanismo de expiración para los tokens y renovar los mismos cuando sea necesario, asegurando así que las sesiones no sean interminables y que los usuarios deban volver a autenticarse después de un tiempo determinado.
¿Cómo crear una aplicación backend con FastAPI, Beanie y OpenAI?
El uso de Modelos de Lenguaje de Gran Escala (LLMs) ha ganado gran popularidad en los últimos años, y se espera que su adopción siga creciendo, extendiéndose a nuevos campos de implementación. En este contexto, aprender a desarrollar aplicaciones backend eficaces con herramientas modernas como FastAPI y Beanie es esencial para el desarrollo de servicios robustos y escalables.
Construcción del backend con FastAPI y Beanie
Para simplificar y hacer la aplicación lo más ilustrativa posible, el API que se construirá en este capítulo no se diferenciará demasiado del que se creó en el capítulo 7, "Construcción de un Backend con FastAPI". Este enfoque permite al lector captar de forma natural las diferencias clave entre el uso de Motor (o PyMongo) directamente y el uso de Beanie como ODM (Object-Document Mapper).
Los Mappers de Objetos Relacionales (ORMs) y los Mappers de Objetos de Documento (ODMs) son herramientas que permiten abstraer las bases de datos subyacentes, ya sean relacionales o no relacionales, facilitando así el proceso de desarrollo. Ejemplos conocidos de ORMs en Python son Django ORM y SQLAlchemy, que han demostrado su eficacia en el tiempo. En cuanto a los ODMs, Beanie y Odmantic están ganando popularidad en la comunidad de Python y MongoDB, siendo Beanie la opción más madura y estable de las dos.
Introducción a Beanie
Beanie es uno de los ODM más populares para trabajar con MongoDB en Python. Utiliza clases que representan documentos NoSQL, y al usarse, cada colección en MongoDB se mapea a una clase de documento correspondiente. Este enfoque facilita las operaciones CRUD y elimina la necesidad de escribir código repetitivo y de bajo nivel. Además, Beanie maneja elegantemente el tipo ObjectId de MongoDB, y dado que la clase de documento está basada en Pydantic, se heredan de inmediato todas las poderosas funcionalidades de validación y análisis que esta librería ofrece.
Una de las características destacadas de Beanie es su naturaleza asincrónica, lo que lo hace perfecto para aplicaciones de alto rendimiento como las construidas con FastAPI. Además, es totalmente compatible con Pydantic, lo que permite realizar validaciones de datos de manera sencilla. También permite realizar operaciones CRUD de manera eficiente y soporta el robusto marco de agregación de MongoDB.
Integración de servicios de terceros con FastAPI y Beanie
A lo largo de este capítulo, se explorará cómo integrar Beanie en una aplicación FastAPI, realizando conexiones con bases de datos, mapeando colecciones a clases de documentos y gestionando operaciones CRUD. Para comenzar el proyecto, primero es necesario crear un entorno de desarrollo adecuado y definir los requisitos del proyecto.
Una vez configurado el entorno y los paquetes necesarios (FastAPI, Beanie, Pydantic, entre otros), se procederá a definir los modelos. En Beanie, los modelos de datos se crean heredando de la clase Document, que mapea las colecciones de MongoDB a clases Python. Estos modelos facilitan tanto la validación como el manejo de datos en la base de datos.
A continuación, se detallan algunos pasos iniciales para comenzar a trabajar con Beanie:
-
Crear una nueva carpeta de proyecto y configurar el entorno virtual.
-
Instalar las dependencias necesarias mediante un archivo
requirements.txt. -
Crear un archivo
.envpara gestionar variables de entorno y un.gitignorepara excluir archivos innecesarios.
Definición de los modelos con Beanie
El proceso de definir modelos con Beanie se inicia creando una clase que herede de Document. Esto permite mapear los documentos de MongoDB de forma eficiente. Los modelos en Beanie no solo se definen como clases tradicionales de Python, sino que también se benefician de la validación y características avanzadas que Pydantic ofrece.
Por ejemplo, el modelo de un User (usuario) se puede definir de la siguiente forma:
El campo created utiliza Field(default_factory=datetime.now) para establecer la fecha de creación por defecto. Además, la clase Settings permite especificar el nombre de la colección en MongoDB. También es posible agregar configuraciones adicionales como la validación al guardar los documentos.
En paralelo, se definen los modelos de validación con Pydantic, que se utilizan para registrar nuevos usuarios, iniciar sesión y recuperar información sobre el usuario actual:
El uso de PydanticObjectId facilita la conversión del tipo ObjectId de MongoDB a un formato compatible con Pydantic.
Además, al crear modelos para otras entidades, como el Car (coche), se debe definir cada uno de los campos relevantes para la colección de coches. Aquí se ejemplifica cómo sería la estructura de este modelo:
En este caso, se utiliza un enlace (Link[User]) para asociar un coche con un usuario específico. Esto muestra cómo Beanie permite referencias entre documentos sin necesidad de incrustar directamente los datos.
Consideraciones adicionales
Además de lo anterior, es importante que el lector comprenda que la integración de servicios de terceros, como OpenAI y servicios de envío de correos electrónicos, es un paso crucial para crear aplicaciones backend modernas y completas. El uso de FastAPI y Beanie proporciona la flexibilidad necesaria para integrar estos servicios sin comprometer el rendimiento.
Es fundamental también tener en cuenta las mejores prácticas para la gestión de datos sensibles, como las contraseñas de los usuarios, utilizando librerías de seguridad como bcrypt. Asimismo, la correcta gestión de CORS y el manejo de variables de entorno para la configuración de la aplicación son elementos esenciales para asegurar la escalabilidad y la seguridad de la aplicación.
¿Cómo integrar servicios externos en una aplicación con FastAPI y Beanie?
En el desarrollo de aplicaciones web modernas, una de las tareas más recurrentes es integrar servicios externos que permitan enriquecer la funcionalidad del backend. Un ejemplo clásico es el uso de APIs para realizar tareas complejas en segundo plano, como generar descripciones automáticas o enviar correos electrónicos a los usuarios. Este tipo de integración no solo mejora la eficiencia del desarrollo, sino que también optimiza la experiencia del usuario final. Este capítulo explora cómo hacerlo utilizando FastAPI, Beanie y servicios como OpenAI y Resend.
Para ilustrar esto, imagina que estamos trabajando en una aplicación de gestión de coches. En primer lugar, se tiene una operación que permite cargar una imagen de un coche a la nube, por ejemplo, utilizando Cloudinary. A continuación, se crea un registro en la base de datos utilizando Beanie, que es una biblioteca ODM (Object-Document Mapper) para MongoDB. Esta operación es tan sencilla como obtener los datos del coche (marca, modelo, año, etc.), cargar la imagen y almacenar toda esa información en la base de datos. Sin embargo, esta es solo una parte del proceso.
Una vez que el coche se ha insertado, se realiza una tarea en segundo plano para generar una descripción automática del coche usando la API de OpenAI. Esta descripción, junto con los pros y contras del modelo, se inserta en el documento del coche. Lo interesante aquí es que la generación de la descripción no bloquea la respuesta al usuario, gracias a la función background_tasks.add_task, que permite ejecutar tareas largas sin afectar la experiencia en tiempo real. Esta es una característica clave de FastAPI, que permite mantener la aplicación ágil y receptiva.
Sin embargo, es importante tener en cuenta que, en muchos casos, las respuestas de servicios externos como OpenAI pueden no ser perfectas. Por ejemplo, el modelo de lenguaje podría no generar una respuesta válida o completa. Además, no siempre se validan correctamente los parámetros proporcionados, como la marca y el modelo del coche. Por lo tanto, es esencial agregar validaciones y manejo de errores para garantizar que el sistema sea robusto y pueda manejar escenarios imprevistos.
Tras generar la descripción, el siguiente paso común en muchas aplicaciones es notificar a los usuarios. Para hacerlo, se puede integrar un servicio de envío de correos electrónicos, como Resend. Este servicio permite enviar correos electrónicos automáticos de forma sencilla, utilizando una API basada en REST. Al igual que con la integración de OpenAI, no es necesario que el proceso de envío de correos electrónicos bloquee la aplicación. La función send de Resend permite enviar correos electrónicos a los usuarios, notificándoles de nuevos productos o actualizaciones, como en el caso de un coche recién insertado en el sistema.
Lo importante aquí es entender cómo integrar estos servicios sin que el flujo principal de la aplicación se vea afectado. Por ejemplo, después de insertar un coche en la base de datos y generar su descripción, se puede enviar un correo electrónico a los usuarios informándoles sobre el nuevo modelo disponible, todo de manera asíncrona y sin que el sistema se detenga o pierda rendimiento.
Es fundamental comprender que la integración de estos servicios no es una tarea aislada. Requiere un manejo adecuado de las claves API, la validación de respuestas, la gestión de errores y la planificación de tareas en segundo plano. El objetivo no es solo hacer que la aplicación funcione, sino también garantizar que sea escalable, eficiente y fácil de mantener.
Además, en el desarrollo de aplicaciones modernas, es importante considerar las mejores prácticas de seguridad, como el uso de variables de entorno para almacenar las claves API y evitar que se expongan en el código. Asimismo, los desarrolladores deben estar al tanto de las limitaciones de los servicios externos y cómo manejar situaciones en las que no se obtiene una respuesta esperada.
Por último, aunque servicios como OpenAI y Resend facilitan enormemente el desarrollo, es importante también considerar alternativas o complementos. Por ejemplo, en lugar de usar OpenAI para generar descripciones, se podrían utilizar otras APIs de generación de contenido o incluso soluciones internas si se desea mayor control sobre el proceso. De igual manera, para el envío de correos, herramientas como Mailgun o SendGrid también son opciones populares y confiables.
¿Cómo podemos reclamar nuestro tiempo y alcanzar la verdadera libertad?
¿Cómo puede sobrevivir la democracia después de Trump?
¿Cómo identificar y abordar un “buen” problema en el diseño centrado en el usuario?
¿Cómo afectan las estructuras de conocimiento en el aprendizaje de los estudiantes?

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский