Seguimos adentrándonos en las diferentes herramientas que nos ofrece Firebase para el almacenamiento en la nube. En este primer post vimos cuáles son esas herramientas y vimos en detalle la primera de ellas: Cloud Storage.

Ahora, en este segundo post, nos ocuparemos de ver las dos soluciones que nos ofrece Firebase para crear una base de datos y conectarla con nuestra app. Estas dos herramientas son Cloud Firestore y Realtime Database.

Cloud Firestore

¿Qué es?

Cloud Firestore es una base de datos NoSQL alojada en la nube, flexible y escalable y, al igual que Firebase Realtime Database, mantiene tus datos sincronizados entre apps cliente a través de objetos de escucha en tiempo real.

El modelo de datos de Cloud Firestore admite estructuras de datos flexibles y jerárquicas. Almacena tus datos en documentos, organizados en colecciones. Los documentos pueden contener objetos anidados complejos, además de subcolecciones.

En Cloud Firestore, puedes usar consultas para recuperar documentos individuales o todos los documentos de una colección que coinciden con los parámetros de la consulta. Tus consultas pueden incluir varios filtros en cadena y combinar los filtros con criterios de orden.

Cloud Firestore almacena en caché datos que usa tu app de forma activa, por lo que la app puede escribir, leer, escuchar y consultar datos, aunque el dispositivo se encuentre sin conexión. Cuando el dispositivo vuelve a estar en línea, Cloud Firestore sincroniza todos los cambios locales de vuelta a Cloud Firestore.

¿Para qué sirve?

Cloud Firestore te permite crear tu propia base de datos. En ella podrás almacenar todos los datos que necesites manejar en tu app ya que permite almacenar desde datos simples como strings, integer hasta objetos anidados complejos.

Desde tu app podrás acceder y gestionar esa base de datos que aportará más riqueza a tu proyecto.

¿Cómo usar Cloud Firestore en tu app?

NOTA: en este post vamos a mostrar cómo implementarlo en Android, pero también se puede usar en el resto de tecnologías, donde la implementación sería muy similar.

Antes de importarlo en tu proyecto, deberás habilitar Firestore Database dentro de tu proyecto en la consola de Firebase. Para ello, una vez dentro de tu proyecto, deberás entrar en Firestore Database y pulsar en Crear base de datos.

Al igual que con Cloud Storage, en Firestore Database también puedes configurar tus reglas de privacidad.

Nosotros, en este ejemplo, vamos a configurarlo público, con permisos abiertos tanto de escritura como de lectura. Para ello, configuraremos esta regla:

match /{document=**} {
      allow read
      allow write
}

Con esto ya tenemos lista nuestra base de datos Firestore Database en nuestro proyecto de Firebase.

Ahora, nos vamos a Android Studio y empezamos a usar Firestore Database en nuestra app. Lo primero es importar el módulo de Firebase a tu fichero build.gradle:

implementation platform('com.google.firebase:firebase-bom:29.1.0')

implementation 'com.google.firebase:firebase-firestore-ktx'

Antes de agregar datos a nuestra base de datos debemos saber que el modelo de datos se basa en colecciones, que contienen documentos.

Y dentro de estos documentos, podemos almacenar colecciones y datos. Los datos podrán ser del tipo: string, number, boolean, map, array, null, timestamp y geopoint.

Como ejemplo, vamos a crear una colección llamada users, donde almacenaremos la info de nuestros usuarios:

// 1 - Create dr reference
val db = FirebaseFirestore.getInstance()
// Create a new user with a first and last name
val user = hashMapOf(
        "first" to "Michael",
        "last" to "Miles",
        "born" to 1990
)

Ahora, vamos a ver dos formas de crear un documento dentro de la colección:

  1. La primera forma es usando la función add. Con esta función podremos añadir un documento dentro de nuestra colección y Firestore Database nos generará un ID de forma automática.
// Add a new document with a generated ID
db.collection("users")
    .add(user)
    .addOnSuccessListener { documentReference ->
        Log.d(TAG, "DocumentSnapshot added with ID: ${documentReference.id}")
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "Error adding document", e)
    }
  1. La segunda es usando la función set. Con esta función, deberemos nosotros definir el ID del documento previamente. Con set crearemos un nuevo documento o lo reemplazaremos si ya existía.
