Pinche mundo de promesas…

Héctor BlisS
FixterGeek
Published in
7 min readMar 19, 2019

Cómo crear tu propio 'populate' para Firebase Firestore en documentos relacionados.

No, estas no.

No, este post no es sobre esas promesas que te hacen en tu relación, o las promesas que le haces a tus hijos y no cumples si, el mundo está llenos de esas falsas promesas, pero no es la ocasión, en este post quiero hablar sobre las promesas con las que nos acostumbramos a trabajar cuando ocupamos la base de datos de Firebase en este caso la más moderna y estable que es Firestore.

The problem?

Bueno pensemos que tenemos un par de colecciones en Firestore, como son Libros y Autores, y obviamente están relacionados, un autor tiene varios libros publicados, y por ahora no nos metamos en problemas de niveles de normalización, ni tampoco en el tipo de relación (1–1, 1-many,many-many), simplemente digamos que un autor publico ya varios libros, y tendríamos algo así con nuestros autores:

Los autores tienen un array de libros

Ahora veamos nuestra colección de libros:

Los libros podrían tener una llave que apunte al autor

Aquí ya tenemos un dilema y debemos tomar una decisión, y en el mundo del desarrollo web, un programador esta enfrentado todo el tiempo a tomar este tipo de pequeñas decisiones de diseño que por mas pequeñas que parezcan, en el futuro seguro se convertirán en monstruos enormes que nos harán arrepentirnos de ellas. aún así debemos tomarlas, a que me refiero?, bueno.

Debemos crear la relación entre los autores y los libros, pero debemos decidir si el autor guarda una colección de libros, (array) o si el libro guarda una llave con el id del autor, (incluso puede ser una array de autores, no estamos preocupado ahora por el tipo de ralción si no de quién tendrá memoria de ella), el autor o el libro?, siempre será más económico para nuestra base de datos evitar los arrays que puedan tender a convertirse en una lista gigantesca, aún que nuestros autores no tengan increíbles habilidades y no publiquen miles de libros, siempre es mejor evitar esta tendencia, aunque la forma más cómoda de trabajar cómo desarrollador es tener una doble relación, (si evitar la decisión XD ) y crear tanto la llave en el libro, como el array en el autor, con esto podemos decidir más tarde cuál nos conviene más. Así que es hora de copiar el ID del autor y ponérselo al libro, así cómo copiar el ID del libro y ponerlo en el array del autor:

Pero esto es simplemente un String

Podemos poner un String y después arreglarnolas con este dato y pedirlo a la base de datos a partir de una referencia hecha a mano, pero porqué no aprovechar de una vez la función de las referencias de Firestore?

Es importante agregar el nombre de la colección en la ruta

Hay que asegurarnos de crear las dos relaciones de la misma manera, recordando que en el libro es una llave valor, mientras que en el autor es un array.

Recordemos colocar el nombre de la colección en la ruta de la referencia

Ok. Tenemos relacionados nuestro libro y nuestro autor, es hora de intentar consumirlo desde un archivo JS.

How to get both documents with one service function call?

Vamos a crear un servicio, esto para poder usar este archivo completamente independiente de que framework vayamos a ocupar, esto no es un tutorial especifico, este snipet de código vamos a poder utilizarlo en cualquier herramientas es meramente JS.

Es importante recordar que en este punto ya tienes instalada la librería oficial de Firebase, ya sea en el html o con npm y webpack, cómo sea el caso, ya podemos usar firebase porque está instalado y tus rules en la DB están en modo desarrollo o prueba, para poder leer sin iniciar sesión ok?

Vamos a trabajar en esta función 'getAutoresAndLibros' toda pocha, porque este post es en español y mis colecciones también, pero usen sus buenas prácticas y pónganlo en ingles ;) así pues entiéndase qué, voy a pedir los autores, estos traen un array de libros, pero este array contiene referecias, significa que la información del libro como tál no esta disponible, (su titulo) hasta que sea intercambiado por información de verdad, y justo para eso nos sirve la referencia, el problema comienza cuando debemos concatenar un pinche mundo de promesas.

