REST API مقابل GraphQL
يتواجه REST صاحب التاريخ الممتد لأكثر من 20 عامًا مع GraphQL المنبثق من فيسبوك. أي نمط تصميم واجهة برمجة تطبيقات يجب اختياره لخلفية تطبيق الموبايل؟
منصة Google الشاملة لتطوير تطبيقات الموبايل
بديل Firebase مفتوح المصدر — مبني على PostgreSQL وقابل للاستضافة الذاتية
| الفئة | Firebase | Supabase |
|---|---|---|
| الأداء | 9/10 | 9/10 |
| سهولة التعلّم | 8/10 | 7/10 |
| النظام البيئي | 10/10 | 7/10 |
| المجتمع | 9/10 | 8/10 |
| سوق العمل | 9/10 | 7/10 |
| الاستدامة المستقبلية | 7/10 | 9/10 |
// Firebase - رسائل محادثة في الوقت الفعلي
import FirebaseFirestore
import FirebaseAuth
import SwiftUI
@Observable
class ChatViewModel {
var messages: [Message] = []
private var listener: ListenerRegistration?
private let db = Firestore.firestore()
struct Message: Identifiable, Codable {
@DocumentID var id: String?
let senderId: String
let senderName: String
let text: String
let timestamp: Timestamp
}
func startListening(roomId: String) {
listener = db.collection("rooms")
.document(roomId)
.collection("messages")
.order(by: "timestamp", descending: false)
.limit(toLast: 50)
.addSnapshotListener { [weak self] snapshot, error in
guard let documents = snapshot?.documents else { return }
self?.messages = documents.compactMap {
try? $0.data(as: Message.self)
}
}
}
func sendMessage(text: String, roomId: String) async throws {
guard let user = Auth.auth().currentUser else { return }
let message = Message(
senderId: user.uid,
senderName: user.displayName ?? "مجهول",
text: text,
timestamp: Timestamp()
)
try db.collection("rooms")
.document(roomId)
.collection("messages")
.addDocument(from: message)
}
func stopListening() { listener?.remove() }
}// Supabase - عمليات CRUD لمنشورات المستخدمين
import Supabase
import Foundation
struct Post: Codable, Identifiable {
let id: UUID
let userId: UUID
let title: String
let content: String
let createdAt: Date
enum CodingKeys: String, CodingKey {
case id, title, content
case userId = "user_id"
case createdAt = "created_at"
}
}
class PostRepository {
private let client = SupabaseClient(
supabaseURL: URL(string: ProcessInfo.processInfo.environment["SUPABASE_URL"]!)!,
supabaseKey: ProcessInfo.processInfo.environment["SUPABASE_ANON_KEY"]!
)
func fetchPosts() async throws -> [Post] {
try await client
.from("posts")
.select("*")
.order("created_at", ascending: false)
.limit(20)
.execute()
.value
}
func createPost(title: String, content: String) async throws -> Post {
let userId = try await client.auth.session.user.id
return try await client
.from("posts")
.insert(["user_id": userId.uuidString, "title": title, "content": content])
.select()
.single()
.execute()
.value
}
func subscribeToNewPosts(onNewPost: @escaping (Post) -> Void) -> RealtimeChannelV2 {
let channel = client.channel("public:posts")
channel.onPostgresChange(
InsertAction.self,
table: "posts"
) { change in
if let post = try? change.record.decode(as: Post.self) {
onNewPost(post)
}
}
Task { await channel.subscribe() }
return channel
}
}Firebase قوي للتطوير السريع، وواجهة iOS SDK ممتازة، وميزات الوقت الفعلي (realtime). أما Supabase فمناسب عند الحاجة لبيانات علائقية، ومرونة SQL، ومصدر مفتوح، أو عند القلق من الارتباط بمزوّد واحد (vendor lock-in). في 2025 ينضج Supabase بسرعة ويصبح بديلاً جاداً — خاصة بفضل تكامل pgvector للتطبيقات المدعومة بالذكاء الاصطناعي.
احصل على استشارة مجانيةممكن لكنه ليس سهلاً. يجب تحويل بنية Firestore غير العلائقية (NoSQL) إلى مخطط PostgreSQL، ونقل مستخدمي Auth، ونقل ملفات Storage. يتطلب ذلك مشروع ترحيل شامل.