واجهات GraphQL
الاتصال بقاعدة البيانات
ربط GraphQL بقاعدة البيانات
تحتاج خوادم GraphQL في العالم الحقيقي إلى جلب البيانات من قواعد البيانات. في هذا الدرس، سنتعلم كيفية ربط GraphQL بقاعدة بيانات باستخدام Prisma ORM وتنفيذ عمليات CRUD.
لماذا نستخدم ORM؟
أدوات ORM (Object-Relational Mapping) مثل Prisma وMongoose تبسط عمليات قاعدة البيانات من خلال:
- توفير استعلامات قاعدة بيانات آمنة من حيث النوع
- التعامل مع ترحيلات قاعدة البيانات
- إنشاء أنواع TypeScript من مخطط قاعدة البيانات الخاصة بك
- منع هجمات حقن SQL
- دعم قواعد بيانات متعددة (PostgreSQL، MySQL، SQLite، MongoDB)
إعداد Prisma
قم بتثبيت Prisma واعتمادياته:
npm install @prisma/client
npm install -D prisma
# تهيئة Prisma
npx prisma init
هذا ينشئ مجلد prisma مع ملف schema.prisma:
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
قم بتعيين
DATABASE_URL الخاص بك في ملف .env الذي أنشأه Prisma. مثال: DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
تعريف نماذج البيانات
قم بتعريف مخطط قاعدة البيانات الخاصة بك في prisma/schema.prisma:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
تشغيل الترحيلات (Migrations)
إنشاء وتطبيق ترحيلات قاعدة البيانات:
# إنشاء ترحيل
npx prisma migrate dev --name init
# إنشاء Prisma Client
npx prisma generate
شغّل
npx prisma studio لفتح متصفح قاعدة بيانات مرئي في متصفح الويب الخاص بك. إنه رائع لعرض وتحرير البيانات أثناء التطوير.
ربط Prisma بـ GraphQL
أنشئ مثيل Prisma client وأضفه إلى سياق GraphQL الخاص بك:
// db.js
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
module.exports = prisma;
// server.js
const { ApolloServer } = require('apollo-server-express');
const prisma = require('./db');
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
return {
prisma, // متاح الآن في جميع المحللات
req
};
}
});
محللات CRUD مع Prisma
تنفيذ عمليات الإنشاء والقراءة والتحديث والحذف:
const typeDefs = `
type User {
id: Int!
email: String!
name: String
posts: [Post!]!
createdAt: String!
}
type Post {
id: Int!
title: String!
content: String
published: Boolean!
author: User!
createdAt: String!
}
type Query {
users: [User!]!
user(id: Int!): User
posts: [Post!]!
post(id: Int!): Post
}
type Mutation {
createUser(email: String!, name: String): User!
updateUser(id: Int!, email: String, name: String): User!
deleteUser(id: Int!): User!
createPost(title: String!, content: String, authorId: Int!): Post!
updatePost(id: Int!, title: String, content: String, published: Boolean): Post!
deletePost(id: Int!): Post!
}
`;
const resolvers = {
Query: {
users: async (parent, args, { prisma }) => {
return await prisma.user.findMany();
},
user: async (parent, { id }, { prisma }) => {
return await prisma.user.findUnique({ where: { id } });
},
posts: async (parent, args, { prisma }) => {
return await prisma.post.findMany();
},
post: async (parent, { id }, { prisma }) => {
return await prisma.post.findUnique({ where: { id } });
}
},
Mutation: {
createUser: async (parent, { email, name }, { prisma }) => {
return await prisma.user.create({
data: { email, name }
});
},
updateUser: async (parent, { id, email, name }, { prisma }) => {
return await prisma.user.update({
where: { id },
data: { email, name }
});
},
deleteUser: async (parent, { id }, { prisma }) => {
return await prisma.user.delete({ where: { id } });
},
createPost: async (parent, { title, content, authorId }, { prisma }) => {
return await prisma.post.create({
data: { title, content, authorId }
});
},
updatePost: async (parent, { id, title, content, published }, { prisma }) => {
return await prisma.post.update({
where: { id },
data: { title, content, published }
});
},
deletePost: async (parent, { id }, { prisma }) => {
return await prisma.post.delete({ where: { id } });
}
},
User: {
posts: async (parent, args, { prisma }) => {
return await prisma.post.findMany({
where: { authorId: parent.id }
});
}
},
Post: {
author: async (parent, args, { prisma }) => {
return await prisma.user.findUnique({
where: { id: parent.authorId }
});
}
}
};
تحذير أمني: تحقق دائمًا من صحة بيانات الإدخال قبل تمريرها إلى استعلامات قاعدة البيانات. أضف فحوصات التفويض للتأكد من أن المستخدمين يمكنهم فقط تعديل بياناتهم الخاصة.
استعلامات Prisma المتقدمة
يدعم Prisma التصفية المعقدة والفرز والترقيم:
const resolvers = {
Query: {
// التصفية
publishedPosts: async (parent, args, { prisma }) => {
return await prisma.post.findMany({
where: { published: true }
});
},
// الفرز
recentPosts: async (parent, args, { prisma }) => {
return await prisma.post.findMany({
orderBy: { createdAt: 'desc' },
take: 10
});
},
// الترقيم
paginatedPosts: async (parent, { skip, take }, { prisma }) => {
return await prisma.post.findMany({
skip: skip || 0,
take: take || 10,
orderBy: { createdAt: 'desc' }
});
},
// البحث
searchPosts: async (parent, { query }, { prisma }) => {
return await prisma.post.findMany({
where: {
OR: [
{ title: { contains: query } },
{ content: { contains: query } }
]
}
});
}
}
};
استخدام Mongoose (بديل لـ MongoDB)
إذا كنت تستخدم MongoDB، فإن Mongoose خيار شائع:
const mongoose = require('mongoose');
// الاتصال بـ MongoDB
mongoose.connect(process.env.MONGODB_URI);
// تعريف المخطط
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
name: String,
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
// الاستخدام في المحللات
const resolvers = {
Query: {
users: async () => await User.find(),
user: async (parent, { id }) => await User.findById(id)
},
Mutation: {
createUser: async (parent, { email, name }) => {
const user = new User({ email, name });
return await user.save();
}
}
};
Prisma مقابل Mongoose:
- Prisma: آمن من حيث النوع، يدعم SQL وNoSQL، يُنشئ أنواع TypeScript تلقائيًا، بناء جملة حديث
- Mongoose: خاص بـ MongoDB، تصميم مخطط مرن، نظام بيئي غني بالإضافات، ناضج
تمرين تطبيقي:
- قم بإعداد Prisma مع قاعدة بيانات SQLite
- قم بتعريف نماذج لـ
CategoryوProduct(علاقة واحد لكثير) - أنشئ الترحيلات وأنشئ Prisma Client
- نفذ استعلامات GraphQL لجلب جميع المنتجات والمنتجات حسب الفئة
- نفذ طفرات لإنشاء وتحديث وحذف المنتجات
- أضف استعلام بحث يجد المنتجات حسب الاسم