Nhiều ứng dụng web cần dùng database để lưu trữ thông tin người dùng hoặc cấu hình. Thế nhưng bạn đã biết rằng có sẵn một database trong mọi trình duyệt web hiện đại chưa?
IndexedDB là một database NoSQL bên client, cho phép bạn lưu trữ và truy xuất dữ liệu được cấu trúc trong trình duyệt web của người dùng.
IndexedDB mang tới một số lợi thế như bộ nhớ lớn hơn, lưu trữ và truy xuất dữ liệu offline so với các lựa chọn lưu trữ khác. Dưới đây là cách dùng IndexedDB làm database.
Thiết lập database
Để tạo database, bạn phải tạo một truy vấn mở bằng open của IndexedDB. Hàm open trả về một đối tượng IDBOpenDBRequest. Đối tượng này cung cấp truy cập tới success, error và upgradeneeded từ hành động mở.
Phương thức open tính 2 đối số: tên và số phiên bản (tùy chọn). Tên của đối số đại diện cho tên của database. Số phiên bản xác định phiên bản của database mà ứng dụng muốn làm việc cùng. Giá trị mặc định ở đây là 0.
Dưới đây là cách tạo một truy vấn mở:
const openRequest = indexedDB.open("usersdb", 1);
Sau khi tạo truy vấn mở, bạn cần lắng nghe và xử lý các sự kiện trên đối tượng được trả về.
Sự kiện success xảy ra khi bạn tạo database thành công. Sau khi được triển khai, bạn truy cập tới đối tượng database qua event.target.result:
openRequest.onsuccess = function (event) {
const db = event.target.result;
console.log("Database created", db);
};
Ví dụ trên xử lý một sự kiện thành công bằng cách đăng nhập đối tượng database.
Sự kiện error xảy ra nếu IndexedDB gặp phải vấn đề khi tạo database. Bạn có thể xử lý nó bằng cách ghi lại lỗi cho console hoặc dùng các phương thức sửa lỗi khác.
openRequest.onerror = function (event) {
// ...
};
Sự kiện upgradeneeded xảy ra khi bạn tạo database lần đầu tiên hoặc khi update phiên bản của nó. Nó chỉ kích hoạt một lần nên đây là nơi lý tưởng để tạo một lưu trữ đối tượng.
Tạo lưu trữ đối tượng
Một lưu trữ đối tượng tương tự như một bảng ở database quan hệ bên server. Bạn có thể dùng nó để chứa các cặp giá trị.
Bạn nên tạo object store đáp ứng sự kiện upgradeneeded. Sự kiện này kích hoạt khi bạn tạo một phiên bản mới của database hoặc nâng cấp phiên bản hiện tại. Phương thức này lấy tên của lưu trữ đối tượng và một đối tượng cấu hình làm đối số.
Trong đối tượng cấu hình, bạn phải xác định khóa cơ bản bằng cách định nghĩa một key path - thuộc tính luôn tồn tại và chứa một giá trị riêng biệt. Ngoài ra, bạn có thể dùng một trình tạo key bằng cách đặt thuộc tính keyPath sang “id” và autoIncrement sang true trong đối tượng cấu hình.
Ví dụ:
openRequest.onupgradeneeded = function (event) {
const db = event.target.result;
// Tạo object store
const userObjectStore = db.createObjectStore("userStore", {
keyPath: "id",
autoIncrement: true,
});
}
Ví dụ này tạo một lưu trữ đối tượng tên “userStore” trong database và đặt khóa chính là một id tăng tự động.
Xác định chỉ mục
Trong IndexedDB, index là cách sắp xếp và truy xuất dữ liệu hiệu quả hơn. Nó cho phép tìm kiếm lưu trữ đố tượng và phân loại nó dựa trên các thuộc tính được index.
Để định nghĩa một index trên lưu trữ đối tượng, dùng phương thức createIndex() của một đối tượng object store. Phương thức này lấy tên index, tên thuộc tính và đối tượng cấu hình làm đối số:
userObjectStore.createIndex("name", "name", { unique: false });
userObjectStore.createIndex("email", "email", { unique: true });
Khối code trên định nghĩa 2 index, “name” và “email” trên userObjectStore. Index “name” là non-unique, nghĩa là nhiều đối tượng có thể có cùng giá trị tên, còn index “email” là unique, đảm bảo không có hai đối tượng có cùng giá trị email.
Dưới đây là một ví dụ về cách bạn có thể xử lý sự kiện upgradeneeded:
openRequest.onupgradeneeded = function (event) {
const db = event.target.result;
// Tạo object store
const userObjectStore = db.createObjectStore("userStore", {
keyPath: "id",
autoIncrement: true,
});
// Tạo các chỉ mục
userObjectStore.createIndex("name", "name", { unique: false });
userObjectStore.createIndex("email", "email", { unique: true });
};
Thêm dữ liệu vào IndexedDB
Một giao dịch trong IndexedDB là cách nhóm nhiều thao tác đọc và viết thành một. Để đảm bảo tính nhất quán và toàn vẹn của dữ liệu, nếu một trong số các hoạt động ở một giao dịch bị thất bại, IndexedDB hoàn lại tất cả các thao tác.
Để thêm dữ liệu vào database IndexedDB, bạn cần tạo giao dịch trên lưu trữ đối tượng muốn thêm vào dữ liệu, rồi dùng add() trên giao dịch để thêm dữ liệu.
Bạn có thể tạo một giao dịch bằng cách gọi transaction trên đối tượng database. Phương thức này lấy 2 đối số: tên của datastore và chế độ của transaction mà có thể là readonly (mặc định) hoặc readwrite.
Sau đó, gọi objectStore() trên giao dịch và chuyển tên của lưu trữ đối tượng bạn muốn để thêm dữ liệu vào. Phương thức này trả về một tham chiếu tới lưu trữ đối tượng.
Cuối cùng, gọi add() trên lưu trữ đối tượng và chuyển dữ liệu bạn muốn thêm vào:
const addUserData = (userData, db) => {
// Mở transaction
const transaction = db.transaction("userStore", "readwrite");
// Thêm dữ liệu vào object store
const userObjectStore = transaction.objectStore("userStore");
// Tạo truy vấn để thêm userData
const request = userObjectStore.add(userData);
// Xử lý sự kiện thành công
request.onsuccess = function (event) {
//...
};
// Xử lý lỗi
request.onerror = function (event) {
//...
};
};
Hàm này tạo một giao dịch với lưu trữ đối tượng “userStore” và đặt chế độ sang “readwrite”. Sau đó, nó lấy lưu trữ đối tượng và thêm userData vào bằng phương thức add.
Truy xuất dữ liệu từ IndexedDB
Để truy xuất dữ liệu từ database IndexedDB, bạn cần tạo một giao dịch trên lưu trữ đối tượng muốn lấy dữ liệu, rồi dùng get() hoặc getAll() trên giao dịch đó để lấy dữ liệu phụ thuộc vào số lượng dữ liệu bạn muốn truy xuất.
Get() lấy một giá trị cho khóa chính của đối tượng bạn muốn truy xuất và trả về đối tượng bằng khóa tương ứng từ lưu trữ đối tượng.
getAll() trả về tất cả dữ liệu trong một lưu trữ đối tượng. Nó cũng lấy hằng số tùy chọn làm đối số và trả về tất cả dữ liệu phù hợp từ store.
const getUserData = (id, db) => {
const transaction = db.transaction("userStore", "readonly");
const userObjectStore = transaction.objectStore("userStore");
// Tạo truy vấn để lấy dữ liệu
const request = userObjectStore.get(id);
request.onsuccess = function (event) {
console.log(request.result);
};
request.onerror = function (event) {
// Xử lý lỗi
};
};
Hàm này tạo một giao dịch với lưu trữ đối tượng “userStore” và đặt chế độ readonly. Tiếp theo, nó truy xuất dữ liệu người dùng với id trùng khớp từ lưu trữ đối tượng.
Update dữ liệu với IndexedDB
Để update dữ liệu trong IndexedDB, bạn cần tạo một giao dịch với chế độ “readwrite”. Tiếp tục bằng cách truy xuất đối tượng bạn muốn cập nhật bằng phương thức get(). Sau đó, chỉnh sửa đối tượng và gọi put() trên lưu trữ đối tượng để lưu đối tượng được cập nhật trở lại database.
const updateUserData = (id, userData, db) => {
const transaction = db.transaction("userStore", "readwrite");
const userObjectStore = transaction.objectStore("userStore");
// Tạo truy vấn để lấy dữ liệu
const getRequest = userObjectStore.get(id);
// Xử lý sự kiện thành công
getRequest.onsuccess = function (event) {
// Lấy dữ liệu người dùng thành công
const user = event.target.result;
// Cập nhật dữ liệu người dùng
user.name = userData.name;
user.email = userData.email;
// Tạo truy vấn để update dữ liệu
const putRequest = userObjectStore.put(user);
putRequest.onsuccess = function (event) {
// Xử lý thành công
};
putRequest.onerror = function (event) {
// Xử lý lỗi
};
};
getRequest.onerror = function (event) {
// Xử lý lỗi
};
};
Hàm này tạo một giao dịch để lấy và update dữ liệu của database.
Xóa dữ liệu từ IndexedDB
Để xóa dữ liệu từ IndexedDB, bạn cần tạo một giao dịch ở chế độ readwrite. Sau đó, gọi delete trên lưu trữ đối tượng để loại bỏ nó khỏi database:
const deleteUserData = (id, db) => {
const transaction = db.transaction("userStore", "readwrite");
const userObjectStore = transaction.objectStore("userStore");
// Tạo truy vấn để xóa dữ liệu
const request = userObjectStore.delete(id);
request.onsuccess = function (event) {
// Xử lý thành công
};
request.onerror = function (event) {
// Xử lý lỗi
};
};
Hàm này tạo một giao dịch xóa dữ liệu với id tương ứng từ lưu trữ đối tượng.
Trên đây là cách dùng IndexedDB làm database. Hi vọng bài viết hữu ích với các bạn.