Um Guia para Iniciantes sobre como Usar as APIs do IndexedDB

@apidog

@apidog

20 junho 2025

Um Guia para Iniciantes sobre como Usar as APIs do IndexedDB

Construir aplicações que funcionem perfeitamente offline se tornou uma exigência fundamental no desenvolvimento web moderno. O IndexedDB oferece uma solução poderosa para desenvolvedores que buscam armazenar e gerenciar grandes quantidades de dados diretamente no navegador. Este banco de dados NoSQL do lado do cliente permite um manuseio de dados eficiente, tornando-o ideal para aplicações web que precisam de funcionalidade offline. Neste artigo, exploraremos os fundamentos do IndexedDB, suas principais características e guiaremos você pelos passos para configurá-lo e usá-lo em seus projetos web.

O que é o IndexedDB?

IndexedDB é um poderoso banco de dados NoSQL do lado do cliente que permite que aplicações web armazenem e gerenciem dados estruturados diretamente no navegador. Ele fornece uma solução eficiente para gerenciar grandes conjuntos de dados localmente, tornando-o ideal para funcionalidade offline e aplicações intensivas em dados.

Uma das principais vantagens do IndexedDB é sua capacidade de suportar aplicações web offline. Ele permite que os desenvolvedores armazenem quantidades substanciais de dados, usem indexação para recuperação rápida, gerenciem transações e naveguem pelos dados usando cursores. Essas características o tornam uma excelente escolha para lidar com dados complexos em aplicativos web.

A maioria dos navegadores web modernos, incluindo Chrome, Firefox, Safari e Edge, suporta o IndexedDB. No entanto, é importante garantir que a versão do IndexedDB seja compatível com o navegador que você está direcionando para sua aplicação web.

Para começar a usar o IndexedDB, o primeiro passo é abrir uma conexão com o banco de dados, o que é necessário para criar e interagir com ele.

💡
quando você também está trabalhando com APIs, é importante considerar como ferramentas como o Apidog podem simplificar seu fluxo de trabalho. Apidog fornece uma plataforma unificada que simplifica o design, teste e documentação de APIs. Ao contrário de outras ferramentas que exigem múltiplos passos separados para gerenciamento de APIs, o Apidog reúne tudo em um só lugar com recursos como sincronização em tempo real e colaboração sem interrupções para equipes de desenvolvimento.
button

Introdução ao IndexedDB: Um Guia Abrangente

O IndexedDB é uma poderosa solução de armazenamento do lado do cliente para aplicações web. Este guia irá guiá-lo por tudo, desde a configuração básica até técnicas avançadas e implementação no mundo real.

Configurando o IndexedDB

Criando e Abrindo um Banco de Dados

Para criar um novo banco de dados no IndexedDB, você usará o método indexedDB.open():

const request = indexedDB.open("MeuBancoDeDados", 1);
request.onupgradeneeded = (event) => {
  const db = event.target.result;
  // Crie lojas de objetos e índices aqui
};

O método aceita dois parâmetros: o nome do banco de dados e o número da versão. Para abrir um banco de dados existente, você pode simplesmente chamar o método sem especificar um número de versão.

Versionamento de Banco de Dados

O IndexedDB suporta versionamento para lidar com mudanças de esquema. Quando você abre um banco de dados com um número de versão mais alto do que o existente, o evento onupgradeneeded é acionado, permitindo que você atualize o esquema do seu banco de dados.

Criando Lojas de Objetos e Índices

Lojas de objetos são contêineres para seus dados no IndexedDB:

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  // Crie uma loja de objetos com um caminho de chave
  const objectStore = db.createObjectStore("clientes", { keyPath: "id" });
  
  // Crie um índice para consultas eficientes
  objectStore.createIndex("nome", "nome", { unique: false });
};

Trabalhando com Transações

Entendendo os Fundamentos das Transações

Transações no IndexedDB mantêm a integridade dos dados agrupando várias operações em uma única unidade atômica. Elas garantem que todas as alterações sejam aplicadas ou nenhuma.

Criando e Gerenciando Transações

const transaction = db.transaction(["clientes"], "readwrite");
const objectStore = transaction.objectStore("clientes");

transaction.oncomplete = (event) => {
  console.log("Transação concluída com sucesso");
};

transaction.onerror = (event) => {
  console.error("Transação falhou");
};

Tratamento de Erros e Reversão

Se uma operação falhar, você pode usar o método abort() para reverter toda a transação:

try {
  // Realizar operações
} catch (error) {
  transaction.abort();
  console.error("Transação abortada:", error);
}

