15

Fundamentals of storing data in the browser with IndexedDB

 4 years ago
source link: https://www.tuicool.com/articles/hit/FzeaIjM
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

IndexedDBis another API meant for client-side storage. It is good for storing a significant amount of data, including files. IndexedDB is more suitable than WebStorage suitable for keeping structured data, and definitely more adequate for that than cookies. In this article, we go through the main concepts of IndexedDB.

Basic concepts of IndexedDB

IndexedDB is object-oriented and requires you to create an object store for data and put objects inside. Objects stored and retrieved are indexed by the IndexedDB with a key All of the objects belong in the  object stores that act as wrappers for them.

It is a transactional database system, any operation meant to read or write to the database is enclosed in a transaction , which is meant to ensure database integrity. The API is asynchronous and uses events. The IndexedDB  is very fast, especially when  reading data.

All major browsers support IndexedDB, but you might first want to check if it is available to avoid errors.

If you would like to read about other ways of storing data in the browser, check out my articles aboutcookies and theWeb Storage API

Working with IndexedDB

The database is available in the global window object through the indexedDB property. The first thing to do is to call the  open function that will open a connection to the database. This operation performs asynchronously. It returns a request object that we can use to listen to upcoming events.

const openRequest = window.indexedDB.open('toDoList');

upgradeneeded event

The upgradeneeded event fires when a database of a bigger version number than the existing one loads. It happens when you load your database for the first time. The  upgradeneeded callback is the place to create new  object stores , so if you want to add some new ones to an already existing database, you need to create a new version. You can do it by passing an additional argument to the  open function

If you call the window.indexedDB.open method with the same version as before, the upgradeneeded  event will not fire!

The object store   is a storage mechanism for storing data in a database. The  createObjectStore function creates a new object store. It takes two arguments: the  name of the store and  options .

This is the time to mention a very important topic: keys . Every record in the database needs to have a unique key . There are two types of them:

out-of-line key

This is a key stored separately from the value. You can either generate it automatically by passing autoIncrement : true to the createObjectStore  function, or define it every time you add an item to the database, which we will cover in a second.

const openRequest = window.indexedDB.open('toDoList');
 
openRequest.addEventListener('upgradeneeded', (event) => {
  const database = event.target.result;
  database.createObjectStore('TaskStore', {
    autoIncrement: true
  })
});

in-line key

That’s a key stored as a part of the value. You define it by passing the keyPath property to the  createObjectStore  function.

const openRequest = window.indexedDB.open('toDoList');
 
openRequest.addEventListener('upgradeneeded', (event) => {
  const database = event.target.result;
  database.createObjectStore('TaskStore', {
    keyPath: 'title'
  })
});

Keep in mind that every key has to be unique. With the code above you will not be able to add two tasks with the same title, even for different people assigned. You can fix that by passing an array to the keyPath so that the  key will be a combination of more than one property.

const openRequest = window.indexedDB.open('toDoList');
 
openRequest.addEventListener('upgradeneeded', (event) => {
  const database = event.target.result;
  database.createObjectStore('TaskStore', {
    keyPath: ['title', 'assignedTo']
  })
});

Thanks to that, two tasks will be unique even if they have the same title, but different people assigned.

success event

When the connection establishes and the upgradeneeded callback finishes, the  success fires. This means that you are free to create a  transaction and send some data!

const openRequest = window.indexedDB.open('toDoList');
 
openRequest.addEventListener('upgradeneeded', (event) => {
  const database = event.target.result;
  database.createObjectStore('TaskStore', {
    autoIncrement: true
  })
});
 
openRequest.addEventListener('success', (event) => {
  const database = event.target.result;
  const transaction = database.transaction('TaskStore', 'readwrite');
  const store = transaction.objectStore('TaskStore');
  store.add({
    name: 'Wash the dishes',
    assignedTo: 'Marcin'
  });
});

The database.transaction function returns a transaction object. You can call the  objectStore function to get to any store that you created in the  upgradeneeded event callback.

An important argument of the database.transaction is the  mode . By default, it is readonly . This mode is faster but prevents you from saving any data to the database.

In the example above we passed the value to the add function. Since we use the key generator here through the  autoIncrement : true , it is all that we need.

If you choose to use out-of-line keys without the autoIncrement , you need to pass the key as a second argument.

const openRequest = window.indexedDB.open('toDoList');
 
openRequest.addEventListener('upgradeneeded', (event) => {
  const database = event.target.result;
  database.createObjectStore('TaskStore')
});
 
openRequest.addEventListener('success', (event) => {
  const database = event.target.result;
  const transaction = database.transaction('TaskStore', 'readwrite');
  const store = transaction.objectStore('TaskStore');
  store.add({
    name: 'Wash the dishes',
    assignedTo: 'Marcin'
  }, 1);
});

It does not need to be a number, it can also be a string.

Put and delete functions

If you use autoIncrement, you do not provide the key directly. You need to obtain it to modify the existing value. Since the  store.add function returns a request object, you can easily get to the  key of your new database record through it. It might prove to be useful when trying to modify existing records.

openRequest.addEventListener('success', (event) => {
  const database = event.target.result;
  const transaction = database.transaction('TaskStore', 'readwrite');
  const store = transaction.objectStore('TaskStore');
  const addRequest = store.add({
    name: 'Wash the dishes',
    assignedTo: 'Marcin'
  });
  addRequest.addEventListener('success', (event) => {
    const key = event.target.result;
    store.put({
      name: 'Wash the dishes',
      assignedTo: 'Maciek'
    }, key);
  })
});

The put function works similar to add , but it updates a given record when provided with an already existing  key . This is why it is called an  update or insert function.

Same goes for the delete  function. It will delete a record if provided with a proper key.

Accessing values

There are two basic ways to access values from a store. The first one is the getAll function that retrieves all objects in the store.

openRequest.addEventListener('success', (event) => {
  const database = event.target.result;
  const transaction = database.transaction('TaskStore', 'readwrite');
  const store = transaction.objectStore('TaskStore');
  const addRequest = store.add({
    name: 'Wash the dishes',
    assignedTo: 'Marcin'
  });
  addRequest.addEventListener('success', () => {
    const getRequest = store.getAll();
    getRequest.addEventListener('success', (event) => {
      console.log('Tasks', event.target.result)
    });
  })
});

The other function is the get function that works similar but needs the key as the argument. It retrieves one objects matching the key.

The getAll function can also accept a search query as an argument, but this is a material for a separate article.

Summary

IndexedDBcan be a very good solution for storing and accessing a lot of structured data. In this article, we covered all the fundamentals of using it. It included the basic concepts of how it works, how to open the connection, how to deal with events and how to save and access data. There is a good chance that you didn’t like the event-based nature of working with the IndexDB.  If that’s the case, you might be interested in a library  IndexedDB Promised that is recommended by the developers working in Google .


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK