본문 바로가기

Frontend/JavaScript

Client Side Storage - Indexed DB

반응형

indexed db is a client storage. It is similar to web storage but can save more data than web storage.

 

Think of indexed db as a database where you can create, add, edit, and delete. For every tasks, you have to connect to the database first.

Create

open(“dbName”, version): this opens a database or creates one if not exists. The schema of a database differs by the version.

const req = window.indexedDB.open("cars", 3);

open command not actually opens the database to work with. Instead, it returns if the request was successful or not. All the CRUD operation is done using ‘success’ event except for creation and version change which uses ‘.onupgradeneeded’

// seed data
const carData = [
  { id: "1", year: "2000", model: "BMW" },
  { id: "2", year: "2001", model: "Toyota" }
];

// access request
const req = indexedDB.open("cars", 3);
req.onerror = (event) => { 
  // Handle errors.
};
req.onupgradeneeded = (event) => { // if access was successful 
  const db = event.target.result;
  // creates an object (like a table in a relational database)
  const objectStore = db.createObjectStore("cars", { keyPath: "id" });
  
  // creates indices(optional)
  objectStore.createIndex("year", "year", { unique: false });
  objectStore.createIndex("model", "model", { unique: false });
  // checks if the object is created
  objectStore.transaction.oncomplete = (event) => {
    const carObjectStore = db.transaction("cars", "readwrite").objectStore("cars");
    carData.forEach((car) => {
      carObjectStore.add(car);
    });
  };
};

To check, open the developer tool -> select ‘Application’

Note that ‘year’ and ‘model’ under the ‘cars’ won be created if you omit the ‘createIndex’ code.

For the primary key, there is a auto increment option too.

const carData = [
  { year: "2000", model: "BMW" },
  { year: "2001", model: "Toyota" }
];

const req = indexedDB.open("cars", 3);
req.onerror = (event) => { 
  // Handle errors.
};
req.onupgradeneeded = (event) => { 
  const db = event.target.result;
  // with auto increment
  const objectStore = db.createObjectStore("cars", { keyPath: 'id', autoIncrement: true });  
    
  objectStore.createIndex("year", "year", { unique: false });
  objectStore.createIndex("model", "model", { unique: false });
  
  objectStore.transaction.oncomplete = (event) => {
    const carObjectStore = db.transaction("cars", "readwrite").objectStore("cars");
    carData.forEach((car) => {
      carObjectStore.add(car);
    });
  };
};

 

For other tasks, we use transaction([“objectName”], “mode”). Second parameter of transaction has three options “readonly” (this is the default if omitted) for read, “readwrite” for add, update, and delete, and “versionchange” to change the version.

Read

1. Getting an item (A searched value must be unique)

const req = indexedDB.open("cars", 3);

req.onerror = (event) => { 
  // Handle errors.
};

req.onsuccess = (event) => { 
  const db = event.target.result;
  const transaction = db.transaction(["cars"]);
  const objectStore = transaction.objectStore("cars");
  const data = objectStore.get("1");
  
  data.onerror = (event) => {
  	// Handle errors
  };

  data.onsuccess = (event) => {    
    console.log(`BMW - ${data.result.year}`);        
  };
};

The same result but with a different syntax.

const req = indexedDB.open("cars", 3);

req.onerror = (event) => { 
  // Handle errors.
};

req.onsuccess = (event) => { 
  const db = event.target.result;
  db.transaction("cars").objectStore("cars").get("1").onsuccess = (event) => {
    console.log(`BMW - ${event.target.result.year}`);
  };
};

2. Getting an item with an index (It returns the first match if there are many)

const req = indexedDB.open("cars", 3);

req.onerror = (event) => { 
  // Handle errors.
};

req.onsuccess = (event) => { 
  const db = event.target.result;
  const transaction = db.transaction(["cars"]);
  const objectStore = transaction.objectStore("cars");
  const index = objectStore.index("model");

  index.get("BMW").onsuccess = (event) => {
    console.log(`BMW - ${event.target.result.year}`);
  };
};

3. Getting all the items

 

To get all items we use .openCursor(). This returns the data in ascending order by default. If you want to change to descending order use .openCursor(“prev”).

