IndexedDB: Base de Datos Indexada en el Navegador
Aprende a crear y usar bases de datos persistentes en el navegador con IndexedDB, ideal para almacenar grandes cantidades de datos estructurados.
TL;DR - Resumen rápido
- IndexedDB es una base de datos NoSQL asíncrona en el navegador
- Permite almacenar grandes cantidades de datos estructurados
- Usa almacenes (object stores) para organizar datos por tipo
- Las transacciones garantizan consistencia de datos
- Es más potente que localStorage para datos complejos
Introducción a IndexedDB
IndexedDB es una API de almacenamiento de bajo nivel que permite guardar grandes cantidades de datos estructurados en el navegador. A diferencia de localStorage, IndexedDB es una base de datos completa con indexación, transacciones y consultas eficientes, ideal para aplicaciones que necesitan almacenar datos complejos o grandes volúmenes de información.
IndexedDB es asíncrona y orientada a eventos, lo que la hace adecuada para aplicaciones modernas que no deben bloquear el hilo principal. Soporta múltiples tipos de datos, incluyendo objetos, arrays, blobs y files, y permite crear índices para búsquedas rápidas sin necesidad de escanear todos los datos.
- Base de datos NoSQL asíncrona y orientada a eventos
- Capacidad de almacenamiento mucho mayor que localStorage (50MB+)
- Soporta múltiples tipos de datos: objetos, arrays, blobs, files
- Permite crear índices para búsquedas eficientes
- Usa transacciones para garantizar consistencia de datos
Asincrona y No Bloqueante
IndexedDB es completamente asíncrona y no bloquea el hilo principal del navegador. Esto es fundamental para aplicaciones que necesitan mantener una UI fluida mientras realizan operaciones pesadas de base de datos.
¿Qué es IndexedDB?
IndexedDB es una base de datos transaccional integrada en el navegador que permite almacenar cantidades significativas de datos estructurados. Es una API de bajo nivel que proporciona más control y flexibilidad que localStorage, pero con una curva de aprendizaje más pronunciada debido a su API basada en eventos y callbacks.
A diferencia de las bases de datos SQL tradicionales, IndexedDB es una base de datos NoSQL que almacena datos como pares clave-valor en almacenes de objetos. Puedes crear índices en cualquier propiedad de los objetos para realizar búsquedas eficientes, y usar transacciones para agrupar múltiples operaciones y garantizar que todas se completen o ninguna se complete.
Ventajas sobre localStorage
IndexedDB ofrece almacenamiento mucho mayor (50MB+ vs 5-10MB), soporta tipos de datos complejos, permite búsquedas eficientes con índices, usa transacciones para consistencia, y es asíncrona para no bloquear la UI.
Conceptos Básicos
Antes de usar IndexedDB, es importante entender sus conceptos fundamentales. IndexedDB tiene una terminología específica que difiere de otras bases de datos, y comprender estos conceptos es esencial para usar la API de forma efectiva.
Bases de Datos y Almacenes
Una base de datos en IndexedDB contiene uno o más almacenes de objetos (object stores). Cada almacén es similar a una tabla en una base de datos SQL y contiene objetos del mismo tipo. Puedes crear múltiples almacenes en una base de datos, cada uno con su propia estructura y índices.
El código muestra los conceptos básicos de IndexedDB. Una base de datos puede tener múltiples almacenes, cada almacén contiene objetos del mismo tipo, y puedes crear índices en cualquier propiedad para búsquedas eficientes. Los índices son fundamentales para el rendimiento, ya que permiten encontrar datos sin escanear todo el almacén.
Los conceptos clave son: la base de datos como contenedor principal, los almacenes (object stores) como colecciones de objetos del mismo tipo, los índices para búsquedas eficientes, las transacciones que agrupan operaciones garantizando que todas se completen o ninguna, y los cursores que permiten iterar sobre resultados de consultas.
Transacciones
Las transacciones son fundamentales en IndexedDB para garantizar la consistencia de datos. Una transacción agrupa múltiples operaciones de lectura/escritura y asegura que todas se completen exitosamente o ninguna se complete. Si alguna operación falla, toda la transacción se revierte automáticamente.
Este ejemplo muestra cómo usar transacciones en IndexedDB. Las transacciones tienen un modo (readonly o readwrite) y especifican qué almacenes afectan. Todas las operaciones dentro de una transacción se agrupan, y si alguna falla, todas se revierten automáticamente, garantizando la integridad de los datos.
Modos de Transacción
Las transacciones pueden ser readonly (solo lectura) o readwrite (lectura y escritura). Usa readonly para consultas que no modifican datos, ya que permite múltiples transacciones readonly simultáneas, mientras que readwrite bloquea el almacén.
Abrir una Base de Datos
Para usar IndexedDB, primero necesitas abrir una base de datos usando el método indexedDB.open(). Este método devuelve una solicitud (request) que puede tener éxito o fallar, y necesitas manejar ambos casos. La primera vez que abres una base de datos, se dispara el evento onupgradeneeded donde puedes crear los almacenes.
El código muestra cómo abrir una base de datos IndexedDB. El evento onupgradeneeded es crucial: es donde creas los almacenes y defines la estructura de la base de datos. Este evento solo se dispara cuando la versión de la base de datos cambia, lo que te permite hacer migraciones de esquema de forma controlada.
Versionado de Base de Datos
Cada base de datos IndexedDB tiene un número de versión. Cuando abres una base de datos con una versión mayor que la actual, se dispara onupgradeneeded. Usa este mecanismo para hacer migraciones de esquema de forma controlada y segura.
Crear un Almacén
Los almacenes (object stores) son donde se almacenan los datos en IndexedDB. Debes crear los almacenes en el evento onupgradeneeded cuando abres la base de datos. Cada almacén tiene un nombre único y puede tener opciones como keyPath para definir la clave primaria de los objetos.
Este ejemplo muestra cómo crear almacenes con diferentes configuraciones. El primer almacén usa keyPath automático (autoIncrement), el segundo define una clave primaria específica, y el tercero crea índices adicionales para búsquedas eficientes. Los índices son fundamentales para el rendimiento, ya que permiten encontrar datos rápidamente sin escanear todo el almacén.
Agregar Datos
Para agregar datos a IndexedDB, usas el método add() de un almacén dentro de una transacción readwrite. El método add() intenta agregar un nuevo objeto y fallará si ya existe un objeto con la misma clave. Si quieres permitir sobrescribir objetos existentes, usa put() en lugar de add().
El código muestra cómo agregar datos a IndexedDB usando transacciones. La transacción readwrite permite modificar el almacén, y el método add() agrega nuevos objetos. Es importante manejar tanto el éxito como el error de la operación, ya que add() fallará si intentas agregar un objeto con una clave que ya existe.
add() vs put()
Usa add() cuando quieras que falle si la clave ya existe (evitar duplicados). Usa put() cuando quieras permitir sobrescribir objetos existentes. put() es más flexible y es la opción preferida en la mayoría de casos.
Leer Datos
Para leer datos de IndexedDB, usas el método get() de un almacén dentro de una transacción. El método get() recupera un objeto por su clave y lo retorna en el evento onsuccess de la solicitud. También puedes usar getAll() para recuperar todos los objetos de un almacén, o usar índices para búsquedas más específicas.
Este ejemplo muestra cómo leer datos de IndexedDB. El método get() recupera un objeto específico por su clave, mientras que getAll() recupera todos los objetos del almacén. Para búsquedas más complejas, puedes usar índices con métodos como getAll() en el índice específico, lo que permite filtrar y ordenar resultados eficientemente.
Actualizar Datos
Para actualizar datos en IndexedDB, usas el método put() de un almacén dentro de una transacción readwrite. El método put() agrega un nuevo objeto o reemplaza uno existente si la clave ya existe. Es la forma más flexible de guardar datos, ya que funciona tanto para agregar nuevos como para actualizar existentes.
El código muestra cómo actualizar datos en IndexedDB. El método put() es la forma principal de guardar datos, ya que funciona tanto para agregar nuevos objetos como para actualizar existentes. Es importante incluir la clave del objeto si no estás usando autoIncrement, ya que put() necesita saber qué objeto actualizar.
Eliminar Datos
Para eliminar datos de IndexedDB, usas el método delete() de un almacén dentro de una transacción readwrite. El método delete() elimina un objeto por su clave. Para eliminar todos los objetos de un almacén, puedes usar clear() del almacén, lo que es más eficiente que eliminar uno por uno.
Este ejemplo muestra cómo eliminar datos de IndexedDB. El método delete() elimina un objeto específico por su clave, mientras que clear() elimina todos los objetos del almacén. clear() es mucho más eficiente cuando necesitas eliminar todos los datos, ya que es una sola operación en lugar de múltiples operaciones delete().
Operación Destructiva
El método clear() elimina TODOS los datos de un almacén y es irreversible. Úsalo con precaución y solo cuando estés seguro de que quieres limpiar completamente el almacén. Considera hacer un backup antes de usar clear() en producción.
Casos de Uso Prácticos
IndexedDB es ideal para aplicaciones que necesitan almacenar grandes cantidades de datos o datos complejos que requieren búsquedas eficientes. Estos son algunos de los escenarios más comunes donde IndexedDB brinda valor significativo sobre localStorage.
El código muestra tres casos de uso prácticos de IndexedDB. El primero es una aplicación de notas offline-first, donde IndexedDB almacena todas las notas del usuario. El segundo es un caché de respuestas de API para reducir llamadas al servidor. El tercero es un gestor de archivos con almacenamiento local de blobs.
- Aplicaciones offline-first con datos complejos
- Caché de respuestas de API para reducir llamadas al servidor
- Gestores de archivos con almacenamiento local de blobs
- Aplicaciones de productividad con grandes cantidades de datos
- Sistemas de mensajería con historial de conversaciones
Limitaciones y Consideraciones
IndexedDB tiene varias limitaciones importantes que debes conocer antes de usarla en producción. Estas limitaciones afectan cuándo y cómo deberías usar IndexedDB en tus aplicaciones, y cuándo considerar alternativas o estrategias complementarias.
- <strong>API compleja:</strong> Basada en eventos y callbacks, difícil de usar
- <strong>Soporte de navegadores:</strong> No disponible en navegadores muy antiguos
- <strong>Límite por dominio:</strong> Depende del navegador y espacio disponible
- <strong>Síncrona vs Asíncrona:</strong> No se puede usar en workers web síncronos
- <strong>Consultas limitadas:</strong> No soporta consultas complejas como SQL
Curva de Aprendizaje Pronunciada
La API de IndexedDB es compleja y basada en eventos, lo que la hace difícil de usar para principiantes. Considera usar una librería como Dexie.js o IDB que simplifica la API mientras mantienes los beneficios de IndexedDB.
Errores Comunes
Estos son los errores más frecuentes que encontrarás al trabajar con IndexedDB, especialmente cuando estás aprendiendo o migrando desde localStorage. Conocer estos patrones te ayudará a evitar bugs comunes y escribir código más robusto.
Error: No crear almacén en onupgradeneeded
Un error común es intentar agregar datos sin haber creado el almacén primero. Los almacenes solo se pueden crear en el evento onupgradeneeded cuando abres la base de datos. Si intentas agregar datos a un almacén que no existe, recibirás un error.
El ejemplo muestra el error de intentar agregar datos sin crear el almacén. El código fallará porque el almacén 'users' no existe. La solución es crear el almacén en onupgradeneeded antes de intentar agregar datos.
Error: Intentar escribir en transacción readonly
Otro error frecuente es intentar modificar datos en una transacción readonly. Las transacciones readonly solo permiten leer datos, no modificarlos. Si intentas usar add(), put() o delete() en una transacción readonly, recibirás un error de transacción abortada.
El código muestra el error de intentar escribir en una transacción readonly. La transacción fallará porque intentas modificar datos en modo readonly. La solución es usar una transacción readwrite cuando necesites modificar datos.
Error: No manejar cambios de versión
Un error común es no manejar correctamente el evento onupgradeneeded cuando cambia la versión de la base de datos. Si un usuario tiene una versión antigua de la base de datos y abres una nueva versión, debes crear los almacenes en onupgradeneeded. Si no lo haces, la base de datos se abrirá sin los almacenes necesarios.
El ejemplo muestra cómo manejar cambios de versión correctamente. El código incrementa la versión de la base de datos y crea almacenes en onupgradeneeded. Esto permite hacer migraciones de esquema de forma controlada cuando la estructura de la base de datos cambia.
Resumen: IndexedDB
Conceptos principales:
- •IndexedDB es una base de datos NoSQL asíncrona en el navegador
- •Usa almacenes (object stores) para organizar datos por tipo
- •Las transacciones garantizan consistencia de datos
- •Los índices permiten búsquedas eficientes sin escanear todo
- •Soporta grandes cantidades de datos (50MB+) y tipos complejos
Mejores prácticas:
- •Crea almacenes en onupgradeneeded cuando cambie la versión
- •Usa transacciones readonly para consultas y readwrite para modificaciones
- •Usa put() para agregar/actualizar, add() solo para evitar duplicados
- •Crea índices en propiedades que usarás para búsquedas
- •Considera librerías como Dexie.js para simplificar la API