Operações de Dados no IndexedDB

Adicionando Dados

const customerData = { id: "00001", nome: "João Silva", email: "joao@exemplo.com" };
const request = objectStore.add(customerData);

request.onsuccess = (event) => {
  console.log("Dados adicionados com sucesso");
};

Recuperando Dados

Recuperação básica de dados por chave:

const request = objectStore.get("00001");
request.onsuccess = (event) => {
  console.log("Dados do cliente:", request.result);
};

Filtrando com Índices

const index = objectStore.index("nome");
const request = index.get("João Silva");

request.onsuccess = (event) => {
  console.log("Encontrado pelo nome:", request.result);
};

Métodos de Consulta Avançados

Para consultas complexas, o IndexedDB oferece consultas de intervalo e consultas compostas usando métodos como openCursor() e IDBKeyRange:

const range = IDBKeyRange.bound("A", "F"); // Nomes começando com A até F
const request = index.openCursor(range);

Atualizando Registros

const updateData = { id: "00001", nome: "João Pereira", email: "joao@exemplo.com" };
const request = objectStore.put(updateData);

Excluindo Registros

const request = objectStore.delete("00001");
request.onsuccess = (event) => {
  console.log("Registro excluído");
};

Trabalhando com Cursores

Entendendo a Funcionalidade do Cursor

Os cursores permitem iterar eficientemente sobre registros em uma loja de objetos ou índice, fornecendo uma maneira de percorrer e manipular dados.

const request = objectStore.openCursor();
request.onsuccess = (event) => {
  const cursor = event.target.result;
  if (cursor) {
    console.log("Chave:", cursor.key, "Valor:", cursor.value);
    cursor.continue(); // Move para o próximo registro
  } else {
    console.log("Sem mais registros");
  }
};

Modificando Dados Usando Cursores

const transaction = db.transaction(["clientes"], "readwrite");
const objectStore = transaction.objectStore("clientes");
const request = objectStore.openCursor();

request.onsuccess = (event) => {
  const cursor = event.target.result;
  if (cursor) {
    if (cursor.value.status === "inativo") {
      const updateData = cursor.value;
      updateData.status = "ativo";
      cursor.update(updateData);
    }
    cursor.continue();
  }
};

Gerenciamento e Atualizações de Esquema

Atualizando o Esquema do Banco de Dados

Quando sua aplicação evolui, você pode precisar modificar o esquema do seu banco de dados:

const request = indexedDB.open("MeuBancoDeDados", 2); // Aumenta o número da versão

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  
  // Verifica se a loja de objetos existe
  if (!db.objectStoreNames.contains("novaLoja")) {
    db.createObjectStore("novaLoja", { keyPath: "id" });
  }
};

Migrando Dados Durante Atualizações

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  const oldVersion = event.oldVersion;
  
  if (oldVersion < 1) {
    // Configuração da primeira versão
  }
  
  if (oldVersion < 2) {
    // Migrar dados para o novo esquema
    const transaction = event.target.transaction;
    const oldStore = transaction.objectStore("oldStore");
    const newStore = db.createObjectStore("novaLoja", { keyPath: "id" });
    
    oldStore.openCursor().onsuccess = (event) => {
      const cursor = event.target.result;
      if (cursor) {
        newStore.add(cursor.value);
        cursor.continue();
      }
    };
  }
};

Otimização de Performance

Operações em Lote Eficientes

Para melhor desempenho, utilize operações em lote ao lidar com múltiplos registros:

const transaction = db.transaction(["clientes"], "readwrite");
const store = transaction.objectStore("clientes");

// Adiciona múltiplos registros em uma única transação
customerList.forEach(cliente => {
  store.add(cliente);
});

Aproveitando Índices para Consultas Mais Rápidas

Crie índices em propriedades frequentemente consultadas para garantir uma recuperação de dados mais rápida:

objectStore.createIndex("email", "email", { unique: true });
objectStore.createIndex("ultimoLogin", "ultimoLogin", { unique: false });

Melhores Práticas de Gerenciamento de Conexão

Abra conexões apenas quando necessário e feche-as quando terminar:

let db;

function openDB() {
  const request = indexedDB.open("MeuBancoDeDados", 1);
  request.onsuccess = (event) => {
    db = event.target.result;
  };
  return request;
}

// Quando você terminar com o banco de dados
function closeDB() {
  if (db) {
    db.close();
    db = null;
  }
}

Exemplo do Mundo Real: Gerenciador de Tarefas com Suporte Offline

Configuração do Banco de Dados