const req = indexedDB.open("cars", 3);

req.onerror = (event) => { 
  // Handle errors.
};

req.onsuccess = (event) => { 
  const db = event.target.result;
  const objectStore = db.transaction("cars").objectStore("cars");

  objectStore.openCursor().onsuccess = (event) => {
    const cursor = event.target.result;
    if (cursor) {
      console.log(`${cursor.key} is ${cursor.value.model}`);
      cursor.continue();
    } else {
      console.log("Done");
    }
  };
};

Adding a range

// Only -> exact match
const singleKeyRange = IDBKeyRange.only("BMW");
// From the value specified
const lowerBoundKeyRange = IDBKeyRange.lowerBound("BMW");
// After the value specified
const lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("BMW", true);
// Before the value specified
const upperBoundOpenKeyRange = IDBKeyRange.upperBound("Ford", true);
// bound("startRange", "endRange", startRange exclusion, endRange exclusion)
const boundKeyRange = IDBKeyRange.bound("1", "3", false, true);
const req = indexedDB.open("cars", 3);
req.onerror = (event) => { 
  // Handle errors.
};
req.onsuccess = (event) => { 
  const db = event.target.result;
  const objectStore = db.transaction("cars").objectStore("cars");
  
  objectStore.openCursor(boundKeyRange).onsuccess = (event) => {
    const cursor = event.target.result;
    if (cursor) {      
      if (cursor) {
        // action console.log(`${cursor.key} is ${cursor.value.model}`);        
        cursor.continue();
      } else {
        console.log("Done");
      }
    }
  };  
};

Adding an item

const newCarData = [
  { id: "3", year: "2020", model: "Ford" },  
];
const req = indexedDB.open("cars", 3);
req.onerror = (event) => { 
  // Handle errors.
};
req.onsuccess = (event) => { 
  const db = event.target.result;
  const transaction = db.transaction(["cars"], "readwrite");
  const objectStore = transaction.objectStore("cars");
  transaction.oncomplete = (event) => {
    // complete
  };
  transaction.onerror = (event) => {
    // handle errors
  };
    
  newCarData.forEach((car) => {
    const req = objectStore.add(car);    
    
    req.onerror = (event) => {
      alert("failed");
    };
    
    req.onsuccess = (event) => {
      alert("a new car added");
    };
  });
};

Make sure that you don’t omit the required value such as primary key. This won’t give you an error so hard to debug. You can omit optional values like shown below.

Update

const req = indexedDB.open("cars", 3);

req.onerror = (event) => { 
  // Handle errors.
};

req.onsuccess = (event) => { 
  const db = event.target.result;
  const objectStore = db.transaction(["cars"], "readwrite").objectStore("cars");  

  const request = objectStore.get("1");
  request.onerror = (event) => {
    // Handle errors
  };
  
  request.onsuccess = (event) => {
    // Get the old value that we want to update
    const data = event.target.result;

    // update
    data.year = 1942;

    // Put this updated object back into the database.
    const requestUpdate = objectStore.put(data);
    
    requestUpdate.onerror = (event) => {
      alert("failed")
    };
    
    requestUpdate.onsuccess = (event) => {
      alert("Updated")
    };
  };
};

Be mindful about the type as there is no type restriction

Delete

const req = indexedDB.open("cars", 3);

req.onerror = (event) => { 
  // Handle errors.
};

req.onsuccess = (event) => { 
  const db = event.target.result;  
  
  const request = db.transaction(["cars"], "readwrite")
  .objectStore("cars")
  .delete("3");
  request.onsuccess = (event) => {
    alert("deleted")
  };  
};

We have seen indexed db in this writing.


References

 

IndexedDB API - Web APIs | MDN

IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. This API uses indexes to enable high-performance searches of this data. While Web Storage is useful for storing smaller amounts of data,

developer.mozilla.org

 

728x90
반응형

'Frontend > JavaScript' 카테고리의 다른 글

NPM - JSON Server  (0) 2023.05.22
JavaScript Module - Text Wave Effect  (2) 2023.04.05
Client Side Storage - Web Storage  (0) 2023.03.31
Module System  (0) 2023.03.29
Webpack  (1) 2023.03.29