Node.js: SQLite و Prisma

آخر تحديث: 2026-08-26

يعمل تشارلي على تطوير تطبيق لتدوين الملاحظات على أجهزة الكمبيوتر المكتبية يتطلب تخزين البيانات محليًّا، لكنه لا يريد أن يضطر المستخدمون إلى تثبيت خدمة MySQL منفصلة. SQLite هي قاعدة بيانات مدمجة لا تتطلب أي إعدادات، حيث يشكل ملف واحد قاعدة البيانات بأكملها؛ وعند استخدامها مع Prisma ORM، فإنها توفر أمانًا كاملاً للأنواع في عمليات SQL.

1. better-sqlite3: قاعدة بيانات مدمجة مزودة بواجهة برمجة تطبيقات متزامنة

(1) لماذا تختار better-sqlite3؟

توجد العديد من مكتبات SQLite في منظومة Node.js، وتشتهر مكتبة better-sqlite3 بـواجهة برمجة التطبيقات المتزامنة — فهي تُرجع النتيجة فور استدعائها، دون الحاجة إلى انتظار أو عمليات الاستدعاء المرتدة المتداخلة. وتقوم هذه المكتبة بترجمة SQLite إلى لغة C++ على المستوى المنخفض، مما ينتج عنه أداء يفوق بكثير أداء مكتبات الغلاف غير المتزامنة.

الميزة الوصف
واجهة برمجة التطبيقات (API) للمزامنة لا «جحيم الاستدعاءات المرتدة»، كود خطي وسهل القراءة
لا حاجة إلى أي إعدادات لا حاجة لتثبيت خدمة قاعدة بيانات — npm install جاهز للاستخدام
التخزين في ملف واحد قاعدة البيانات بأكملها عبارة عن ملف .db واحد
دعم المعاملات المعاملات المتداخلة والبيانات المعدة مسبقًا — كلها مضمنة
الأداء العالي واجهات C++، أسرع بـ 2–3 مرات من node-sqlite3
متعدد المنصات يمكن تجميعه على أنظمة ويندوز وماك أو إس ولينكس

(2) التركيب والتوصيلات الأساسية

BASH
npm init -y
npm install better-sqlite3
JAVASCRIPT
const Database = require('better-sqlite3');
const db = new Database('myapp.db');

db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');

console.log('SQLite Version:', db.prepare('SELECT sqlite_version()').get());

(3) دليل مرجعي سريع للطرق الشائعة

الطريقة الغرض مثال
db.prepare(sql) إنشاء عبارة مُجمَّعة مسبقًا const stmt = db.prepare('SELECT * FROM users WHERE id = ?')
stmt.run(...params) تنفيذ أوامر INSERT/UPDATE/DELETE stmt.run(1, 'Charlie')
stmt.get(...params) العودة إلى الكائن أحادي السطر stmt.get(1)
stmt.all(...params) إرجاع مصفوفة تضم جميع الصفوف stmt.all()
stmt.values(...params) مصفوفة قيم الإرجاع (بدون أسماء مفاتيح) stmt.values()
db.exec(sql) تنفيذ عدة أوامر SQL db.exec(schemaSql)
db.transaction(fn) وظيفة إنشاء معاملة const insertMany = db.transaction((items) => {...})
db.pragma(cmd) تعيين/الاستعلام عن PRAGMA db.pragma('journal_mode = WAL')

▶ مثال: سير عمل CRUD كامل باستخدام better-sqlite3

JAVASCRIPT
const Database = require('better-sqlite3');
const db = new Database('notes.db');

db.exec(`
  CREATE TABLE IF NOT EXISTS notes (
    id    INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    body  TEXT DEFAULT '',
    created_at TEXT DEFAULT (datetime('now'))
  )
`);

const insert = db.prepare('INSERT INTO notes (title, body) VALUES (?, ?)');
const result = insert.run('First Note', 'Hello SQLite!');
console.log('Insert a row ID:', result.lastInsertRowid);

const find = db.prepare('SELECT * FROM notes WHERE id = ?');
console.log('Search Results:', find.get(1));