const request = indexedDB.open("GerenciadorDeTarefasDB", 1);

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  const taskStore = db.createObjectStore("tarefas", { keyPath: "id", autoIncrement: true });
  
  // Crie índices para consultas
  taskStore.createIndex("status", "status", { unique: false });
  taskStore.createIndex("dataVencimento", "dataVencimento", { unique: false });
};

Adicionando Tarefas

function addTask(taskData) {
  const transaction = db.transaction(["tarefas"], "readwrite");
  const taskStore = transaction.objectStore("tarefas");
  
  return new Promise((resolve, reject) => {
    const request = taskStore.add({
      title: taskData.title,
      description: taskData.description,
      status: "pendente",
      dueDate: taskData.dueDate,
      createdAt: new Date()
    });
    
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
  });
}

Recuperando e Exibindo Tarefas

function getAllTasks() {
  const transaction = db.transaction(["tarefas"], "readonly");
  const taskStore = transaction.objectStore("tarefas");
  
  return new Promise((resolve, reject) => {
    const request = taskStore.openCursor();
    const tarefas = [];
    
    request.onsuccess = (event) => {
      const cursor = event.target.result;
      if (cursor) {
        tarefas.push(cursor.value);
        cursor.continue();
      } else {
        resolve(tarefas);
      }
    };
    
    request.onerror = () => reject(request.error);
  });
}

Atualizando e Excluindo Tarefas

function updateTaskStatus(id, newStatus) {
  const transaction = db.transaction(["tarefas"], "readwrite");
  const taskStore = transaction.objectStore("tarefas");
  
  return new Promise((resolve, reject) => {
    const getRequest = taskStore.get(id);
    
    getRequest.onsuccess = () => {
      const tarefa = getRequest.result;
      tarefa.status = newStatus;
      tarefa.updatedAt = new Date();
      
      const updateRequest = taskStore.put(tarefa);
      updateRequest.onsuccess = () => resolve(true);
      updateRequest.onerror = () => reject(updateRequest.error);
    };
    
    getRequest.onerror = () => reject(getRequest.error);
  });
}

Sincronizando com o Servidor

function syncWithServer() {
  if (!navigator.onLine) {
    return Promise.reject(new Error("Sem conexão com a internet"));
  }
  
  return getAllTasks()
    .then(tarefas => {
      // Filtrar tarefas que precisam ser sincronizadas
      const unsynced = tarefas.filter(tarefa => !tarefa.synced);
      
      // Enviar para o servidor usando a API fetch
      return fetch('https://api.exemplo.com/tarefas/sync', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(unsynced)
      });
    })
    .then(response => response.json())
    .then(result => {
      // Marcar tarefas como sincronizadas
      const transaction = db.transaction(["tarefas"], "readwrite");
      const taskStore = transaction.objectStore("tarefas");
      
      result.syncedIds.forEach(id => {
        const request = taskStore.get(id);
        request.onsuccess = () => {
          const tarefa = request.result;
          tarefa.synced = true;
          taskStore.put(tarefa);
        };
      });
      
      return result;
    });
}

// Ouvir eventos online para sincronizar automaticamente
window.addEventListener('online', syncWithServer);

Integrando Apidog para Gerenciamento de API com IndexedDB

Ao construir aplicações com IndexedDB para armazenamento do lado do cliente, você frequentemente precisará interagir com APIs de back-end para sincronização de dados. O Apidog fornece uma solução perfeita para gerenciar essas interações de API.

Por que o Apidog Melhora o Desenvolvimento do IndexedDB

Enquanto você desenvolve aplicações com capacidade offline usando o IndexedDB, o Apidog oferece vários benefícios:

Sincronização em Tempo Real: O Apidog garante que seus endpoints de API usados para sincronização de dados estejam sempre corretamente configurados e testados, eliminando problemas de integração quando sua aplicação vai online.

Respostas de API Falsas: Ao desenvolver funcionalidade offline, os motores de simulação inteligentes do Apidog permitem que você simule respostas de API para testar sua lógica de sincronização do IndexedDB sem um servidor ativo.

Design Colaborativo de API: Quando sua equipe está simultaneamente trabalhando no armazenamento do front-end e nas APIs de back-end, o Apidog facilita a colaboração em tempo real nas especificações da API.

Ao integrar o Apidog em seu fluxo de trabalho de desenvolvimento, você cria uma ponte sem costura entre o armazenamento do lado do cliente e o processamento do lado do servidor, tornando sua aplicação mais robusta e mais fácil de manter.

button

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs

Um Guia para Iniciantes sobre como Usar as APIs do IndexedDB