// Add a new document with a specific ID
db.collection("users").document("miles")
        .set(user)
        .addOnSuccessListener { 
Log.d(TAG, "DocumentSnapshot written with ID: ${documentReference.id}" 
        }
        .addOnFailureListener { e -> 
Log.w(TAG, "Error writing document", e) 
}

Ejecutando este código, desde nuestra app estaremos creando una nueva colección llamada “users” que dentro almacenará un documento cuyo ID será “miles” y que contendrá la información del usuario donde podremos ver su nombre, apellido y año de nacimiento.

Una vez ejecutado este código, si volvemos a la consola de Firebase, podremos ver lo siguiente:

Si posteriormente, queremos leer los datos guardados en nuestra base datos, lo podremos hacer de esta sencilla forma usando la función get:

db.collection("users")
        .get()
        .addOnSuccessListener { result ->
            for (document in result) {
                Log.d(TAG, "${document.id} =>      ${document.data}")
            }
        }
        .addOnFailureListener { exception ->
            Log.w(TAG, "Error getting documents.", exception)
        }

Con Firestore Database también podremos realizar consultas más complejas, que nos devuelvan exactamente la lista de datos que deseamos.

Por ejemplo, podemos realizar una consulta para obtener todos los usuarios que se llamen Michael usando la función whereEqualTo:

// Create a reference to the users collection
val usersRef = db.collection("users")

// Create a query against the collection.
val query = usersRef.whereEqualTo("first", "Michael")

Y también podemos realizar otra para obtener todos los usuarios que sean mayores de edad. Esta vez usaremos la función whereGreaterThanOrEqualTo:

// Create a reference to the users collection
val usersRef = db.collection("users")

// Create a query against the collection.
val query = usersRef.whereGreaterThanOrEqualTo
("born", "2004")

Para ejecutar las consultas, añadiremos la llamada a get() después de especificar la query que queremos lanzar. Además, si lo necesitas, puedes anidar varias queries antes de ejecutar la consulta.

val usersRef = db.collection("users")
.whereEqualTo("first", “Michael")
.whereGreaterThanOrEqualTo("born", "2004")
.get()

Con esta última consulta, estaremos obteniendo todos los usuarios registrados en nuestra base de datos que se llamen Michael y tengan al menos 18 años.

Con Firestore Database también podemos actualizar los datos previamente almacenados. Para ello usaremos la función update.

//Create a reference to the document
val milesRef = db.collection("users").document("miles")

// Set the "born" field of the user "miles"
milesRef.update("born", 1991)
        .addOnSuccessListener { 
Log.d(TAG, "DocumentSnapshot successfully updated!") }
        .addOnFailureListener { e -> 
Log.w(TAG, "Error updating document", e) 
        }

Firebase Realtime Database

¿Qué es?

Firebase Realtime Database es una base de datos alojada en la nube. Los datos se almacenan en formato JSON y se sincronizan en tiempo real con cada cliente conectado.

En lugar de solicitudes HTTP típicas, Firebase Realtime Database usa la sincronización de datos, de forma que cada vez que cambian los datos, los dispositivos conectados reciben esa actualización en milisegundos.

Los datos persisten de forma local. Además, incluso cuando no hay conexión, se siguen activando los eventos en tiempo real, lo que proporciona una experiencia adaptable al usuario final. Cuando el dispositivo vuelve a conectarse, Realtime Database sincroniza los cambios de los datos locales con las actualizaciones remotas que ocurrieron mientras el cliente estuvo sin conexión, lo que combina los conflictos de forma automática.

¿Para qué sirve?

Firebase Realtime Database, como vimos anteriormente te permite crear una base de datos simple, en la que los datos son estructurados en formato JSON.

Si para tu app necesitas una conjunto de datos simples, y necesitas la capacidad de poder acceder a ellos para según esos datos modificar algunos parámetros de tu app, por ejemplo un fichero de configuración, Firebase Realtime Database será tu solución ideal.

¿Cómo usar Firebase Realtime Database en tu app?

NOTA: en este post vamos a mostrar cómo implementarlo en Android, pero también se puede usar en el resto de tecnologías, donde la implementación sería muy similar.

Antes de importarlo en tu proyecto, deberás habilitar Firebase Realtime Database dentro de tu proyecto en la consola de Firebase. Para ello, una vez dentro de tu proyecto, deberás entrar en Firebase Realtime Database y pulsar en Crear base de datos.

Al igual que con Cloud Storage y Firestore Database, en Firebase Realtime Database también puedes configurar tus reglas de privacidad.

Nosotros, en este ejemplo, vamos a configurarlo como público, con permisos tanto de lectura como de escritura. Para ello, configuraremos esta regla:

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Con esto ya tenemos lista nuestra base de datos Firebase Realtime Database en nuestro proyecto de Firebase.

Ahora, nos vamos a Android Studio y empezamos a usar Firebase Realtime Database en nuestra app. Lo primero es importar el módulo de Firebase a tu fichero build.gradle:

implementation platform('com.google.firebase:firebase-bom:29.1.0')

implementation 'com.google.firebase:firebase-database'

Antes de escribir datos en nuestra base de datos, tenemos que saber qué tipos de datos podemos almacenar en ella. Estos tipos son: string, long, double, boolean, map y list.

Además, tenemos que tener en cuenta que si tenemos en nuestra app un objeto Java/Kotlin, el contenido del objeto se asignará automáticamente a las ubicaciones secundarias de forma anidada.

Para el ejemplo contaremos con este objeto User:

data class User(val first: String? = null, val last: String? = null, val born: Long? = null) {
    // Null default values create a no-argument default constructor, which is needed
    // for deserialization from a DataSnapshot.
}

Para agregar un usuario a nuestra base de datos, haremos lo siguiente:

// Create a reference to the database
private lateinit var databaseRef: DatabaseReference
// ...
databaseRef = FirebaseDatabase.getInstance().reference

// Create a user
val user = User("Michael", "Miles", 1990)

// Put value into database where miles will be the ID
databaseRef.child("users").child("miles")
.setValue(user)

A la operación de escritura, al igual que vimos antes con Cloud Firestore, también podemos añadirle un listener para escuchar el resultado:

.addOnSuccessListener {
  // Write was successful!
  // ...
}.addOnFailureListener {
  // Write failed
  // ...
}

Tras ejecutar este código, si echamos un vistazo a nuestra consola de Firebase y entramos en Remote Config, veremos algo así:

En la misma consola de Firebase, podemos descargar el JSON pulsando en los 3 puntos de arriba a la derecha - Exportar JSON. Si lo hacemos, veremos que el JSON obtenido es:

{
  "users" : {
    "miles" : {
      "born" : 1990,
      "first" : "Miles",
      "name" : "Michael"
    }
  }
}

Para leer los datos que tenemos en nuestra base de datos Realtime, tenemos varias opciones según cómo y cuándo queremos leer esos datos.

  1. Si queremos leer datos en tiempo real y además detectar los posibles cambios que se realicen a futuro, tendremos que usar el método addValueEventListener.
// 1 - First, create the listener
val postListener = object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        // Get Post object and use the values to update the UI
        val post = dataSnapshot.getValue<Post>()
        // ...
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
    }
}