Comencemos utilizando nuestra "autoresRef" y pidamos los autores y observemos:

Observemos el console.log, tenemos 1 autor con un array de libros, pero el indice 0 no es un libro

El indice 0 de nuestro array de libros de nuestro autor, no es un libro, es una referencia a un libro, entendamos que si es una referencia de Firestore, entonces se comporta de igual forma que cualquier referencia que podamos crear directamente (justo cómo autoresRef), así que en base a esto continuemos, recorriendo nuestro array de referencias y pidamos que se transformen en objetos con un .get()

Antes de agregar el autor a el array de autores, recorremos su array de referencias

Estamos convirtiendo su referencia en una petición de datos a Firestore, el problema es que estamos almacenando promesas, pues cada una tomará un tiempo en resolverse.

Libros ahora es un array de promesas

Si observamos, casi lo logramos, pero en lugar de tener un array de objetos libro, ahora tenemos un array de promesas, sí, conseguimos sustituir las referencias por otra cosa, pero no son promesas lo que necesitamos para mostrar la info, si no, objetos. Como resolvemos esto?

Sí ya sé, "Aquí podríamos usar Async Await y no complicarnos la vida con promesas", pero na, en realidad esto es trabajar con las mismas promesas pero de una forma no identada, personalmente no soy fan de async await, y aún utilizándolo tendríamos que pensar en promesas porque es como Firebase trabaja.

Así que mejor pensemos en dividir y conquistar, y agreguemos una hermosa función que haga algo en especifico.

Creamos una función llamada getLibrosFromPromises y se encarga básicamente de recibir un autor, pedir sus libros con la ayuda de la clase Promise y devolver un autor pero con el array de libros (promesas) sustituido por los resultados al resolverlos todos.

Upss, tenemos ahora un array de promesas en vez de un array de autores D= pero esto no es malo, al contrarío, ahora que nuestro autor es una promesa, pues podemos utilizar la clase Promise una vez más para esperar la resolución de TODOS nuestros autores:

Un array de autores, con un array de libros cada uno.

Wow!, raro?, sip. Pero este ejercicio también nos ayuda a entender las promesas y cómo concatenar los .then, el truco está en concatenar .then hasta poder devolver un objeto o un array desde la función principal que es getAutoresAndLibros.

Ahora, tenemos un servicio que devuelve un autor con todo y sus libros, pero tendríamos que escribir esto para cada uno de nuestros modelos que este relacionado, =S y si transformamos nuestras funciones en funciones dinámicas que sean reciclables?

Separemos nuestro código en 3 funciones principales, según lo que hizimos en nuestra función getAutoresAndLibros: 1. pedimos los documentos de cierta colección, y los recorrimos pidiendo su .data() 2. sabemos que esos documentos tienen un array de referencias y las recorrimos también solicitando los datos con .get() 3. convertimos esas promesas esperando los resultados con Promise.all, tanto con los libros o subdocumentos como con los documentos o autores, así que hagamos 3 funciones que sean dinámicas para cada paso, y cambiemos los nombres por nombres reciclables, dejemos de hablar de libros y autores y hablemos de documentos y keys:

Nota que cambiamos todo lo especifico, por un par de nombres genericos, cómo key y doc, y ahora tenemos una función populate que solo necesita el snap, de una colección más el nombre de la llave a popular, y haciendo uso de Promise.all y un par de minihacks, devolvemos una colección de documentos populados.

Posiblemente necesites un par de minutos para leer el código y haga completo sentido, pero una vez que lo consigas concordarás conmigo de que las concatenaciones de promesas (return, then) son geniales para esperar la resolución de las mismas y solo devolver hasta el final, justo lo que necesitamos.

Espero abordar este tema más adelante en otro post, acercándonos por el lado de los libros y su llave autor, en vez del array de libros del autor, pero por ahora espero esto te sea útil para consumir documentos relacionados con Firebase, inténtalo y cuéntame en los comentarios cómo te fue. Un abrazo.

--

--

Héctor BlisS
FixterGeek

Soy de la generación que jugó con Atari, se conecto a internet con dial-up mientras mi mamá me regañaba por usar la linea, creo que soy millennial. =P