const update = db.prepare('UPDATE notes SET title = ? WHERE id = ?');
update.run('Revised Title', 1);

const remove = db.prepare('DELETE FROM notes WHERE id = ?');
remove.run(1);

const listAll = db.prepare('SELECT * FROM notes ORDER BY created_at DESC');
console.log('All Notes:', listAll.all());

db.close();
▶ جرّب الكود

(4) الاستخدام في المعاملات

JAVASCRIPT
const insertMany = db.transaction((notes) => {
  for (const n of notes) {
    insert.run(n.title, n.body);
  }
});

insertMany([
  { title: 'Notes A', body: 'Content A' },
  { title: 'Notes B', body: 'Content B' },
]);


2. مراجعة أساسيات جمل SQL

(1) العمليات الأساسية الأربع: CRUD

العملية SQL الكلمة الرئيسية
إنشاء إدراج INSERT INTO table (col) VALUES (val)
قراءة اختيار SELECT col FROM table WHERE cond
تحديث تحديث UPDATE table SET col=val WHERE cond
حذف حذف DELETE FROM table WHERE cond

(2) جمل الاستعلام الشائعة

TEXT 📖 للعرض فقط
SELECT Listed
FROM Table Name
WHERE Conditions
GROUP BY Grouping Column
HAVING Grouping Criteria
ORDER BY Sorted List ASC|DESC
LIMIT Quantity OFFSET offset

(3) استعلامات الانضمام

JAVASCRIPT
db.exec(`
  CREATE TABLE IF NOT EXISTS authors (
    id   INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL
  )
`);

db.exec(`
  CREATE TABLE IF NOT EXISTS books (
    id        INTEGER PRIMARY KEY AUTOINCREMENT,
    title     TEXT NOT NULL,
    author_id INTEGER REFERENCES authors(id)
  )
`);

const joinQuery = db.prepare(`
  SELECT books.title, authors.name AS author
  FROM books
  JOIN authors ON books.author_id = authors.id
`);


3. تثبيت برنامج Prisma وتهيئته

(1) ما هو Prisma؟

Prisma هو نموذج ORM من الجيل التالي يعمل مع Node.js وTypeScript. ويتمثل سير العمل الأساسي له فيما يلي:

100%
flowchart LR
    A["schema.prisma"] -->|"prisma migrate dev"| B["Migration SQL"]
    A -->|"prisma generate"| C["Prisma Client"]
    C -->|"Type-Safe Queries"| D[("Database")]
    B --> D

(2) بدء المشروع

BASH
mkdir prisma-notes && cd prisma-notes
npm init -y
npm install prisma --save-dev
npm install @prisma/クライアント
npx prisma init --datasource-provider sqlite

تم إنشاؤها بعد التهيئة:

TEXT 📖 للعرض فقط
prisma-notes/
├── prisma/
│   └── schema.prisma
├── .env
└── package.json

.env محتويات الملف:

TEXT 📖 للعرض فقط
DATABASE_URL="file:./dev.db"

(3) دليل مرجعي سريع لـ Prisma Command

الأمر الغرض
npx prisma init تهيئة مشروع Prisma
npx prisma migrate dev إنشاء عملية ترحيل للتطوير وتطبيقها
npx prisma migrate deploy ترحيل التطبيقات إلى بيئة الإنتاج
npx prisma generate إنشاء عميل Prisma
npx prisma studio فتح واجهة الإدارة المرئية
npx prisma db push تطبيق المخطط مباشرةً خلال مرحلة النموذج الأولي (دون إنشاء ملفات الترحيل)
npx prisma db seed تشغيل البرنامج النصي لبيانات البذور


4. schema.prisma: Defining the Model

(1) الهيكل الأساسي

