JavaScript IndexedDB
Aprenda JavaScript IndexedDB: abra um banco de dados, trate onupgradeneeded, crie object stores e índices, execute transações, e adicione, obtenha, atualize e exclua registros com cursores.
IndexedDB é uma API de armazenamento client-side poderosa, mais robusta do que outras soluções de armazenamento local disponíveis nos navegadores web. Ela permite que aplicações web armazenem e manipulem quantidades significativas de dados estruturados de forma assíncrona. O IndexedDB é ideal para aplicações que exigem armazenamento de dados offline, alto desempenho e capacidades de consulta avançadas sem dependência de uma conexão de rede.
Este capítulo abrange tudo o que você precisa para começar a usar o IndexedDB: abrir um banco de dados e tratar atualizações de versão, criar object stores e índices, executar transações e realizar o ciclo CRUD completo (adicionar, obter, atualizar, excluir), incluindo iteração com cursores e consultas por meio de índices.
Quando usar o IndexedDB
O IndexedDB armazena praticamente qualquer valor JavaScript — objetos, arrays, datas, blobs e arquivos — não apenas strings, e pode armazenar centenas de megabytes, muito além do limite de ~5 MB do localStorage e sessionStorage. É um armazenamento NoSQL de chave/valor transacional com índices, o que o torna a escolha certa para:
- Aplicações offline-first que precisam funcionar sem uma conexão de rede.
- Cache de grandes conjuntos de dados, como respostas de API, documentos ou arquivos de mídia.
- Consultas de registros por campos indexados, em vez de carregar tudo na memória.
Se você só precisa persistir alguns valores de string pequenos, prefira as APIs de Web Storage mais simples. Consulte o capítulo Storage API para uma visão geral de como o navegador gerencia cotas de armazenamento entre esses mecanismos.
A API assíncrona baseada em eventos
Cada operação do IndexedDB é assíncrona e orientada a eventos. Chamadas como indexedDB.open() ou store.get() retornam imediatamente um objeto de request; o resultado real chega posteriormente por meio dos handlers de eventos onsuccess / onerror. Seu código nunca bloqueia aguardando E/S de disco. Por causa disso, você não pode ler um resultado de forma síncrona logo após emitir uma solicitação — você deve aguardar o disparo do evento. (O código moderno frequentemente encapsula essas solicitações em Promises, mas a API nativa em si é baseada em callbacks, que é o que os exemplos abaixo utilizam.)
Configurando um Banco de Dados IndexedDB
Passo 1: Abrir um Banco de Dados
Para usar o IndexedDB, o primeiro passo é abrir um banco de dados com indexedDB.open(name, version). O segundo argumento opcional é um número de versão inteiro. Se o banco de dados não existir, ou se a versão solicitada for maior do que a armazenada, o evento onupgradeneeded é disparado — este é o único lugar onde você pode alterar a estrutura do banco de dados (criar ou excluir object stores e índices).
A chamada retorna uma solicitação e pode disparar três eventos:
onsuccess— o banco de dados foi aberto e está pronto para uso (event.target.resulté oIDBDatabase).onerror— a abertura falhou.onupgradeneeded— uma mudança de esquema é necessária (consulte o Passo 2).
Veja como você pode criar ou abrir um banco de dados IndexedDB. Após uma operação bem-sucedida, fechamos o banco de dados para evitar efeitos colaterais indesejados em outros exemplos. Você pode omitir este passo no seu próprio código, ou incluí-lo se necessário.
Aviso: Chamar deleteDatabase() apagará todos os dados desse banco de dados. Isso é apenas para fins de teste e não deve ser usado em produção.
Passo 2: Criando Object Stores
Depois que o banco de dados é aberto, você pode criar uma object store, que é análoga a uma tabela em bancos de dados relacionais. Observe que isso só pode ser feito durante uma atualização de versão (ao abrir o banco de dados com um número de versão maior). O evento a ser observado é onupgradeneeded.
Ao criar uma store, você escolhe como a chave primária de cada registro é definida:
{ keyPath: 'id' }— a chave é lida a partir da propriedadeidde cada objeto armazenado (uma chave inline).{ keyPath: 'id', autoIncrement: true }— a store gera uma chave sequencial automaticamente se você não fornecer uma.{ autoIncrement: true }— as chaves são geradas e armazenadas separadamente do valor (uma chave out-of-line).
Você também cria índices dentro de onupgradeneeded. Um índice permite buscar registros por uma propriedade diferente da chave primária. Passe { unique: true } para rejeitar valores duplicados para aquela propriedade.
Transações
Depois que seu banco de dados e object stores estão configurados, você precisará gerenciar as operações de dados usando transações. Uma transação no IndexedDB é um mecanismo que agrupa múltiplas operações em uma única unidade de trabalho que ou é concluída com sucesso na íntegra, ou falha completamente. É essencial para garantir a consistência e integridade dos dados, especialmente quando múltiplas operações dependem umas das outras para produzir um resultado correto.
Como Usar Transações no IndexedDB
Passo 1: Iniciando uma Transação
Para realizar qualquer operação no IndexedDB, você começa criando uma transação em um banco de dados. Uma transação é criada especificando quais object stores a transação vai envolver e o modo da transação, que pode ser "readonly" ou "readwrite".
Passo 2: Acessando uma Object Store
Dentro de uma transação, você pode acessar uma ou mais object stores para realizar operações de dados.
Passo 3: Realizando Operações
Assim que você tem acesso a uma object store, pode executar várias operações, como adicionar, recuperar, atualizar ou excluir dados. Cada operação retorna um objeto de solicitação que você pode usar para tratar eventos de sucesso ou erro.
Passo 4: Concluindo a Transação
Uma transação será concluída automaticamente assim que todas as operações emitidas nela forem resolvidas, seja por sucesso ou por falha. Você também pode ouvir o evento complete na transação para executar ações depois que todas as operações forem concluídas com sucesso.
Aqui está um exemplo de uma transação completa:
Lendo Dados
Para recuperar dados, use store.get(key) ou um cursor para iterar sobre múltiplos registros. Aqui estão exemplos rápidos:
Recuperando um único registro:
Iterando com um cursor:
Consultando com Índices
A chave primária permite buscar um registro quando você já conhece sua chave. Um índice permite pesquisar por outra propriedade — por exemplo, encontrar um livro pelo seu title. Acesse um índice com store.index(name), depois chame get(), getAll() ou openCursor() nele, assim como faria em uma store.
Buscando um registro por uma propriedade indexada:
Você também pode restringir uma consulta a um intervalo de chaves com IDBKeyRange (por exemplo, IDBKeyRange.bound('A', 'M') para buscar todos os livros cujo título começa entre A e M), passando o intervalo para getAll() ou openCursor().
Atualizando e Excluindo Registros
Para modificar dados existentes, use store.put() com a mesma chave. Para remover dados, use store.delete(key).
Atualizando um registro:
Excluindo um registro:
Como visualizar o conteúdo do IndexedDB no seu navegador
Após armazenar e manipular dados, você pode querer inspecionar o que está realmente salvo no seu navegador. A maioria dos navegadores modernos exibe o que está armazenado no IndexedDB. Abaixo está o exemplo das ferramentas de desenvolvedor do Chrome:

Boas Práticas para Usar Transações
Para garantir que sua implementação de IndexedDB permaneça confiável e com bom desempenho, siga estas diretrizes:
- Minimize o Escopo: Mantenha as transações o menores possível, tanto em termos de número de operações quanto de duração. Isso reduz a probabilidade de conflitos e melhora o desempenho.
- Tratamento de Erros: Sempre implemente tratamento de erros tanto no nível da solicitação quanto da transação. Isso ajuda a diagnosticar problemas e evitar atualizações parciais que poderiam levar à corrupção de dados.
- Concorrência: Entenda que, embora o IndexedDB seja assíncrono e não bloqueante, as transações no mesmo banco de dados são enfileiradas e executadas em série para evitar condições de corrida e inconsistências.
Conclusão
O IndexedDB fornece uma plataforma robusta para o gerenciamento complexo de dados em aplicações web, tornando-o uma ferramenta essencial para desenvolvedores web modernos. Por meio da implementação adequada de seus recursos, os desenvolvedores podem armazenar, recuperar, atualizar e excluir dados client-side de forma eficiente, melhorando o desempenho da aplicação e a experiência do usuário.
Para necessidades de armazenamento mais simples e para entender o panorama mais amplo de armazenamento no navegador, consulte estes capítulos relacionados:
- localStorage e sessionStorage — armazenamento leve de chave/valor para pequenas quantidades de dados em string.
- A Storage API — como o navegador gerencia cotas de armazenamento e persistência entre os mecanismos.
- Trabalhando com JSON — serialização de dados, útil ao mover objetos para dentro e fora do armazenamento.