// 2 - Finally, add the listener
databaseRef.addValueEventListener(postListener)
  1. Si necesitas leer los datos sólo una vez, por ejemplo en el acceso a determinada pantalla de la app, pero no necesitas detectar los cambios que se vayan produciendo en tiempo real, puedes usar el método get.
databaseRef.child("users").child("miles")
.get()
.addOnSuccessListener {
    Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
    Log.e("firebase", "Error getting data", it)
}}
  1. Si necesitas leer unos datos sólo una vez y no esperas que cambien con frecuencia, ni que necesiten escucha activa, puedes usar el método addListenerForSingleValueEvent.

Con este método recuperarás el valor de la caché local, en lugar de buscar un valor actualizado en el servidor.

// 1 - First, create the listener
val postListener = object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        // Get Post object and use the values to update the UI
        val post = dataSnapshot.getValue<Post>()
        // ...
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
    }
}

// 2 - Finally, add the single value listener
databaseRef.addListenerForSingleValueEvent(postListener)

Si queremos actualizar el valor de nuestro objeto, podemos hacerlo de una forma muy sencilla, ya que lo podemos hacer sin tener que enviar todo el objeto completo.

Nos bastaría con ejecutar el siguiente código:

// Update the year birth from 1990 to 1991
databaseRef.child("users").child("miles").child("born")
.setValue(1991)

Hasta aquí hemos visto cómo escribir, leer y actualizar datos en nuestras bases de datos, tanto en Cloud Firestore como en Realtime Database, pero, como te puedes imaginar, las posibilidades que nos proporcionan ambas son mucho mayores.

En este post sólo hemos querido mostrar cómo empezar a trabajar con ellas para romper esa barrera inicial. Pero sin duda, son dos herramientas muy potentes de Firebase en las que merece la pena profundizar para sacar mayor rendimiento a tu app.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.