JAVASCRIPT
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Note {
  id        Int      @id @default(autoincrement())
  title     String
  body      String   @default("")
  pinned    Boolean  @default(false)
  tags      String   @default("")
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

(2) مرجع سريع لأنواع الحقول

نوع بريزما تخطيط SQLite الوصف
String نص سلسلة
Int INTEGER عدد صحيح 32 بت
BigInt INTEGER عدد صحيح 64 بت
Float حقيقي عدد عائم
Boolean عدد صحيح 0 أو 1
DateTime نص سلسلة ISO 8601
Json نص سلسلة JSON
Bytes BLOB بيانات ثنائية
Decimal نص أرقام عشرية عالية الدقة

ملاحظة: يختلف نظام أنواع البيانات في SQLite عن نظيره في PostgreSQL؛ وتقوم Prisma بإجراء التكييف الأساسي. عند التبديل إلى provider، سيتم تعديل تعيينات أنواع الحقول تلقائيًا.

(3) الخصائص والمُعدِّلات

المُعدِّل الغرض مثال
@id المفتاح الأساسي id Int @id
@default القيمة الافتراضية @default(autoincrement()) / @default(now()) / @default("active")
@unique قيد فريد email String @unique
@relation تعريف العلاقة @relation(fields: [authorId], references: [id])
@map / @@map تعيين أسماء الأعمدة/الجداول @map("created_at")
@@unique مركب فريد @@unique([firstName, lastName])
@@index المؤشر المركب @@index([categoryId, createdAt])
? حقل اختياري bio String?

(4) تعريفات العلاقات

JAVASCRIPT
model User {
  id    Int    @id @default(autoincrement())
  email String @unique
  name  String
  notes Note[]
}

model Note {
  id       Int   @id @default(autoincrement())
  title    String
  body     String @default("")
  authorId Int
  author   User   @relation(fields: [authorId], references: [id], onDelete: Cascade)
}

▶ مثال: تعريف علاقات متعددة النماذج

JAVASCRIPT
// prisma/schema.prisma — العلاقات بين نماذج User و Note و Tag
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String
  notes     Note[]
  createdAt DateTime @default(now())
}

model Note {
  id        Int        @id @default(autoincrement())
  title     String
  body      String     @default("")
  published Boolean    @default(false)
  authorId  Int
  author    User       @relation(fields: [authorId], references: [id], onDelete: Cascade)
  tags      NoteTag[]
  createdAt DateTime   @default(now())
  updatedAt DateTime   @updatedAt
}

model Tag {
  id    Int       @id @default(autoincrement())
  name  String    @unique
  notes NoteTag[]
}

model NoteTag {
  noteId Int
  tagId  Int
  note   Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
  tag    Tag  @relation(fields: [tagId], references: [id], onDelete: Cascade)

  @@id([noteId, tagId])
  @@index([tagId])
}
▶ جرّب الكود

يحدد هذا المثال ثلاثة نماذج مع نوعين من العلاقات:




5. عملية الترحيل باستخدام Prisma Migrate

(1) إنشاء عملية ترحيل وتطبيقها

BASH
npx prisma migrate dev --name init

بعد التنفيذ:

TEXT 📖 للعرض فقط
prisma/
├── schema.prisma
└── migrations/
    └── 20260703_init/
        └── migration.sql

تم إنشاؤه migration.sql:

SQL
CREATE TABLE "Note" (
    "id"        INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "title"     TEXT NOT NULL,
    "body"      TEXT NOT NULL DEFAULT '',
    "pinned"    BOOLEAN NOT NULL DEFAULT false,
    "tags"      TEXT NOT NULL DEFAULT '',
    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" DATETIME NOT NULL
);

(2) سير عمل الهجرة

TEXT 📖 للعرض فقط
Development Phase:  schema Edit → npx prisma migrate dev --name Description
Testing Phase:  npx prisma migrate deploy(App Only,Do not create a new migration)
Prototyping Phase:  npx prisma db push(Skip migrating files,Rapid Iteration)
Reset Data:  npx prisma migrate reset(Clear the database and replay all migrations)

(3) نشر الإنتاج

BASH
npx prisma migrate deploy

