Gestionar la sesión de un usuario de forma eficiente es uno de los retos más comunes al construir aplicaciones con Angular. Aunque guardar el token en local storage es un primer paso, no basta para que toda la aplicación conozca el estado del usuario en tiempo real. Aquí se explica cómo crear un estado global reactivo que permite a cualquier componente, guardián o servicio saber si existe un usuario autenticado, sin repetir peticiones innecesarias al servidor.
¿Cómo crear un store global reactivo para el usuario?
El punto de partida es el store service, donde previamente se implementó reactividad para el carrito de compras usando un BehaviorSubject. La misma técnica se aplica para el usuario [01:40].
Se define un BehaviorSubject cuyo tipo es User | null, con un valor inicial de null, lo que representa que no hay ningún usuario autenticado. A partir de ese BehaviorSubject, se expone un observable público con el sufijo $ (convención para identificar observables), permitiendo que otros componentes se suscriban al estado del usuario sin poder modificarlo directamente [02:15].
Para nutrir ese estado, se utiliza el método getProfile dentro del auth service. Cada vez que se obtiene el perfil del servidor, se ejecuta un operador tap dentro del pipe. A diferencia de switchMap, que cambia de observable, tap simplemente ejecuta una acción lateral sin mutar la información ni cambiar el flujo. En este caso, esa acción es llamar a this.user.next(usuario) para dejar al usuario en el estado global [03:12].
¿Por qué usar tap en lugar de switchMap?
El operador switchMap se emplea cuando necesitas cambiar de un observable a otro. Si solo quieres realizar una acción secundaria —como guardar datos en un store— sin alterar el flujo del observable, tap es la opción correcta [04:00]. Esto mantiene el código limpio y semánticamente preciso.
¿Cómo se aprovecha el estado global en los guardianes?
En el auth guard, en lugar de verificar directamente si existe un token mediante el token service, ahora se consulta el observable del usuario [04:30]. El guardián retorna un observable que emite un booleano gracias al operador map:
- Si el usuario es
null, se ejecuta la redirección al home y se retorna false.
- Si existe un usuario, se retorna
true.
Esto es posible porque Angular permite que un guardián retorne un boolean, una Promise<boolean> o un Observable<boolean> [05:05].
¿Cómo evitar peticiones duplicadas de perfil?
Uno de los beneficios más importantes de este patrón es eliminar llamadas redundantes al endpoint de perfil. Antes, el componente de profile hacía su propio getProfile. Ahora, como el perfil ya se obtuvo durante el login (mediante el método loginAndGet), el componente simplemente se suscribe al estado global y obtiene la información sin hacer ninguna petición HTTP adicional [06:10].
En el componente de navegación también se aplica el mismo principio. En el ngOnInit, se suscribe al observable del usuario para actualizar la interfaz de forma reactiva cada vez que el estado cambie [07:00].
¿Qué pasa cuando el usuario recarga la página?
El BehaviorSubject inicia en null cada vez que la aplicación se carga, lo que significa que al recargar se pierde el estado. La solución se implementa en el app component, que es el punto de entrada de toda la aplicación [08:10].
En su ngOnInit, se inyectan el auth service y el token service. La lógica es sencilla:
- Se verifica si existe un token en local storage.
- Si existe, se ejecuta
getProfile una única vez.
- Como
getProfile tiene el tap que nutre el BehaviorSubject, todos los componentes suscritos reciben automáticamente al usuario [09:00].
De esta forma, el flujo completo funciona así:
- Login: se autentica, se obtiene el perfil y se guarda en el store reactivo.
- Recarga: el app component detecta el token y vuelve a pedir el perfil una sola vez.
- Logout: el estado vuelve a
null y los guardianes bloquean el acceso a rutas protegidas.
¿Cómo se verifica que todo funciona correctamente?
Al hacer login, el usuario es redirigido al profile y tanto la barra de navegación como el componente de perfil muestran la información sin peticiones extra [10:15]. Al hacer logout, el estado se limpia y los guardianes impiden el acceso a rutas protegidas, devolviendo al usuario al home [10:45]. Al recargar la página estando autenticado, el app component recupera la sesión y la aplicación continúa funcionando con normalidad [11:10].
Este patrón de estado reactivo con BehaviorSubject es fundamental para escalar aplicaciones Angular. En el siguiente paso, se extiende esta lógica para proteger rutas no solo por autenticación, sino también por rol de usuario, diferenciando entre administradores y clientes. ¿Ya has implementado este patrón en tus proyectos? Comparte tu experiencia.