better-sqlite3-typescript مع TypeScript: دليل شامل مع أمثلة
في عالم التطوير باستخدام TypeScript، غالبًا ما تظهر الحاجة إلى تخزين البيانات محليًا دون الحاجة إلى نشر قاعدة بيانات كاملة. SQLite هو الحل الأمثل لهذه المهام: فهي قاعدة بيانات علائقية مدمجة تعمل بدون خادم منفصل. لكن المكتبة القياسية better-sqlite3 مكتوبة بلغة JavaScript ولا توفر تعريفات أنواع مدمجة. هنا يأتي دور better-sqlite3-typescript، وهي طبقة تغليف تضيف كتابة صارمة للأنواع (TypeScript) فوق واجهة better-sqlite3 المتزامنة.
تتيح لك هذه المكتبة كتابة كود آمن من حيث الأنواع للعمل مع SQLite، مع الحصول على الإكمال التلقائي في بيئة التطوير المتكاملة (IDE)، والتحقق من الأنواع أثناء مرحلة التجميع، مما يقلل من أخطاء وقت التشغيل. ستحصل على جميع مزايا الوصول المتزامن إلى قاعدة البيانات (البساطة، القدرة على التنبؤ، الأداء العالي) مع قوة نظام الأنواع في TypeScript.
في هذه المقالة، سنشرح بالتفصيل عملية التثبيت، وأهم الدوال، وسنقدم أمثلة عملية ونصائح حول الأداء. المحتوى مفيد لكل من المبتدئين والمطورين ذوي الخبرة الذين يرغبون في استخدام SQLite بكفاءة في مشاريع TypeScript الخاصة بهم.
التثبيت
لتثبيت المكتبة، قم بتنفيذ الأمر التالي في terminal مشروعك:
npm install better-sqlite3-typescript better-sqlite3
ستحتاج أيضًا إلى تثبيت تعريفات الأنواع الخاصة بـ TypeScript:
npm install -D @types/better-sqlite3
بعد التثبيت، يمكنك استيراد المكتبة إلى مشروعك:
import Database from 'better-sqlite3-typescript';
بداية سريعة
مثال عملي بسيط لإنشاء قاعدة بيانات، وإنشاء جدول، وإدراج البيانات:
import Database from 'better-sqlite3-typescript';
// إنشاء أو فتح قاعدة بيانات
const db = new Database('my_database.db');
// إنشاء جدول
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
age INTEGER
)
`);
// إدراج بيانات
const insert = db.prepare('INSERT INTO users (name, email, age) VALUES (?, ?, ?)');
insert.run('Alice', 'alice@example.com', 30);
insert.run('Bob', 'bob@example.com', 25);
// قراءة البيانات
const select = db.prepare('SELECT * FROM users');
const users = select.all();
console.log(users);
// إغلاق الاتصال
db.close();
سينشئ هذا الكود ملف my_database.db في المجلد الحالي، وينشئ جدول users، ويضيف مستخدمين اثنين، ثم يعرضهم في وحدة التحكم.
الدوال والطرق الأساسية
دعنا نستعرض الدوال الرئيسية للمكتبة. جميع الأمثلة تفترض أن لديك كائن قاعدة بيانات db جاهزًا.
1. new Database(filename, options?)
التوقيع: new Database(filename: string, options?: DatabaseOptions): Database
الوصف: ينشئ أو يفتح قاعدة بيانات SQLite. إذا كان الملف غير موجود، فسيتم إنشاؤه. تسمح الخيارات بتعيين وضع الفتح (للقراءة فقط، في الذاكرة، إلخ).
مثال:
// فتح قاعدة بيانات في الذاكرة (لا يتم حفظها على القرص)
const memDb = new Database(':memory:');
// فتح قاعدة بيانات في وضع القراءة فقط
const readOnlyDb = new Database('data.db', { readonly: true });
2. db.prepare(sql)
التوقيع: prepare(sql: string): Statement
الوصف: يترجم استعلام SQL إلى عبارة مُعدة (prepared statement). يُرجع كائن Statement يمكن تنفيذه عدة مرات بمعاملات مختلفة.
مثال:
// تحضير استعلام لإدراج البيانات
const insertStmt = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
// تنفيذ الاستعلام بقيم مختلفة
insertStmt.run('Omar', 'omar@example.com');
insertStmt.run('Layla', 'layla@example.com');
3. statement.run(params?)
التوقيع: run(...params: any[]): RunResult
الوصف: ينفذ العبارة المُعدة (statement) دون إرجاع صفوف. يُستخدم مع أوامر INSERT و UPDATE و DELETE. يُرجع كائنًا يحتوي على changes (عدد الصفوف المتأثرة) و lastInsertRowid (معرف آخر صف تم إدراجه).
مثال:
const result = insertStmt.run('Sara', 'sara@example.com');
console.log(`تم إدراج ${result.changes} صف، والمعرف هو ${result.lastInsertRowid}`);
4. statement.get(params?)
التوقيع: get(...params: any[]): T | undefined
الوصف: ينفذ الاستعلام ويُرجِع الصف الأول من النتيجة ككائن. إذا لم توجد نتائج، يُرجع undefined. مثالي لجلب سجل واحد.
مثال:
const user = db.prepare('SELECT * FROM users WHERE id = ?').get(1);
console.log(user); // { id: 1, name: 'Alice', email: 'alice@example.com', age: 30 }
5. statement.all(params?)
التوقيع: all(...params: any[]): T[]
الوصف: ينفذ الاستعلام ويُرجِع جميع الصفوف كمصفوفة من الكائنات. يُستخدم لجلب بيانات متعددة.
مثال:
const allUsers = db.prepare('SELECT * FROM users').all();
console.log(allUsers); // مصفوفة تحتوي على جميع المستخدمين
6. statement.iterate(params?)
التوقيع: iterate(...params: any[]): IterableIterator
الوصف: يُنشئ مكررًا (iterator) لمعالجة النتائج صفًا تلو الآخر دون تحميلها كلها في الذاكرة. مفيد جدًا لمجموعات البيانات الكبيرة.
مثال:
const iterator = db.prepare('SELECT * FROM users').iterate();
for (const user of iterator) {
console.log(user.name);
}
7. db.exec(sql)
التوقيع: exec(sql: string): void
الوصف: ينفذ جملة SQL واحدة أو أكثر (مفصولة بفواصل منقوطة). لا يُرجع أي بيانات. يُستخدم لإنشاء الجداول أو تنفيذ أوامر DDL.
مثال:
db.exec(`
CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
content TEXT
);
CREATE INDEX IF NOT EXISTS idx_title ON posts(title);
`);
8. db.transaction(fn)
التوقيع: transaction(fn: (...params: any[]) => T): (...params: any[]) => T
الوصف: ينشئ دالة ملفوفة في معاملة (transaction). إذا حدث خطأ داخل الدالة، يتم التراجع عن جميع التغييرات تلقائيًا. يضمن atomicity (الذرية) للعمليات.
مثال:
const transfer = db.transaction((fromId: number, toId: number, amount: number) => {
const updateFrom = db.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?');
const updateTo = db.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?');
updateFrom.run(amount, fromId);
updateTo.run(amount, toId);
});
try {
transfer(1, 2, 100);
console.log('تم التحويل بنجاح');
} catch (error) {
console.error('فشل التحويل، تم التراجع عن العملية');
}
9. db.close()
التوقيع: close(): void
الوصف: يغلق الاتصال بقاعدة البيانات. يجب استدعاؤه بعد الانتهاء من العمل لتحرير الموارد.
مثال:
db.close();
نصائح للأداء
- استخدم المعاملات (Transactions): عند إجراء عدة عمليات كتابة، قم بتجميعها في معاملة واحدة. هذا يقلل من عمليات الإدخال/الإخراج (I/O) ويسرع الأداء بشكل كبير.
- أعد استخدام العبارات المُعدة (Prepared Statements): بدلاً من تحضير نفس الاستعلام عدة مرات، قم بتحضيره مرة واحدة وأعد استخدامه بمعاملات مختلفة.
- استخدم
iterate()للمجموعات الكبيرة: إذا كنت تتعامل مع آلاف الصفوف، استخدمiterate()بدلاً منall()لتجنب تحميل كل البيانات في الذاكرة دفعة واحدة. - فهرسة الأعمدة (Indexing): أضف فهارس (indexes) للأعمدة التي تستخدمها بكثرة في استعلامات WHERE أو JOIN لتحسين سرعة البحث.
الخلاصة
مكتبة better-sqlite3-typescript هي أداة قوية وسهلة الاستخدام للعمل مع SQLite في مشاريع TypeScript. توفر لك كتابة آمنة للأنواع، وأداءً ممتازًا بفضل الوصول المتزامن، وواجهة برمجة تطبيقات (API) بسيطة وواضحة. سواء كنت تبني تطبيقًا صغيرًا أو نظامًا معقدًا، فإن هذه المكتبة ستساعدك على إدارة البيانات المحلية بكفاءة وثقة.
ابدأ باستخدامها اليوم في مشروعك القادم، واستفد من قوة SQLite مع راحة TypeScript!