migrate deploy يُشغّل عمليات الترحيل التي لم يتم تطبيقها فقط؛ ولا ينشئ عمليات ترحيل جديدة ولا يعيد تعيين البيانات — وهو خيار مثالي لمسارات CI/CD.



6. عمليات CRUD في عميل Prisma

(1) تهيئة العميل

JAVASCRIPT
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function main() {
  // CRUD Instructions are written here
}

main()
  .catch(console.error)
  .finally(() => prisma.$disconnect());

(2) إنشاء — إنشاء

JAVASCRIPT
const note = await prisma.note.create({
  data: {
    title: 'Study Prisma',
    body: 'Prisma Making Database Operations Type-Safe',
    pinned: true,
  },
});

const notes = await prisma.note.createMany({
  data: [
    { title: 'Notes A', body: 'Content A' },
    { title: 'Notes B', body: 'Content B', pinned: true },
  ],
});

(3) قراءة — استعلام

JAVASCRIPT
const one = await prisma.note.findUnique({ where: { id: 1 } });

const first = await prisma.note.findFirst({
  where: { pinned: true },
  orderBy: { createdAt: 'desc' },
});

const all = await prisma.note.findMany();

const filtered = await prisma.note.findMany({
  where: {
    pinned: true,
    title: { contains: 'Prisma' },
  },
});

(4) تحديث — تحديث

JAVASCRIPT
const updated = await prisma.note.update({
  where: { id: 1 },
  data: { title: 'Updated Title', pinned: false },
});

const count = await prisma.note.updateMany({
  where: { pinned: false },
  data: { tags: 'archived' },
});

(5) حذف — حذف

JAVASCRIPT
const deleted = await prisma.note.delete({ where: { id: 1 } });

const deleteCount = await prisma.note.deleteMany({
  where: { pinned: false },
});

(6) قائمة مرشحات الاستعلام

الفلتر المعنى مثال
equals يساوي { title: { equals: 'Hello' } }
not لا يساوي { id: { not: 1 } }
contains تضمين { title: { contains: 'Prisma' } }
startsWith البادئة { title: { startsWith: 'Learn' } }
endsWith لاحقة { email: { endsWith: '@test.com' } }
in في القائمة { id: { in: [1, 2, 3] } }
notIn غير موجود في القائمة { id: { notIn: [4, 5] } }
lt / lte أقل من / أقل من أو يساوي { id: { lte: 10 } }
gt / gte أكبر من / أكبر من أو يساوي { id: { gte: 5 } }
AND و { AND: [{ pinned: true }, { title: { contains: 'A' } }] }
OR أو { OR: [{ pinned: true }, { pinned: false }] }
NOT لا { NOT: { title: 'Hello' } }

▶ مثال: تضمين البيانات المرتبطة والتصفية

JAVASCRIPT
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function queryNotes() {
  // البحث عن الملاحظات المنشورة مع المؤلف والوسوم
  const notes = await prisma.note.findMany({
    where: {
      published: true,
      title: { contains: 'Prisma' },
    },
    include: {
      author: {
        select: { id: true, name: true, email: true },
      },
      tags: {
        include: {
          tag: { select: { id: true, name: true } },
        },
      },
    },
    orderBy: { createdAt: 'desc' },
    take: 10,
  });

  console.log(`تم العثور على ${notes.length} ملاحظة`);
  for (const note of notes) {
    const tagNames = note.tags.map((nt) => nt.tag.name).join(', ');
    console.log(`${note.title} — ${note.author.name} [${tagNames}]`);
  }
}

queryNotes()
  .catch(console.error)
  .finally(() => prisma.$disconnect());
▶ جرّب الكود

يستخدم هذا المثال include لتحميل author و tags المرتبطة مسبقًا، و select لتحديد الحقول المطلوبة فقط. تتضمن النتيجة بيانات متداخلة بدون استعلامات إضافية.




7. الفرز وتقسيم الصفحات

(1) الفرز

JAVASCRIPT
const sorted = await prisma.note.findMany({
  orderBy: [
    { pinned: 'desc' },
    { createdAt: 'desc' },
  ],
});

(2) ترقيم الصفحات

