Firebase Realtime Database es una base de datos JSON en la nube en tiempo real lanzada por Google en 2012 para aplicaciones móviles y web. Todos los datos se almacenan en un único árbol JSON grande y se sincronizan entre los clientes conectados en tiempo real a través de una conexión WebSocket. Según la documentación oficial Firebase, 2025, Realtime Database puede manejar hasta 200 000 conexiones simultáneas y admite hasta 1000 escrituras concurrentes por segundo. La base de datos no requiere infraestructura de servidor y proporciona SDK para iOS, Android, Web y plataformas de servidor.
Puntos clave
Firebase Realtime Database es una base de datos NoSQL en la nube que almacena y sincroniza datos en tiempo real entre todos los clientes conectados. Lanzada en 2012 como Firebase (antes de la adquisición por Google), se convirtió en la primera base de datos en la nube en tiempo real para desarrolladores móviles. Los datos se representan en formato JSON y se organizan en un árbol jerárquico, donde cada nodo tiene una ruta única.
El valor principal de Realtime Database es la sincronización incorporada. Cuando una aplicación cambia datos en cualquier dispositivo, todos los demás clientes conectados reciben instantáneamente la actualización a través de una conexión persistente. Esto elimina la necesidad de que los desarrolladores implementen su propio mecanismo de sincronización, servidor WebSocket o API REST para la transferencia de datos entre clientes.
La base de datos proporciona SDK para todas las plataformas principales: Android (Java, Kotlin), iOS (Swift, Objective-C), Web (JavaScript) y entornos de servidor a través de Admin SDK. Según Google, Realtime Database se utiliza en más de 1,5 millones de proyectos activos de Firebase en todo el mundo. A pesar de la aparición de Firestore, más moderna, Realtime Database sigue siendo una opción popular para proyectos con estructuras de datos simples.
A diferencia de las bases de datos relacionales, Realtime Database no utiliza tablas ni filas. Todos los datos son un único árbol JSON que se asemeja a objetos JavaScript anidados. Por ejemplo, para almacenar usuarios y sus mensajes, se crea una jerarquía: users/userId/name y messages/messageId/text. Cada ruta en el árbol es una cadena, y se puede acceder a los datos directamente por esta ruta.
{
"users": {
"user1": {
"name": "Juan Petrov",
"email": "ivan@example.com"
},
"user2": {
"name": "María Sokolova",
"email": "maria@example.com"
}
},
"messages": {
"-Nabc123": {
"text": "¡Hola!",
"userId": "user1"
}
}
}
Una característica importante es que el anidamiento profundo afecta el rendimiento. Cuando una aplicación lee datos en una determinada ruta, carga todos los nodos secundarios de esa ruta. Por lo tanto, se recomienda diseñar la estructura de datos lo más plana posible, evitando anidamientos de más de 3-4 niveles. Para solucionar este problema, se utiliza la desnormalización de datos: duplicar información en diferentes nodos del árbol.
Realtime Database y Firestore se comparan a menudo como dos bases de datos en la nube en tiempo real de Google. La elección entre ellas depende de los requisitos específicos del proyecto: complejidad de las consultas, consistencia requerida y carga esperada. Comprender las fortalezas de cada base de datos ayuda a tomar la decisión arquitectónica correcta.
La principal ventaja de Realtime Database es la baja latencia de sincronización. Dado que todos los datos se almacenan en un único árbol JSON sin capas de abstracción adicionales, la sincronización es más rápida que en Firestore. Para aplicaciones donde la velocidad de entrega de actualizaciones es crítica (chats, juegos en línea, sistemas de edición colaborativa), Realtime Database puede ser la opción más adecuada.
Realtime Database es más adecuada para escenarios con estructuras de datos simples y alta frecuencia de actualizaciones. Ejemplos típicos: chats, me gusta en tiempo real, indicadores de escritura, estados de presencia de usuarios. También es una buena opción para prototipos y proyectos con presupuesto limitado, ya que el precio se basa en el volumen de datos, no en el número de operaciones.
Por otro lado, para aplicaciones con consultas complejas (filtrado por múltiples campos, ordenación, agregación), Firestore ofrece capacidades mucho más potentes. Realtime Database solo admite filtrado por un parámetro y no puede ordenar resultados por múltiples campos simultáneamente. Si un proyecto planea análisis de datos complejos en el cliente, Firestore será una opción más práctica.
Realtime Database utiliza una conexión WebSocket persistente para la sincronización bidireccional de datos. Cuando un cliente llama a setValue o updateChildren en una ruta específica, los datos se envían al servidor de Firebase a través del canal abierto. El servidor aplica los cambios y distribuye las actualizaciones a todos los clientes suscritos en milisegundos. Cada conexión se identifica mediante una clave de sesión única.
El mecanismo de suscripción funciona a través de listeners. Un desarrollador puede suscribirse a cambios en un nodo específico (addListenerForSingleValueEvent) o recibir actualizaciones continuas (addValueEventListener). Cada vez que los datos cambian, se activa el callback onDataChange con una instantánea completa de los datos en la ruta especificada. Esto difiere de Firestore, donde solo se reciben los documentos modificados — en Realtime Database siempre se cargan todos los datos del nodo.
Realtime Database admite el modo sin conexión en Android e iOS mediante caché en disco. El SDK mantiene una copia local de los datos y continúa procesando operaciones de escritura cuando no hay red. Cuando se restablece la conexión, todos los cambios acumulados se envían al servidor. Se utiliza la estrategia de última escritura ganadora para la resolución de conflictos, pero los desarrolladores pueden implementar lógica personalizada a través de ServerValue.TIMESTAMP para la resolución de colisiones.
val database = FirebaseDatabase.getInstance()
val myRef = database.getReference("messages")
// Escritura de datos
myRef.push().setValue(
hashMapOf(
"text" to "Nuevo mensaje",
"timestamp" to ServerValue.TIMESTAMP
)
)
// Lectura con actualizaciones continuas
myRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val data = snapshot.getValue()
Log.d("TAG", "Datos: $data")
}
override fun onCancelled(error: DatabaseError) {
Log.w("TAG", "Error: ${error.message}")
}
})
Para optimizar el tráfico y el rendimiento, se recomienda usar child listeners en lugar de value listeners al rastrear cambios en nodos secundarios específicos. ChildEventListener proporciona callbacks separados para agregar, modificar, eliminar y mover elementos secundarios, lo que permite un control más preciso de las actualizaciones de la interfaz y evita redibujar todos los elementos de la lista en cada cambio de datos.
Realtime Database utiliza un lenguaje de reglas declarativo para el control de acceso a datos. Las reglas describen quién puede leer y escribir datos en cada ruta del árbol JSON. Se verifican en el servidor de Firebase antes de cada solicitud y no requieren lógica de servidor para la autorización. Las reglas admiten variables, objetos incorporados y funciones para una configuración de acceso flexible.
Por defecto, el acceso a la base de datos está denegado para todos los usuarios. El desarrollador abre gradualmente el acceso usando las reglas ".read" y ".write" en varios niveles del árbol. Las condiciones pueden verificar la autenticación mediante la variable auth, el tipo de solicitud (lectura/escritura) y los datos existentes a través del objeto data. Además, las reglas admiten la validación de datos escritos mediante el objeto newData.
{
"rules": {
"users": {
"$uid": {
// Solo el propietario puede leer sus datos
".read": "$uid === auth.uid",
// Solo el propietario puede escribir
".write": "$uid === auth.uid",
// Validación de campos al escribir
".validate": "newData.hasChildren(['name', 'email'])"
}
},
"messages": {
// Cualquier usuario autenticado puede leer
".read": "auth !== null",
// Solo usuario autenticado puede escribir
".write": "auth !== null",
".indexOn": ["timestamp"]
}
}
}
Las reglas también admiten la indexación de datos mediante la directiva ".indexOn". Sin ella, las consultas con ordenación (orderByChild) serán rechazadas o se ejecutarán de manera ineficiente. Los índices se especifican para cada ruta donde se realiza la ordenación por un campo específico. Las reglas son en cascada: las reglas más profundas anulan las reglas padre, y si el acceso no está definido en algún nivel, se considera permitido o denegado según la regla padre.
Realtime Database admite cinco tipos de datos: String, Number, Boolean, Map (objeto) y List (matriz). La profundidad de anidamiento está limitada a 32 niveles, y el tamaño máximo de un solo nodo no debe exceder los 256 MB. Para un trabajo eficiente con la base de datos, se recomienda diseñar una estructura de datos plana y usar desnormalización para evitar consultas profundas que carguen grandes cantidades de datos.
Consideremos un ejemplo práctico de integración de Realtime Database en una aplicación Android para estados de usuario (en línea/fuera de línea). La aplicación mostrará una lista de usuarios con su estado actual, actualizado en tiempo real. Para la demostración se utilizan Firebase Authentication para la identificación de usuarios y corrutinas para operaciones asíncronas.
Para empezar, añada la dependencia firebase-database-ktx al archivo build.gradle del módulo de la aplicación. La versión de la biblioteca se gestiona mediante Firebase BoM para garantizar la compatibilidad de todos los componentes. Después de añadir la dependencia, Firebase debe inicializarse en la clase Application o mediante inicialización perezosa en el ViewModel.
dependencies {
implementation platform("com.google.firebase:firebase-bom:33.0.0")
implementation "com.google.firebase:firebase-database-ktx"
implementation "com.google.firebase:firebase-auth-ktx"
}
Después de la configuración, se crea un repositorio para trabajar con usuarios. Cada usuario está representado por un nodo en el árbol /users/{uid} con los campos name, email y status. Para el seguimiento del estado se utiliza onDisconnect, un mecanismo especial de Firebase que ejecuta automáticamente una operación de escritura cuando se interrumpe la conexión del cliente. Esto garantiza que el estado del usuario cambie a "offline" al cerrar la aplicación o perder la red sin necesidad de código adicional en el cliente.
class PresenceRepository {
private val database = FirebaseDatabase.getInstance()
private val auth = FirebaseAuth.getInstance()
private val presenceRef = database
.getReference("presence")
fun trackPresence() {
val uid = auth.currentUser?.uid ?: return
val userRef = presenceRef.child(uid)
userRef.onDisconnect().setValue("offline")
userRef.setValue("online")
}
fun getPresenceStream(): Flow<Map<String, String>> =
presenceRef.snapshotFlow()
.map { snapshot ->
(snapshot.value as? Map<*, *>)
?.mapKeys { it.key.toString() }
?.mapValues { it.value.toString() }
?: emptyMap()
}
}
El elemento clave del ejemplo es onDisconnect. Este mecanismo permite establecer una operación de escritura que se ejecutará en el servidor cuando se interrumpa la conexión del cliente. En este caso, cuando el usuario se desconecta, su estado se establece automáticamente en "offline" sin necesidad de manejar el evento de cierre de la aplicación. Si la aplicación falla, Firebase mismo ejecutará la operación onDisconnect y otros usuarios verán el estado correcto.
Preguntas frecuentes
Realtime Database almacena datos en un único árbol JSON y proporciona una latencia de sincronización más baja. Firestore utiliza colecciones de documentos, admite consultas complejas y consistencia fuerte. Realtime Database es mejor para chats simples y estados, Firestore es mejor para aplicaciones con estructuras de datos complejas y análisis.
El tamaño máximo de un solo nodo de Realtime Database es de 256 MB. La profundidad de anidamiento está limitada a 32 niveles. Para un proyecto de Firebase, se pueden crear múltiples instancias de Realtime Database (hasta 5 en el plan Spark y hasta 100 en el plan Blaze), lo que permite distribuir datos entre diferentes instancias.
Realtime Database se integra con Firebase Authentication. La variable auth que contiene el uid del usuario autenticado está disponible en las reglas de seguridad. Los desarrolladores pueden restringir el acceso a nivel de nodos individuales del árbol JSON verificando el uid del propietario de los datos. Los usuarios anónimos y no autenticados tienen auth = null.
Sí, Realtime Database admite transacciones a través del método runTransaction. Una transacción garantiza la atomicidad de la operación de lectura-modificación-escritura para un solo nodo. Cuando ocurren cambios concurrentes, la transacción se reintenta con datos actualizados. Esto es útil para contadores, calificaciones y otros escenarios donde la consistencia de los datos es importante.
Sí, Realtime Database admite el modo sin conexión en Android e iOS. El SDK almacena en caché los datos localmente y continúa procesando operaciones de escritura sin red. Cuando se restablece la conexión, todos los cambios acumulados se sincronizan con el servidor. Para habilitar el modo sin conexión, use el método keepSynced(true) en el nodo deseado.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también