JavaScript ve IndexedDB
Bu makalede JavaScript ve IndexedDB'yi açıklayacağız.
Bu eğitim, her adımda pratik örnek kodlar içeren, JavaScript ve IndexedDB
'nin adım adım açıklamasını sunar ve anlayışınızı derinleştirmenize yardımcı olur.
YouTube Video
javascript-indexed-db.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JavaScript & HTML</title>
6 <style>
7 * {
8 box-sizing: border-box;
9 }
10
11 body {
12 margin: 0;
13 padding: 1em;
14 padding-bottom: 10em;
15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16 background-color: #f7f9fc;
17 color: #333;
18 line-height: 1.6;
19 }
20
21 .container {
22 max-width: 800px;
23 margin: 0 auto;
24 padding: 1em;
25 background-color: #ffffff;
26 border: 1px solid #ccc;
27 border-radius: 10px;
28 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
29 }
30
31 .container-flex {
32 display: flex;
33 flex-wrap: wrap;
34 gap: 2em;
35 max-width: 1000px;
36 margin: 0 auto;
37 padding: 1em;
38 background-color: #ffffff;
39 border: 1px solid #ccc;
40 border-radius: 10px;
41 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
42 }
43
44 .left-column, .right-column {
45 flex: 1 1 200px;
46 min-width: 200px;
47 }
48
49 h1, h2 {
50 font-size: 1.2rem;
51 color: #007bff;
52 margin-top: 0.5em;
53 margin-bottom: 0.5em;
54 border-left: 5px solid #007bff;
55 padding-left: 0.6em;
56 background-color: #e9f2ff;
57 }
58
59 button {
60 display: block;
61 margin: 1em auto;
62 padding: 0.75em 1.5em;
63 font-size: 1rem;
64 background-color: #007bff;
65 color: white;
66 border: none;
67 border-radius: 6px;
68 cursor: pointer;
69 transition: background-color 0.3s ease;
70 }
71
72 button:hover {
73 background-color: #0056b3;
74 }
75
76 #output {
77 margin-top: 1em;
78 background-color: #1e1e1e;
79 color: #0f0;
80 padding: 1em;
81 border-radius: 8px;
82 min-height: 200px;
83 font-family: Consolas, monospace;
84 font-size: 0.95rem;
85 overflow-y: auto;
86 white-space: pre-wrap;
87 }
88
89 .highlight {
90 outline: 3px solid #ffc107; /* yellow border */
91 background-color: #fff8e1; /* soft yellow background */
92 transition: background-color 0.3s ease, outline 0.3s ease;
93 }
94
95 .active {
96 background-color: #28a745; /* green background */
97 color: #fff;
98 box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
99 transition: background-color 0.3s ease, box-shadow 0.3s ease;
100 }
101 </style>
102</head>
103<body>
104 <div class="container">
105 <h1>JavaScript Console</h1>
106 <button id="executeBtn">Execute</button>
107 <div id="output"></div>
108 </div>
109
110 <script>
111 // Override console.log to display messages in the #output element
112 (function () {
113 // Override console.log
114 const originalLog = console.log;
115 console.log = function (...args) {
116 originalLog.apply(console, args);
117 const message = document.createElement('div');
118 message.textContent = args
119 .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
120 .join(" ");
121 output.appendChild(message);
122 };
123
124 // Override console.error
125 const originalError = console.error;
126 console.error = function (...args) {
127 originalError.apply(console, args);
128 const message = document.createElement('div');
129 message.textContent = args
130 .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
131 .join(" ");
132 message.style.color = 'red'; // Color error messages red
133 output.appendChild(message);
134 };
135 })();
136
137 document.getElementById('executeBtn').addEventListener('click', () => {
138 // Prevent multiple loads
139 if (document.getElementById('externalScript')) return;
140
141 const script = document.createElement('script');
142 script.src = 'javascript-indexed-db.js';
143 script.id = 'externalScript';
144 //script.onload = () => console.log('javascript-indexed-db.js loaded and executed.');
145 //script.onerror = () => console.log('Failed to load javascript-indexed-db.js.');
146 document.body.appendChild(script);
147 });
148 </script>
149</body>
150</html>
JavaScript ve IndexedDB
IndexedDB
, tarayıcılara yerleşik olan asenkron bir anahtar-değer depo veritabanıdır. İlişkisel veritabanlarına benzer özellikler sunar ve istemci tarafında büyük miktarda yapılandırılmış veriyi saklamanızı ve aramanızı sağlar. Özellikle çevrimdışı çalışabilen uygulamalar ve PWAs (Aşamalı Web Uygulamaları) için oldukça faydalıdır.
IndexedDB
'nin Özellikleri
- Asenkron ve olay tabanlı bir şekilde çalışır.
- JavaScript nesneleri nesne depolarında saklanabilir.
- Sorgular veya indeksler ile arama yapmak mümkündür.
- Büyük bir depolama kapasitesine sahiptir (yüzlerce MB ve üzeri), böylece çerezlerden veya localStorage'dan çok daha fazla veri saklayabilirsiniz.
Veritabanı Açma ve Oluşturma
IndexedDB
kullanmak için öncelikle bir veritabanı açmalısınız. Eğer mevcut değilse, otomatik olarak oluşturulur.
1const request = indexedDB.open('MyDatabase', 1); // Specify DB name and version
open
metodu, veritabanını asenkron olarak açar ve aşağıdaki üç olayı tetikler.
onsuccess
1// Fired when database is successfully opened
2request.onsuccess = (event) => {
3 const db = event.target.result; // Database instance
4 console.log('Database opened successfully:', db.name);
5};
onsuccess
olayı, veritabanı başarıyla açıldığında tetiklenir. Bundan sonraki işlemler, bu noktada kullanılabilenrequest.result
kullanılarak yapılmalıdır.
onerror
1// Fired when database fails to open
2request.onerror = (event) => {
3 console.error('Failed to open database:', event.target.error);
4};
onerror
olayı, veritabanı açılamadığında tetiklenir. Hata kaydı tutma ve hata yönetimi burada gerçekleştirilmelidir.
onupgradeneeded
1// Fired when database is newly created or upgraded
2request.onupgradeneeded = (event) => {
3 const db = event.target.result;
4 console.log('Database upgrade needed (or newly created):', db.name);
5
6 // Example: Create an object store (like a table) if it doesn’t exist
7 if (!db.objectStoreNames.contains('users')) {
8 db.createObjectStore('users', { keyPath: 'id' });
9 console.log('Object store "users" created');
10 }
11};
onupgradeneeded
, veritabanı yeni oluşturulduğunda veya belirtilen sürüm mevcut sürümden yüksek olduğunda tetiklenir. Tablo (nesne deposu) oluşturma ve şema tanımlama bu aşamada yapılmalıdır.
Nesne Deposu Oluşturma
Öncelikle, onupgradeneeded
içerisinde bir 'nesne deposu' (tabloya eşdeğer) oluşturun.
1request.onupgradeneeded = function (event) {
2 const db = event.target.result;
3 console.log("onupgradeneeded triggered. Database version:", db.version);
4 if (!db.objectStoreNames.contains('users')) {
5 console.log("Creating object store: users");
6 const store = db.createObjectStore('users', { keyPath: 'id' });
7 store.createIndex('name', 'name', { unique: false });
8 }
9};
Burada aşağıdaki ayarlar uygulanır:.
-
createObjectStore
createObjectStore
, veritabanında yeni bir nesne deposu oluşturmak için kullanılan bir yöntemdir. Nesne deposunda kayıtlar saklayabilir vekeyPath
ve diğer seçenekleri belirleyerek verinin nasıl yönetileceğini tanımlayabilirsiniz. Bu işlem mutlakaonupgradeneeded
olayı içerisinde yapılmalıdır. -
keyPath: 'id'
keyPath
olarakid
özelliğini belirleyin, böylece her kayıt birincil anahtar olarak benzersiz şekilde tanımlanır. Bu sayede veri eklerken, ararken veya güncellerkenid
otomatik olarak kullanılır. -
createIndex
createIndex
metodunu kullanarak arama içinname
özelliğine dayalı bir indeks oluşturun.unique: false
ayarlanarak, aynıname
değerine sahip birden çok kayda izin verilir.
Başarılı Veritabanı Bağlantılarını Yönetme
Başarılı bir veritabanı bağlantısında yürütülecek işlemleri onsuccess
olayına atayın. Bu işlemde veritabanı örneğini alır ve veri okuma/yazma için hazırlık yaparsınız.
1request.onsuccess = function (event) {
2 const db = event.target.result;
3 console.log('Database opened successfully');
4 // Use db for reading and writing in subsequent operations
5};
-
Bu işlem,
IndexedDB
veritabanı bağlantısı başarıyla tamamlandığında çalışır. -
event.target.result
, işlemlerin başlatılması ve nesne depolarına erişim için kullanılan açılmış veritabanı örneğini (IDBDatabase
nesnesi) içerir. -
Veri ekleme, alma, güncelleme ve silme gibi gerçek okuma ve yazma işlemleri
db
nesnesi kullanılarak gerçekleştirilir.
Bu noktada veritabanı hazırdır, böylece işlemlere güvenle başlayabilirsiniz.
Veri Ekleme
IndexedDB
'ye yeni veri eklemenin yolu şöyledir.
1function addUser(db, user) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.add(user);
5
6 request.onsuccess = () => {
7 console.log('User added:', user);
8 };
9
10 request.onerror = () => {
11 console.error('Add failed:', request.error);
12 };
13}
14
15// Example: Add a user
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 addUser(db, { id: 1, name: 'Alice' });
19 addUser(db, { id: 2, name: 'Bob' });
20 addUser(db, { id: 3, name: 'John' });
21};
db.transaction()
ile bir işlem oluşturun ve çalışılacak nesne deposunu ve modu (bu durumda,readwrite
) belirtin.store.add()
metodunu kullanarak yeni veri ekleyin.- İşlemler otomatik olarak tamamlanır, fakat birden fazla işlemi gruplamak isterseniz işlem sonu olaylarını kullanarak bunları yönetebilirsiniz.
Veri Alma (Birincil Anahtar ile Arama)
Birincil anahtar kullanarak belirli bir verinin nasıl alınacağı aşağıda gösterilmiştir.
1function getUserById(db, id) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.get(id);
5
6 request.onsuccess = () => {
7 if (request.result) {
8 console.log('User found:', request.result);
9 } else {
10 console.log('User not found');
11 }
12 };
13
14 request.onerror = () => {
15 console.error('Error retrieving user:', request.error);
16 };
17}
18
19// Example: Get a user by id
20request.onsuccess = function (event) {
21 const db = event.target.result;
22 getUserById(db, 1);
23};
db.transaction()
ile salt okunur bir işlem başlatın.- Belirtilen birincil anahtara karşılık gelen veriyi almak için
store.get(id)
kullanın. - Veri başarıyla alındığında
onsuccess
çağrılır ve sonuç mevcutsa görüntülenir. Sonuç yoksa, 'karşılık gelen veri yok' olarak kabul edilir.
İndeks ile Arama Kullanmak
Birincil anahtar dışında bir özellik ile arama yapmak isterseniz, önceden oluşturduğunuz indeksi kullanın.
1function getUserByName(db, name) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const index = store.index('name');
5 const request = index.get(name);
6
7 request.onsuccess = () => {
8 if (request.result) {
9 console.log('User by name:', request.result);
10 } else {
11 console.log('No user found with that name');
12 }
13 };
14
15 request.onerror = () => {
16 console.error('Error retrieving user by name:', request.error);
17 };
18}
19
20// Example: Get a user by id
21request.onsuccess = function (event) {
22 const db = event.target.result;
23 getUserByName(db, 'Alice');
24};
- Önceden oluşturulmuş
name
indeksinestore.index('name')
ile erişin. index.get(value)
ile eşleşen ilk kaydı alırsınız. Aynı değere sahip birden fazla kayıt varsa, bunların hepsiniindex.getAll(value)
ile alabilirsiniz.
Veri Güncelleme
Mevcut veriyi güncellemek (üzerine yazmak) için put()
metodunu kullanın.
Eşleşen bir birincil anahtara sahip bir kayıt varsa güncellenir; yoksa yeni bir kayıt eklenir.
1function updateUser(db, updatedUser) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.put(updatedUser);
5
6 request.onsuccess = () => {
7 console.log('User updated:', updatedUser);
8 };
9
10 request.onerror = () => {
11 console.error('Update failed:', request.error);
12 };
13}
14
15// Example: Update user
16request.onsuccess = async (event) => {
17 const db = event.target.result;
18
19 // Test : update existing user
20 updateUser(db, { id: 3, name: 'John Updated' });
21
22 // Test : insert new user
23 updateUser(db, { id: 4, name: 'Charlie' });
24};
put()
hem güncelleme hem de ekleme için uygun bir metottur.- Aynı birincil anahtara sahip veri zaten varsa, üzerine yazılır.
- Güncellemeden önce var olup olmadığını kontrol etmek isterseniz, önceden
get()
ile kontrol edebilirsiniz.
Veri Silme
Belirli bir birincil anahtara karşılık gelen veriyi silmek için delete()
metodunu kullanın.
1function deleteUser(db, id) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.delete(id);
5
6 request.onsuccess = () => {
7 console.log(`User with id=${id} deleted successfully`);
8 };
9
10 request.onerror = () => {
11 console.error(`Failed to delete user with id=${id}:`, request.error);
12 };
13}
14
15// Example: Delete a user by id
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 deleteUser(db, 4);
19};
- Eşleşen birincil anahtara sahip veriyi silmek için
store.delete(id)
kullanın. - Veri mevcut olmasa bile hata oluşmaz ve işlem başarılı kabul edilir.
- Hata yönetimi uygulamak daha sağlam bir kod ile sonuçlanacaktır.
Tüm Veriyi Alma
getAll()
Nesne deposundaki tüm kayıtları almak için getAll()
metodunu kullanın.
1function getAllUsers(db) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.getAll();
5
6 request.onsuccess = () => {
7 console.log('All users:', request.result);
8 };
9
10 request.onerror = () => {
11 console.error('Failed to retrieve users:', request.error);
12 };
13}
14
15// Example: Get all users
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 getAllUsers(db);
19};
getAll()
, belirtilen nesne deposundaki tüm kayıtları bir dizi olarak alır.- Büyük miktarda veri tek seferde alınırken bile verimli bir şekilde işlenir.
- Sonuçlar diziler halinde
request.result
'ta saklanır. - Her zaman hata yönetimi ekleyin ki başarısızlıklarla başa çıkabilin.
openCursor()
openCursor()
, bir nesne deposunda veya indekste kayıtların sıralı olarak gezinilmesini sağlayan bir metottur. Büyük miktarda veriyi tek seferde almak yerine, verileri teker teker işlemek istediğinizde faydalıdır.
1function getAllUsersWithCursor(db) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.openCursor();
5
6 request.onsuccess = () => {
7 const cursor = request.result;
8 if (cursor) {
9 console.log('User:', cursor.value); // Process the current record
10 cursor.continue(); // Move to the next record
11 } else {
12 console.log('All users have been processed.');
13 }
14 };
15
16 request.onerror = () => {
17 console.error('Failed to open cursor:', request.error);
18 };
19}
20
21// Example: Get all users
22request.onsuccess = function (event) {
23 const db = event.target.result;
24 getAllUsersWithCursor(db);
25};
openCursor()
kullanarak, bir imleç başlatır ve nesne deposundaki tüm kayıtları teker teker alırsınız.- Mevcut kaydın veri nesnesini almak için
cursor.value
kullanılır. - İmleci bir sonraki kayda taşımak için
cursor.continue()
kullanılır. cursor === null
olduğunda, tüm kayıtlarda gezinilmiş olur.
openCursor() kullanılarak güncelleme işlemi örneği
Örneğin, name
değeri Alice
olan bir kullanıcının adını Alicia
olarak değiştirme işlemi şu şekilde olur:.
1function updateUserName(db, oldName, newName) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const index = store.index('name'); // Use the 'name' index
5 const request = index.openCursor(IDBKeyRange.only(oldName));
6
7 request.onsuccess = () => {
8 const cursor = request.result;
9 if (cursor) {
10 const user = cursor.value;
11 user.name = newName; // Update the name
12 const updateRequest = cursor.update(user);
13
14 updateRequest.onsuccess = () => {
15 console.log('Updated user:', user);
16 };
17
18 updateRequest.onerror = () => {
19 console.error('Failed to update user:', updateRequest.error);
20 };
21
22 cursor.continue();
23 } else {
24 console.log('All matching users have been updated.');
25 }
26 };
27
28 request.onerror = () => {
29 console.error('Cursor error:', request.error);
30 };
31}
32
33// Example: Update user name
34request.onsuccess = function (event) {
35 const db = event.target.result;
36 updateUserName(db, 'Alice', 'Alicia');
37};
-
IDBKeyRange.only(oldName)
IDBKeyRange.only
kullanarak, anahtarı tam olarakoldName
ile eşleşen kayıtları hedefleyebilirsiniz. Belirli bir değere doğrudan erişmek istediğinizde bu yararlıdır. -
cursor.update()
cursor.value
güncellendikten sonra,update()
fonksiyonunun çağrılması ilgili kaydın üzerine yazacaktır. -
Birden Fazla Eşleşmeyi İşleme
cursor.continue()
çağrılarak imleci bir sonraki eşleşen kayda taşıyabilirsiniz. Bu, aynı anahtara veya koşula uyan birden fazla kaydı sırayla işlemden geçirmenizi sağlar. -
Hata Yönetimi Bir işlem başarısız olduğunda
onerror
içinde günlükleme yapmak, nedenleri araştırmayı ve işletim sırasında sorun gidermeyi kolaylaştırır.
openCursor() kullanılarak silme işlemi örneği
Örneğin, name
değeri Bob
olan tüm kullanıcıları silme işlemi şu şekilde olur:.
1function deleteUsersByName(db, targetName) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const index = store.index('name');
5 const request = index.openCursor(IDBKeyRange.only(targetName));
6
7 request.onsuccess = () => {
8 const cursor = request.result;
9 if (cursor) {
10 const deleteRequest = cursor.delete();
11
12 deleteRequest.onsuccess = () => {
13 console.log('Deleted user:', cursor.value);
14 };
15
16 deleteRequest.onerror = () => {
17 console.error('Failed to delete user:', deleteRequest.error);
18 };
19
20 cursor.continue();
21 } else {
22 console.log('All matching users have been deleted.');
23 }
24 };
25
26 request.onerror = () => {
27 console.error('Cursor error:', request.error);
28 };
29}
30
31// Example: Delete user by name
32request.onsuccess = function (event) {
33 const db = event.target.result;
34 deleteUsersByName(db, 'Bob');
35};
cursor.delete()
cursor.delete()
ile imlecin bulunduğu konumdaki kayıt silinir. Sonuçlar eşzamansız olarak döndüğü için işlemionsuccess
içinde kontrol edebilirsiniz.
İşlemler ve Eşzamansız İşleme Hakkında Notlar
IndexedDB
eşzamansız ve olay tabanlıdır. Tüm işlemler onsuccess
veya onerror
olayları ile yönetilmelidir. Birden fazla işlemi birlikte gruplamak istediğinizde, bunları bir Promise
içerisine sarmak kullanışlıdır.
1function openDatabase() {
2 return new Promise((resolve, reject) => {
3 const request = indexedDB.open('MyDatabase', 1);
4
5 request.onupgradeneeded = function (event) {
6 const db = event.target.result;
7
8 // Initialization process (creating object stores and indexes, etc.)
9 const store = db.createObjectStore('users', { keyPath: 'id' });
10 store.createIndex('name', 'name', { unique: false });
11 };
12
13 request.onsuccess = function (event) {
14 const db = event.target.result;
15 resolve(db);
16 };
17
18 request.onerror = function () {
19 reject(request.error);
20 };
21 });
22}
23
24function addUserAsync(db, user) {
25 return new Promise((resolve, reject) => {
26 const transaction = db.transaction('users', 'readwrite');
27 const store = transaction.objectStore('users');
28 const request = store.add(user);
29
30 request.onsuccess = () => resolve();
31 request.onerror = () => reject(request.error);
32 });
33}
34
35async function main() {
36 try {
37 const db = await openDatabase();
38 await addUserAsync(db, { id: 1, name: 'Alice' });
39 console.log('User added successfully');
40 } catch (error) {
41 console.error('Error:', error);
42 }
43}
openDatabase
veyaaddUserAsync
gibi fonksiyonlarıPromise
ile sarmak, eşzamansız işlemleri sezgisel olarakasync/await
ile yönetmenizi sağlar.- Bu, callback cehennemini önler ve kodun daha okunabilir olmasını sağlar.
Özet
Tarayıcı tarafında gelişmiş veri yönetimi yapmak istediğinizde IndexedDB
çok güçlü bir özelliktir. Başlangıçta olay tabanlı eşzamansız işlemler kafa karıştırıcı olabilir, ancak yapıyı anladığınızda istemci tarafında tam ölçekli veri işlemleri yapabilirsiniz.
Özellikle aşağıdaki noktaları göz önünde bulundurmak, onu daha sorunsuz kullanmanıza yardımcı olacaktır:.
- İlk ayarları
onupgradeneeded
ile yapın. - İşlemlerin okuma/yazma moduna dikkat edin.
- Dizinler, verimli arama yapmanızı sağlar.
- Veriler nesne olarak saklanabilir ve bu da onları JSON ile oldukça uyumlu kılar.
IndexedDB
'yi tam olarak öğrenerek PWA ve çevrimdışı uygulamalar için veri yönetimi çok daha kolay hale gelir.
Yukarıdaki makaleyi, YouTube kanalımızda Visual Studio Code'u kullanarak takip edebilirsiniz. Lütfen YouTube kanalını da kontrol edin.