JAVASCRIPT
const PAGE_SIZE = 10;

const page1 = await prisma.note.findMany({
  skip: 0,
  take: PAGE_SIZE,
  orderBy: { createdAt: 'desc' },
});

const page2 = await prisma.note.findMany({
  skip: PAGE_SIZE,
  take: PAGE_SIZE,
  orderBy: { createdAt: 'desc' },
});

(3) ترقيم الصفحات باستخدام المؤشر (موصى به لمجموعات البيانات الكبيرة)

JAVASCRIPT
const first = await prisma.note.findMany({
  take: 10,
  orderBy: { id: 'asc' },
});

const cursor = first[first.length - 1].id;

const next = await prisma.note.findMany({
  take: 10,
  skip: 1,
  cursor: { id: cursor },
  orderBy: { id: 'asc' },
});

▶ مثال: البحث وتقسيم الصفحات بالمؤشر

JAVASCRIPT
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function searchNotes(searchTerm, cursorId = null) {
  const PAGE_SIZE = 5;
  const results = await prisma.note.findMany({
    take: PAGE_SIZE + 1,
    skip: cursorId ? 1 : 0,
    cursor: cursorId ? { id: cursorId } : undefined,
    where: {
      OR: [
        { title: { contains: searchTerm } },
        { body: { contains: searchTerm } },
      ],
    },
    include: { author: { select: { name: true } } },
    orderBy: { id: 'asc' },
  });

  const hasMore = results.length > PAGE_SIZE;
  if (hasMore) results.pop();

  return {
    items: results,
    nextCursor: hasMore ? results[results.length - 1].id : null,
    hasMore,
  };
}

async function main() {
  let cursor = null;
  for (let page = 1; page <= 3; page++) {
    const { items, nextCursor, hasMore } = await searchNotes('Prisma', cursor);
    console.log(`الصفحة ${page}: ${items.length} نتيجة`);
    items.forEach((n) => console.log(`  ${n.id}: ${n.title}`));
    if (!hasMore) break;
    cursor = nextCursor;
  }
}

main()
  .catch(console.error)
  .finally(() => prisma.$disconnect());
▶ جرّب الكود

يجمع بين البحث النصي الكامل في حقلي title و body مع التقسيم إلى صفحات باستخدام المؤشر. يتم جلب سجل إضافي واحد لتحديد ما إذا كانت هناك صفحة تالية، ثم حذفه قبل الإرجاع.




8. مقارنة بين قواعد البيانات

(1) مقارنة بين SQLite و MySQL و PostgreSQL و MongoDB

البعد SQLite MySQL PostgreSQL MongoDB
النوع المدمج العميل-الخادم العميل-الخادم القائم على المستندات
التثبيت لا يتطلب تثبيتًا (مضمن في حزمة npm) يتطلب تثبيت خدمة يتطلب تثبيت خدمة يتطلب تثبيت خدمة
عمليات الكتابة المتزامنة كاتب واحد كتابة متعددة كتابة متعددة كتابة متعددة
حجم البيانات صغير إلى متوسط (بمقياس الجيجابايت) كبير (بمقياس التيرابايت) كبير (بمقياس التيرابايت) كبير (بمقياس التيرابايت)
حالات الاستخدام تطبيقات سطح المكتب / النماذج الأولية / الاختبار تطبيقات الويب / المشاريع متوسطة الحجم الاستعلامات المعقدة / البيانات الجغرافية المكانية المخطط المرن / السجلات
دعم JSON محدود (امتدادات JSON-1) مدعوم JSONB أصلي وثائق أصلية
البحث عن النص الكامل ملحق FTS5 فهرس النص الكامل tsvector فهرس النص
المعاملات ACID الكامل ACID الكامل ACID الكامل 4.0+ المعاملات متعددة المستندات
الترخيص الملكية العامة GPL / تجاري PostgreSQL SSPL

(2) مقارنة بين أطر عمل إدارة السمعة (ORM)

