IndexedDB es una manera de almacenar datos de manera persistente en el navegador. Dado que permite la creación de aplicaciones web con capacidades de consulta mejoradas, éstas pueden funcionar tanto en línea como fuera de línea. IndexedDB es útil para aplicaciones que almacenan una gran cantidad de datos (catálogos de DVDs en una biblioteca, por ejemplo) y para aplicaciones que no necesitan de una conexión permanente a internet para funcionar (clientes de correo, listas de tareas y blocs de notas, por ejemplo).
Sobre este documento
Esta introducción discute conceptos y terminologías fundamentales en IndexedDB. Provee una visión general y orienta sobre los conceptos clave. Para aprender más sobre los términos relacionados con IndexedDB, vea la sección de Definiciones.
Para un tutorial sobre cómo usar la API, vea Usando IndexedDB. Para ver la documentación de referencia sobre la API de IndexedDB, vea el artículo IndexedDB y sus sub-páginas, que documentan los tipos de objetos utilizados por IndexedDB, así como los métodos de las API síncrona y asíncrona.
Visión general de IndexedDB
IndexedDB le permite almacenar y obtener objetos indizados a partir de una "llave". Todos los cambios realizados a la base de datos ocurren dentro de transacciones. Como la mayoría de las soluciones de almacenamiento web, IndexedDB sigue una política de mismo origen, por lo que aún cuando se puede acceder a datos almacenados en un dominio, no se pueden acceder a datos a través de distintos dominios.
La API incluye una variante asíncrona y una síncrona. La variante asíncrona puede ser utilizada en la mayoría de los casos, incluyendo WebWorkers, mientras la variante síncrona está diseñada para ser utilizada solo con WebWorkers. Actualmente, ninguno de los navegadores principales soportan la API síncrona. Sin embargo, aún cuando la API síncrona sea soportada, en la mayoría de los casos de uso de IndexedDB lo más probable es que se utilice la API asíncrona.
IndexedDB es una alternativa a WebSQL, que fue marcada como obsoleta por W3C el 18 de Noviembre de 2010. Aún cuando IndexedDB y WebSQL son soluciones para el almacenamiento, éstas no ofrecen las mismas funcionalidades. WebSQL es un sistema relacional de acceso a datos, mientras que IndexedDB es un sistema de tablas indizadas.
Conceptos principales
Si usted tiene alguna idea de cómo trabajan otros tipos de bases de datos, podría tener algún conflicto de conceptos al trabajar con IndexedDB. Por esta razón mantenga los siguientes conceptos importantes en mente:
-
IndexedDB almacena pares llave-valor. Los valores pueden ser objetos con estructuras complejas, y las llaves pueden ser propiedades de esos objetos. Se pueden crear índices que usan cualquier propiedad de los objetos para realizar búsquedas rápidas o enumeraciones ordenadas.
-
IndexedDB está hecho sobre un modelo de base de datos transaccional. Todo lo que usted haga en IndexedDB siempre ocurre en el contexto de una transacción. La API de IndexedDB provee una gran cantidad de objetos que representan índices, tablas, cursores, y más, pero cada uno de ellos están atados a una transacción particular. Así, usted no puede ejecutar ningún comando o abrir un cursor fuera de una transacción.
Las transacciones tienen un período de vida bien definido. Por esta razón, cualquier intento de utilizar una transacción que ya se ha completado ocasionará excepciones. Igualmente, las transacciones aplican los cambios automáticamente y no se puede hacer commit automáticamente.
Este modelo de transacciones es realmente útil cuando se considera qué podría pasar si un usuario abre dos instancias de una aplicación simultáneamente en dos pestañas. Sin operaciones transaccionales, las dos instancias podrían hacer que los cambios se pisen o se sobreescriban entre sí. Si no está familiarizado con el concepto de transacción en una base de datos, lea el artículo correspondiente en la Wikipedia y transacción en la sección de definiciones.
-
La API de IndexedDB es mayormente asincrona. La API no devuelve datos regresando un valor; en cambio, es necesario pasarle una funcion como callback. Usted no "guarda" un valor en la base de datos, o "lee" un valor de la misma por medios síncronos. En cambio, usted "solicita" una operación a la base de datos. Un evento del DOM es utilizado para notificarle que la operación terminó, y el tipo de evento recibido le permite saber si ésta falló o si fue exitosa. Esto puede sonar complicado al comienzo, pero hay algunas medidas de sanidad incluidas por defecto. Esto no es muy distinto de cómo funciona XMLHttpRequest.
-
IndexedDB usa solicitudes en todos lados. Las solicitudes (requests) son objetos que reciben los eventos del DOM mencionados previamente. Éstas cuentan con las propiedades
onsuccess
yonerror
, sobre las cuales se puede aplicaraddEventListener()
yremoveEventListener()
. Estas también tienen las propiedadesreadyState
,result
, yerrorCode
que permiten conocer el estado de la solicitud. La propiedadresult
es particularmente mágica, dado que puede ser muchas cosas distintas, dependiendo de cómo se generó la solicitud (por ejemplo, una instancia deIDBCursor
, o la llave de un valor recién insertado en la base de datos). -
IndexedDB usa eventos DOM para notificar cuando los resultados están disponibles. Los eventos del DOM siempre tienen una propiedad
type
(en IndexedDB, ésta es generalmente"success"
o"error"
). Los eventos del DOM también tienen una propiedadtarget
que dice a dónde apunta el evento. En la mayoría de los casos, eltarget
de un evento es el objetoIDBRequest
que se generó como resultado de una operación sobre la base de datos. Los eventos exitosos no son pasados a los padres y no pueden ser cancelados. Por otro lado, los eventos de error son pasados a los padres del target y pueden cancelarse. Esto es importante, dado que los eventos de error cancelan cualquier transacción sobre la que estén corriendo a menos que sean cancelados. -
IndexedDB es orientada a objetos. IndexedDB no es una base de datos relacional, con tablas, filas y columnas. Esta diferencia fundamental e importante afecta la manera como usted diseña e implementa sus aplicaciones.
En un almacén de datos relacional, usted podría tener una tabla que almacena una colección de filas de datos y columnas de tipos de datos con un nombre. IndexedDB, por otro lado, necesita que usted cree un almacén de objetos para un tipo de datos y sencillamente almacena objetos JavaScript en ese almacén. Cada almacén de objetos puede tener una colección de índices que hacen que la iteración y la búsqueda de elementos sea más eficiente. Si usted no está familiarizado con los sistemas de manejo de datos orientados a objetos, lea el artículo de la Wikipedia sobre bases de datos orientadas a objetos.
-
IndexedDB no utiliza SQL (Structured Query Language). En cambio usa consultas sobre un índice que producen un cursor. Éste puede utilizarse para iterar sobre el conjunto de resultados. Si no está familiarizado con sistemas NoSQL, lea el artículo de Wikipedia sobre NoSQL.
-
IndexedDB se adhiere a una política de mismo origen. Un orígen es el dominio, protocolo de la capa de aplicación, y el puerto de la URL del documento donde se está ejecutando la aplicación. Cada orígen tiene su propio conjunto de bases de datos asociado. Cada base de datos tiene un nombre que la identifica dentro de un orígen.
El límite de seguridad impuesto en IndexedDB prevé que las aplicaciones puedan acceder a datos de un orígen diferente. Por ejemplo, mientras una aplicación en https://www.example.com/app/ puede obtener datos desde https://www.example.com/dir/, debido a que comparten un mismo orígen, esta no puede leer datos desde https://www.example.com:8080/dir/ (puerto distinto) o desde https://www.example.com/dir/ (protocolo distinto), debido a que tienen distintos orígenes.
Definiciones
Esta sección define y explica los términos utilizados en la API de IndexedDB.
Base de Datos
- base de datos
- Un repositorio de información, típicamente compuesto de uno o más almacenes de objetos. Cada base de datos debe tener:
- Nombre. Identifica la base de datos dentro de un mismo origen y no cambia a lo largo de la existencia de ésta. El nombre puede ser cualquier cadena de caracteres (incluyendo una vacía).
-
Versión actual. Cuando una base de datos se crea por primera vez, la versión es el número entero 1 si no se indica otra cosa. Cada base de datos puede tener una y sólo una versión en cualquier momento.
- almacén de objetos
-
El mecanismo a través del cual los datos son almacenados en la base de datos. El almacén de objetos mantiene registros de manera persistente, que son pares de llave-valor. Los registros dentro de un almacén de objetos son ordenados de acuerdo con las llaves en orden ascendente.
Todo almacén de objetos debe tener un nombre que es único a lo largo de su base de datos. Opcionalmente puede tener un generador de llaves y una ruta de llaves. Si el almacén tiene una ruta de llaves, éste utiliza llaves en línea; si no, utiliza llaves fuera de línea.
Para documentación de referencia sobre los almacenes de objetos, vew IDBObjectStore o IDBObjectStoreSync.
- versión
- Cuando una base de datos es creada por primera vez, su versión es 1. Cada base de datos tiene una versión en cualquier momento y no puede tener varias versiones al mismo tiempo. La única manera de cambiar la versión es abrir la base de datos con una versión mayor a la actual. Esto arranca una transacción
versionchange
y dispara el eventoupgradeneeded
. El único momento cuando se puede actualizar el esquema de la base de datos es dentro del manejador de este evento. - Nota: Esta definición describe la especificación más actual, que solo está implementada por las versiones más nuevas de los navegadores. Los navegadores más antiguos implementaron el método
IDBDatabase.setVersion()
, que ya ha sido marcado obsoleto y removido. - conexión a la base de datos
- Una operación creada al abrir una base de datos. Una base de datos puede tener múltiples conexiónes al mismo tiempo.
- transacción
-
Un conjunto atómico y durable de operaciónes de acceso y modificación de datos sobre una base de datos particular. Esta es la manera cómo se interactúa con los datos de una base de datos. De hecho, cualquier lectura o modificación de datos en la base de datos debe ocurrir dentro de una transacción.
Una conexión a la base de datos puede tener varias transacciones activas, siempre que las operaciones de escrituras no tengan ámbitos que se solapen. El ámbito de las transacciones, que es definido al momento en que éstas son creadas, determina con qué almacenes de datos puede interactuar ésta y permanece constante a lo largo de su existencia. Así, por ejemplo, si una conexión tiene una transacción escribiendo con un ámbito que abarca solo el almacén
flyingMonkey
, se puede iniciar una segunda que tenga como ámbito los almacenesunicornCentaur
yunicornPegasus
. En el caso de las transacciones de lectura, se pueden tener varias aún cuando se solapen.Se espera que las transacciones tengan una existencia corta, de manera que el navegador puede cancelar una transacción que tome demasiado tiempo para liberar recursos que la misma tenga bloqueados. Usted puede abortar la transacción, lo que deshace los cambios hechos a la base de datos durante la misma. Ni siquiera es necesario esperar a que la transacción inicie para abortarla.
Los tres modos de transacción son:
readwrite
,readonly
, yversionchange
. La única manera de crear y borrar almacenes es usar una transacciónversionchange
. Para aprender más sobre los tipos de transacción, vea al artículo de referencia de IndexedDB.Debido a que todo sucede dentro de una transacción, este es un concepto muy importante. Para aprender más sobre las transacciones, especialmente sobre como se relacionan con el versionado, vea IDBTransaction, que también cuenta con documentación de referencia. Para la documentación sobre la API asíncrona, vea IDBTransactionSync.
- solicitud
- La operación por medio de la cual se lee o se escribe en una base de datos. Toda solicitud representa una y solo una operación de lectura o escritura.
- índice
-
Un almacén especializado para buscar registros en otro almacén, llamado almacén de objetos referenciado. El índice es un almacenamiento persistente llave-valor donde el valor es una llave del almacén referenciado. Los registros en un índice son rellenados automáticamente cada vez que se modifican los registros del almacén referenciado. Cada registro en un índice puede apuntar solo a un registro de su almacén referenciado, pero varios índices pueden apuntar al mismo almacén.
Alternativamente, se pueden realizar búsquedas en un almacén de objetos usando la llave.
Para aprender más sobre el uso de los índices, vea Usando IndexedDB. Para documentación de referencia, vea IDBKeyRange.
Llave y valor
- llave
-
Es uno de los atributos del objeto a partir del cual los demás objetos son organizados y obtenidos desde el almacén de objetos. El almacén puede derivar una llave desde uno de tres orígenes: un generador de llaves, una ruta de llave, y un valor indicado de forma explícita. La llave debe ser de un tipo de datos que tenga un número creciente en relación con los objetos almacenados previamente. Cada registro en un almacén de datos debe tener una llave única en el almacén, de manera que no se pueden tener varios objetos con la misma llave en un almacén de objetos dado.
Una llave puede ser de uno de los siguientes tipos: String, Date, float, y Array. Para los arreglos, la llave puede tener un rango desde un valor vacío hasta infinito, y puede incluirse un arreglo dentro de otro. No se requiere usar solo cadenas o enteros para las llaves .
Como alternativa, se pueden realizar búsquedas de objetos usando un índice.
- generador de llaves
- Es un mecanismo para producir nuevas llaves en una secuencia ordenada. Si un almacén de objetos no tiene un generador de llaves, entonces la aplicación debe proveer llaves para los registros que se almacenen. Los generadores no son compartidos entre almacenes. Esto es un detalle de implementación de los navegadores, porque en desarrollo web, realmente no se crea o se accede a un generador de llaves.
- llaves en línea
- Es una llave que se almacena como parte del valor almacenado. La manera como se determina cuál es la llave es utilizando una ruta de llave. Una llave en línea puede obtenerse con un generador. Después de que la llave ha sido generada, esta puede almacenarse en el valor usando la ruta del atributo o puede ser usada directamente como llave.
- llave fuera de línea
- Una llave que se almacena separada del valor.
- ruta de llave
- Define de dónde se debe extraer la llave desde un valor en el almacén o en un índice. Una ruta de llave válida puede incluir alguno de los siguientes: una cadena vacía, un identificador de JavaScript, o múltiples identificadores de JavaScript separados con puntos. No puede incluir espacios.
- valor
-
Cada registro tiene un valor, el cual puede ser cualquier cosa que pueda ser expresada en JavaScript, incluyendo: booleanos, números, cadenas, fechas, objetos, arreglos, expresiones regulares, undefined, y null.
Cuando un objeto o un arreglo es almacenado, las propiedades y valores en ese objeto o arreglo pueden ser cualquier cosa que sea un valor válido.
Se pueden almacenar Blobs y archivos. cf. especificación .
Rango y ámbito
- ámbito
- El conjunto de almacenes de objetos e índices en los que una transacción aplica. Los ámbitos de varias transacciones de solo lectura pueden solaparse y ejecutarse al mismo tiempo. En cambio, los ámbitos de transacciones de escritura no pueden solaparse. Aún así se pueden crear varias transacciones con el mismo ámbito al mismo tiempo, pero éstas serán manejadas por una cola y ejecutadas una detrás de otra.
- cursor
- Un mecanismo para iterar a través de múltiples registros con un rango de llaves. El cursor tiene un orígen que indica que índice o almacén de datos está iterando. El cursor tiene una posición dentro del rango, y se mueve en dirección creciente o decreciente en el orden de las llaves de cada registro. Para obtener documentación de referencia sobre cursores, vea IDBCursor o IDBCursorSync.
- rango de llaves
-
Un intervalo continuo sobre algún tipo de datos utilizado para las llaves. Los registros pueden obtenerse desde los almacenes e índices usando llaves o un rango de llaves. Los rangos pueden limitarse o filtrarse con umbrales inferiores y superiores. Por ejemplo, se puede iterar sobre todos los valores de una llave desde x hasta y.
Para documentación de referencia sobre los rangos de llaves, vea IDBKeyRange.
Limitaciones
IndexedDB está diseñado para cubrir la mayoría de los casos en los que se necesita almacenamiento del lado del cliente. Sin embargo, no están contemplados en su diseño unos pocos casos como los siguientes:
- Ordenamiento internacionalizado. No todos los idiomas ordenan las cadenas de la misma forma, por lo que el ordenamiento internacionalizado no está soportado. Aún cuando la base de datos no puede ordenar los datos respetando su idioma, usted puede ordenar los datos obtenidos de la base de datos por su cuenta.
- Sincronización. La API no está diseñada para tomar en cuenta la sincronización con una base de datos del lado del servidor. Usted debe escribir el código de sincronización para estos casos.
- Búsqueda de Texto Completo. La API no ofrece un equivalente al operador
LIKE
en SQL.
Adicionalmente, tenga en cuenta que los navegadores podrían eliminar la base de datos, como en las siguientes condiciones:
- El usuario pide borrar los datos del navegador.
Muchos navegadores tienen opciones que permiten al usuario eliminar todos los datos almacenados de un sitio, incluyendo cookies, marcadores, contraseñas, y datos de IndexedDB. - El navegador está en modo de navegación privada.
Algunos navegadores tienen modos de "navegación privada" (Firefox) o "incógnito" (Chrome). Al final de la sesión, el navegador elimina la base de datos. - Se alcanza el límite de espacio en disco.
- Los datos se corrompen.
- Se realiza un cambio incompatible a ésta característica.
Las circunstancias exactas y capacidades de los navegadores cambian con el tiempo, pero la filosofía de los navegadores es, en general, hacer lo posible por conservar los datos.
Advertencia: Al momento, debido a errores o a propósito, es imposible abrir una base de datos IndexedDB desde un Web App. Esto requiere mayor investigación para documentarlo.
Próximo paso
Ok, ahora con estos conceptos generales bajo nuestros cinturones, podemos seguir a cosas más concretas. Para un tutorial sobre como utilizar la API, vea Usando IndexedDB.
Vea también
Especificación
Referencia
Tutoriales
- Usando IndexedDB
- Una lista de tareas sencilla usando HTML5 e IndexedDB. Nota: Este ejemplo usa una versión antigua de la especificación y no funciona en las versiones más recientes de los navegadores principales (aún utiliza el método
setVersion()
, que fue removido.
Artículo relacionado