Curso de Firebase con Angular 20

Integración de Firestore en Angular para guardar mensajes de chat

Curso de Firebase con Angular 20

Integración de Firestore en Angular para guardar mensajes de chat

Resumen

Implementa un chat confiable con Firestore en Angular: guarda mensajes y conversaciones, reemplaza mocks por un servicio real y ajusta reglas de seguridad. Aquí verás, paso a paso, cómo usar addDoc, query con where, suscripciones con onSnapshot y manejo de Observable para obtener un flujo reactivo y ordenado por fecha desde el frontend.

¿Cómo integrar FirestoreService para guardar mensajes y conversaciones?

Para centralizar el guardado y la lectura, se crea un FirestoreService que inyecta Firestore y expone métodos claros. Se validan datos mínimos del modelo de mensaje de chat: usuario, contenido y tipo. Si falta algo, se rechaza la promesa y se muestra el error en el chat. Cuando todo está ok, se toma la colección de mensajes y se usa addDoc con la estructura que espera Firebase.

  • Validaciones obligatorias: usuario, contenido y tipo.
  • Colecciones: mensajes y conversaciones.
  • Errores: se capturan con catch y se propagan al componente.
  • Ordenamiento: por fecha en el frontend, tras recibir los documentos.

¿Qué valida guardar mensaje?

Se confirma que el mensaje tenga los campos esenciales. Luego se mapea al formato esperado por Firebase y se almacena con addDoc en la colección de mensajes.

// FirestoreService async guardarMensaje(mensaje: MensajeChat): Promise<void> { if (!mensaje.usuario || !mensaje.contenido || !mensaje.tipo) { throw new Error('Datos de mensaje incompletos'); } const ref = collection(this.firestore, 'mensajes'); const data = { usuario: mensaje.usuario, contenido: mensaje.contenido, tipo: mensaje.tipo, // 'usuario' | 'asistente' | 'error' fecha: mensaje.fecha, }; await addDoc(ref, data); }

¿Cómo obtener mensajes por usuario con observable?

Se construye una query con where para filtrar por el ID de usuario. Se usa onSnapshot para escuchar cambios en tiempo real y emitirlos a través de un Observable. Los mensajes se mapean al modelo de chat y se ordenan por fecha en el frontend para asegurar consistencia.

obtenerMensajesDeUsuario(usuarioId: string): Observable<MensajeChat[]> { return new Observable((observer) => { const ref = collection(this.firestore, 'mensajes'); const q = query(ref, where('usuario', '==', usuarioId)); const unsubscribe = onSnapshot(q, (snap) => { const mensajes = snap.docs.map(d => d.data() as MensajeChat) .sort((a, b) => a.fecha - b.fecha); observer.next(mensajes); }, (error) => { observer.error(error); }); return () => unsubscribe(); }); }

¿Cómo se guardan conversaciones completas?

Además de los mensajes individuales, se guardan conversaciones con sus metadatos: usuario, título y los mensajes incluidos. Se usa la misma mecánica: referencia a la colección y addDoc, gestionando errores en el catch.

async guardarConversacion(conv: Conversacion): Promise<void> { const ref = collection(this.firestore, 'conversaciones'); await addDoc(ref, { usuario: conv.usuario, titulo: conv.titulo, mensajes: conv.mensajes, fecha: conv.fecha, }); }

¿Cómo reemplazar el mock por FirestoreService en el chat?

El ChatService pasa de datos falsos a datos reales. Se inyecta el FirestoreService y se reemplazan, uno por uno, los mocks en inicialización y envío de mensajes. Al suscribirse al observable de mensajes por usuario, cada cambio ejecuta las acciones en los componentes que dependen de ese flujo.

  • Inicializar chat: suscripción a obtener mensajes por usuario.
  • mensajeSubject: emite el array recibido para actualizar la UI.
  • carga de historial: se marca en false cuando llegan los datos.
  • Errores: se muestran y se emite un array vacío.

// ChatService (fragmentos) async inicializarChat(usuarioId: string) { this.firestoreService.obtenerMensajesDeUsuario(usuarioId).subscribe({ next: (mensajes) => { this.mensajeSubject.next(mensajes); this.cargandoHistorial = false; }, error: (e) => { console.error(e); this.mensajeSubject.next([]); } }); } async enviarMensaje(mensajeUsuario: MensajeChat) { await this.firestoreService.guardarMensaje(mensajeUsuario); // tipo: 'usuario' // Al recibir respuesta del asistente o un error, también se guarda. await this.firestoreService.guardarMensaje(mensajeAsistente); // tipo: 'asistente' // En caso de fallo de Gemini, guardar mensaje de 'error'. }

  • Tipos de mensaje: usuario, asistente y error.
  • Sustitución total del mock: se activa la inyección, se importan dependencias y se borran líneas simuladas.

¿Cómo ajustar reglas de Firestore y validar el flujo end-to-end?

En la consola de Firebase, en Firestore Database > rules, se pegan reglas que permiten acceder a documentos y modificar mensajes para usuarios autenticados en modo test. Se guarda con Publish o con control S. Luego se levanta la app con npm start, se inicia sesión con Google y se verifica la creación de la colección de mensajes y la persistencia.

  • Autenticación con Google: la app toma la imagen y el ID del usuario.
  • Persistencia: tras refrescar, se recuperan los mensajes por coincidencia de usuario en el where.
  • Tipos visibles en la data: 'usuario' para el emisor y 'asistente' para la respuesta.
  • Colecciones generadas: mensajes y, cuando corresponda, conversaciones.

Buenas prácticas reforzadas en el proceso: - Uso de Observable para reactividad en la UI. - onSnapshot para escuchar cambios y unsubscribe para limpiar suscripciones. - Ordenamiento por fecha en frontend para consistencia de la conversación. - Manejo de errores consistente con promesas y catch en componentes.

¿Te gustaría ver el patrón para paginar mensajes o mejorar el ordenamiento con índices? Comenta qué parte quieres profundizar y qué retos encuentras al integrar Firestore en tu chat.