البعد Mongoose Prisma Sequelize TypeORM
اللغة جافا سكريبت يُفضل استخدام تايب سكريبت جافا سكريبت يُفضل استخدام تايب سكريبت
قاعدة البيانات MongoDB فقط SQLite/MySQL/PostgreSQL/MongoDB MySQL/PostgreSQL/SQLite/MSSQL MySQL/PostgreSQL/SQLite/MSSQL
تعريف المخطط كائن JS .prisma ملف تعريفي تعريف نموذج JS فئة الزخرفة / الكيان
أمان الأنواع ضعيف (يدوي) قوي (يتم إنشاؤه تلقائيًا) ضعيف متوسط (أنواع الزخرفة)
أداة الترحيل غير مدمجة prisma migrate sequelize-cli مدمجة
طريقة الاستعلام واجهة برمجة التطبيقات المتسلسلة الكائن المتسلسل SQL المتسلسل / الأصلي QueryBuilder / الأصلي
مشكلة N+1 تحتاج إلى تعبئة التضمين التلقائي تحتاج إلى التضمين الفوري/المؤجل تحتاج إلى علاقات
حجم المجتمع كبير ينمو بسرعة كبير كبير
المشاريع المناسبة مشاريع MongoDB TypeScript متكامل Node.js التقليدي منظومة NestJS


9. مثال شامل: طبقة بيانات إدارة الملاحظات

قم بإنشاء طبقة بيانات CRUD كاملة لإدارة الملاحظات باستخدام Prisma وSQLite.

▶ مثال: إدارة الملاحظات باستخدام Prisma وSQLite

الخطوة 1 — تعريف المخطط

JAVASCRIPT
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Note {
  id        Int      @id @default(autoincrement())
  title     String
  body      String   @default("")
  pinned    Boolean  @default(false)
  tags      String   @default("")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt      @map("updated_at")

  @@map("notes")
}
▶ جرّب الكود

الخطوة 2 — الترحيل

BASH
npx prisma migrate dev --name notes_init

الخطوة 3 — طبقة الوصول إلى البيانات

JAVASCRIPT
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function createNote(data) {
  return prisma.note.create({ data });
}

async function getNoteById(id) {
  return prisma.note.findUnique({ where: { id } });
}

async function updateNote(id, data) {
  return prisma.note.update({ where: { id }, data });
}

async function deleteNote(id) {
  return prisma.note.delete({ where: { id } });
}

async function listNotes({ page = 1, pageSize = 10, pinned, keyword } = {}) {
  const where = {};
  if (pinned !== undefined) where.pinned = pinned;
  if (keyword) where.title = { contains: keyword };

  const [items, total] = await Promise.all([
    prisma.note.findMany({
      where,
      orderBy: [{ pinned: 'desc' }, { createdAt: 'desc' }],
      skip: (page - 1) * pageSize,
      take: pageSize,
    }),
    prisma.note.count({ where }),
  ]);

  return { items, total, page, pageSize, totalPages: Math.ceil(total / pageSize) };
}

async function togglePin(id) {
  const note = await prisma.note.findUnique({ where: { id } });
  if (!note) throw new Error('The note does not exist.');
  return prisma.note.update({
    where: { id },
    data: { pinned: !note.pinned },
  });
}

module.exports = {
  createNote,
  getNoteById,
  updateNote,
  deleteNote,
  listNotes,
  togglePin,
};

الخطوة 4 — مثال على الاستخدام

JAVASCRIPT
const db = require('./note-service');

async function main() {
  const n1 = await db.createNote({ title: 'Study Prisma', body: 'Type Safety ORM', pinned: true });
  const n2 = await db.createNote({ title: 'SQLite Key Points', body: 'Zero-Configuration Embedded Database' });
  const n3 = await db.createNote({ title: 'Prisma Migration', body: 'migrate dev Driver' });

  console.log('Single-Record Query:', await db.getNoteById(n1.id));

  await db.updateNote(n2.id, { body: 'What's New' });
  await db.togglePin(n3.id);

  const result = await db.listNotes({ page: 1, pageSize: 10, keyword: 'Prisma' });
  console.log('Search Results:', result);

  await db.deleteNote(n2.id);

  const all = await db.listNotes({ page: 1, pageSize: 10 });
  console.log('Remaining Notes:', all);
}

main()
  .catch(console.error)
  .finally(() => require('@prisma/client').PrismaClient &&
    require('./node_modules/.prisma/client').$disconnect?.());

تنفيذ:

BASH
node index.js

الخطوة 5 — الإدارة المرئية

BASH
npx prisma studio

افتح http://localhost:5555 في متصفحك لعرض البيانات وتحريرها بصريًّا.



10. ملخص هذا الدرس



❓ أسئلة شائعة

س ما الفرق بين Prisma وSequelize؟
ج تستخدم Prisma مخططًا إعلانيًّا لإنشاء عملاء آمنين من حيث الأنواع، بينما تستخدم Sequelize الزخارف أو defineModel. توفر Prisma أمانًا أفضل من حيث الأنواع، في حين تتمتع Sequelize بنظام بيئي أكثر نضجًا.
س هل يُعد SQLite مناسبًا لبيئات الإنتاج؟
ج إنه مناسب لسيناريوهات التزامن المنخفض (حيث تكون عمليات القراءة أكثر من عمليات الكتابة، والنشر على خادم واحد)، مثل المشاريع الشخصية والأدوات الداخلية والتطبيقات المدمجة. أما بالنسبة لعمليات الكتابة التي تتطلب مستوى عالٍ من التزامن، فيُنصح باستخدام PostgreSQL أو MySQL.
س ما الفرق بين Prisma migrate وdb push؟
ج يقوم migrate بإنشاء ملفات الترحيل، وهي مناسبة للتعاون الجماعي ونشر التطبيقات في بيئة الإنتاج؛ أما db push فيقوم بمزامنة المخطط مباشرةً مع قاعدة البيانات، وهو مناسب لإنشاء النماذج الأولية.
س كيف يمكنني التبديل إلى PostgreSQL؟
ج قم بتغيير المزود في schema.prisma إلى postgresql، وقم بتحديث سلسلة الاتصال DATABASE_URL، ثم قم بتشغيل prisma migrate reset لإعادة إنشاء قاعدة البيانات.
س كيف يمكن حل مشكلة N+1 في Prisma؟
ج استخدم INCLUDE لتحميل البيانات ذات الصلة مسبقًا، أو استخدم SELECT لاختيار الحقول بدقة، لتجنب الاستعلام عن السجلات ذات الصلة واحدًا تلو الآخر في حلقة.

📖 ملخص

📝 تمارين

  1. استخدم better-sqlite3 لإنشاء جدول users، وقم بتنفيذ عمليات الإدراج والاستعلام عبر البريد الإلكتروني والتحديث والحذف، واستخدم المعاملات لضمان الترابطية.
  2. قم بتهيئة مشروع Prisma، وحدد نموذجين — User وPost (علاقة «واحد إلى عدة») — وبعد تشغيل عمليات الترحيل، استخدم Prisma Client لإنشاء مستخدمين، ونشر مقالات، والاستعلام عن المستخدمين وجميع مقالاتهم.
  3. استنادًا إلى مثال إدارة الملاحظات، أضف النموذج Category لتمكين تصفية الملاحظات حسب الفئة، ودعم التصفية حسب معرّف الفئة في listNotes.
  4. قارن بين تنفيذ نفس مجموعة عمليات CRUD باستخدام لغة SQL الأصلية better-sqlite3 وعميل Prisma، مع ملاحظة الاختلافات في عدد الأسطر وسهولة القراءة.
  5. اكتب برنامجًا نصيًّا باستخدام prisma.note.findMany لتنفيذ البحث عن الكلمات المفتاحية، وإعطاء الأولوية للنتائج الأعلى، وتقسيم الصفحات، ومقارنة الفروق في الأداء بين تقسيم الصفحات بالإزاحة وتقسيم الصفحات بالمؤشر عند معالجة 10,000 سجل.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%