إنتقل إلى المحتوى الرئيسي

Cursor + Supabase: تطوير الواجهة الخلفية كامل المكدس

يوفر Supabase بديلاً مفتوح المصدر لـ Firebase مع PostgreSQL، والمصادقة، وواجهات برمجة التطبيقات الفورية، والاشتراكات في الوقت الفعلي. بالجمع مع قدرات الذكاء الاصطناعي في Cursor، يمكنك بناء تطبيقات كاملة المكدس دون مغادرة محررك. يغطي هذا الدليل سير العمل الكامل من تصميم قاعدة البيانات إلى النشر في الإنتاج.

تطوير Supabase مع Cursor

لماذا Cursor + Supabase​

تقدم هذه المجموعة تجربة تطوير قوية:

  • مخططات مولدة بالذكاء الاصطناعي: اصف نموذج بياناتك، احصل على SQL وأنواع TypeScript
  • واجهة برمجة تطبيقات فورية: ينشئ Supabase تلقائياً REST وGraphQL APIs من جداولك
  • أمان الأنواع: أنشئ أنواع TypeScript مباشرة من مخطط قاعدة البيانات
  • في الوقت الفعلي: بنِ واجهات مستخدم تُحدّث مباشرة مع اشتراكات Supabase
  • دوال الحافة: انشر دوال TypeScript serverless عالمياً

إعداد المشروع​

تهيئة Supabase​

اطلب من Cursor توليد أوامر الإعداد:

# Install Supabase CLI
npm install -g supabase

# Initialize in your project
supabase init

# Link to your Supabase project
supabase login
supabase link --project-ref your-project-ref

# Generate TypeScript types
supabase gen types typescript --project-id your-project-ref --schema public > types/supabase.ts

تكوين العميل​

// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
import { Database } from '@/types/supabase';

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;

export const supabase = createClient<Database>(supabaseUrl, supabaseKey);
تلميح

اكتب دائماً عميل Supabase بالنوع Database المولد للحصول على الإكمال التلقائي الكامل وأمان الأنواع.

تصميم قاعدة البيانات بالذكاء الاصطناعي​

إنشاء المخططات​

صف تطبيقك لـ Cursor واحصل على مخطط كامل:

Prompt: "صمم مخطط PostgreSQL لتطبيق إدارة المهام يتضمن المستخدمين والمشاريع والمهام والتعليقات. أدرج مفاتيح خارجية مناسبة وفهارس وطوابع زمنية."
-- Cursor-generated schema
-- Enable necessary extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Users table (managed by Supabase Auth, but we can extend it)
CREATE TABLE public.profiles (
id UUID REFERENCES auth.users(id) PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
full_name TEXT,
avatar_url TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Projects table
CREATE TABLE public.projects (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
owner_id UUID REFERENCES public.profiles(id) NOT NULL,
status TEXT DEFAULT 'active' CHECK (status IN ('active', 'archived')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Tasks table
CREATE TABLE public.tasks (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
project_id UUID REFERENCES public.projects(id) ON DELETE CASCADE,
title TEXT NOT NULL,
description TEXT,
status TEXT DEFAULT 'todo' CHECK (status IN ('todo', 'in_progress', 'done')),
priority TEXT DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high')),
assignee_id UUID REFERENCES public.profiles(id),
due_date TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Comments table
CREATE TABLE public.comments (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
task_id UUID REFERENCES public.tasks(id) ON DELETE CASCADE,
author_id UUID REFERENCES public.profiles(id),
content TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Indexes for performance
CREATE INDEX idx_tasks_project ON public.tasks(project_id);
CREATE INDEX idx_tasks_assignee ON public.tasks(assignee_id);
CREATE INDEX idx_tasks_status ON public.tasks(status);
CREATE INDEX idx_comments_task ON public.comments(task_id);

تشغيل الترحيلات​

# Save schema to a migration file
supabase migration new init

# Apply locally
supabase db reset

# Apply to production
supabase db push

المصادقة​

إعداد المصادقة​

// app/auth/callback/route.ts
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
const requestUrl = new URL(request.url);
const code = requestUrl.searchParams.get('code');

if (code) {
const supabase = createRouteHandlerClient({ cookies });
await supabase.auth.exchangeCodeForSession(code);
}

return NextResponse.redirect(requestUrl.origin);
}

مصادقة البريد/كلمة المرور​

// app/login/page.tsx
'use client';

import { useState } from 'react';
import { supabase } from '@/lib/supabase';

export default function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) alert(error.message);
};

return (
<form onSubmit={handleLogin}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
);
}

موفري OAuth​

// components/OAuthButtons.tsx
'use client';

import { supabase } from '@/lib/supabase';

export function OAuthButtons() {
const signInWithProvider = async (provider: 'google' | 'github') => {
await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: `${location.origin}/auth/callback`,
},
});
};

return (
<div className="flex gap-2">
<button onClick={() => signInWithProvider('google')}>
Sign in with Google
</button>
<button onClick={() => signInWithProvider('github')}>
Sign in with GitHub
</button>
</div>
);
}

أمان مستوى الصف (RLS)​

تفعيل RLS​

أمان مستوى الصف ضروري لتطبيقات الإنتاج. اطلب من Cursor توليد السياسات:

-- Enable RLS on all tables
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.projects ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.tasks ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.comments ENABLE ROW LEVEL SECURITY;

-- Profiles: Users can read any profile, update only their own
CREATE POLICY "Profiles are viewable by everyone"
ON public.profiles FOR SELECT
USING (true);

CREATE POLICY "Users can update own profile"
ON public.profiles FOR UPDATE
USING (auth.uid() = id);

-- Projects: Members can view, owners can modify
CREATE POLICY "Projects viewable by owner"
ON public.projects FOR SELECT
USING (auth.uid() = owner_id);

CREATE POLICY "Projects insertable by authenticated"
ON public.projects FOR INSERT
WITH CHECK (auth.uid() = owner_id);

CREATE POLICY "Projects updatable by owner"
ON public.projects FOR UPDATE
USING (auth.uid() = owner_id);

CREATE POLICY "Projects deletable by owner"
ON public.projects FOR DELETE
USING (auth.uid() = owner_id);
warning

فعل دائماً RLS قبل الذهاب إلى الإنتاج. بدون سياسات، بياناتك متاحة للعامة من خلال واجهة برمجة التطبيقات المولدة تلقائياً.

اختبار سياسات RLS​

// Test RLS with different users
async function testRLS() {
// As anonymous user
const { data: anonData } = await supabase
.from('projects')
.select('*');
console.log('Anonymous:', anonData); // Should be empty or filtered

// As authenticated user
const { data: authData } = await supabase
.from('projects')
.select('*');
console.log('Authenticated:', authData); // Should show allowed rows
}

عمليات البيانات​

استعلامات آمنة للأنواع​

مع الأنواع المولدة، جميع الاستعلامات مكتوبة بالكامل:

import { supabase } from '@/lib/supabase';

// Fetch projects with their tasks
async function getProjectWithTasks(projectId: string) {
const { data, error } = await supabase
.from('projects')
.select(`
id,
name,
description,
tasks (
id,
title,
status,
priority,
assignee:profiles(full_name)
)
`)
.eq('id', projectId)
.single();

if (error) throw error;
return data;
}

// Insert with type checking
async function createTask(task: {
project_id: string;
title: string;
description?: string;
priority?: 'low' | 'medium' | 'high';
}) {
const { data, error } = await supabase
.from('tasks')
.insert(task)
.select()
.single();

if (error) throw error;
return data;
}

الاشتراكات في الوقت الفعلي​

بنِ واجهات مستخدم تُحدّث مباشرة مع real-time Supabase:

// hooks/useTasks.ts
'use client';

import { useEffect, useState } from 'react';
import { supabase } from '@/lib/supabase';
import { Database } from '@/types/supabase';

type Task = Database['public']['Tables']['tasks']['Row'];

export function useTasks(projectId: string) {
const [tasks, setTasks] = useState<Task[]>([]);

useEffect(() => {
// Initial fetch
const fetchTasks = async () => {
const { data } = await supabase
.from('tasks')
.select('*')
.eq('project_id', projectId);
if (data) setTasks(data);
};
fetchTasks();

// Subscribe to changes
const subscription = supabase
.channel(`project-${projectId}`)
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'tasks',
filter: `project_id=eq.${projectId}`,
},
(payload) => {
if (payload.eventType === 'INSERT') {
setTasks((prev) => [...prev, payload.new as Task]);
} else if (payload.eventType === 'UPDATE') {
setTasks((prev) =>
prev.map((t) => (t.id === payload.new.id ? (payload.new as Task) : t))
);
} else if (payload.eventType === 'DELETE') {
setTasks((prev) => prev.filter((t) => t.id !== payload.old.id));
}
}
)
.subscribe();

return () => {
subscription.unsubscribe();
};
}, [projectId]);

return tasks;
}

دوال الحافة​

إنشاء دوال Serverless​

انشر دوال TypeScript على الحافة:

// supabase/functions/send-notification/index.ts
import { serve } from 'https://deno.land/[email protected]/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';

serve(async (req) => {
const { taskId, userId } = await req.json();

const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
);

// Fetch task and user details
const { data: task } = await supabase
.from('tasks')
.select('title, projects(name)')
.eq('id', taskId)
.single();

// Send notification (example with email service)
const { data: user } = await supabase
.from('profiles')
.select('email')
.eq('id', userId)
.single();

// Log or send notification
console.log(`Notifying ${user?.email} about task: ${task?.title}`);

return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
});
});

نشر دوال الحافة​

# Deploy a function
supabase functions deploy send-notification

# Set secrets
supabase secrets set RESEND_API_KEY=your_key

# Invoke locally
supabase functions serve --no-verify-jwt

إنشاء أنواع TypeScript​

تحديث الأنواع الآلي​

أضف سكريبت لإعادة توليد الأنواع عندما يتغير المخطط:

// package.json
{
"scripts": {
"db:types": "supabase gen types typescript --project-id your-project-id --schema public > types/supabase.ts",
"db:reset": "supabase db reset && npm run db:types"
}
}

استخدام الأنواع المولدة​

// types/supabase.ts (auto-generated)
export type Database = {
public: {
Tables: {
tasks: {
Row: {
id: string;
title: string;
status: 'todo' | 'in_progress' | 'done';
// ...
};
Insert: {
// ...
};
Update: {
// ...
};
};
};
};
};

// Use in your code
import { Database } from '@/types/supabase';
type Task = Database['public']['Tables']['tasks']['Row'];

ملخص​

الجمع بين Cursor وSupabase يمكّنك من:

  • إنشاء مخططات قاعدة بيانات بوصفها بلغة طبيعية
  • توليد واجهات برمجة تطبيقات REST/GraphQL فورية مع PostgreSQL
  • تأمين البيانات بأمان مستوى الصف المولد بالذكاء الاصطناعي
  • بناء واجهات مستخدم في الوقت الفعلي مع الاشتراكات
  • نشر دوال TypeScript serverless عالمياً
  • الحفاظ على أمان الأنواع من قاعدة البيانات إلى الواجهة الأمامية

يعمل Cursor فهم مخطط Supabase التلقائي جنباً إلى جنب مع الأنواع المولدة على تسريع تطوير الواجهة الخلفية بشكل كبير، مما يسمح لك بالتركيز على منطق التطبيق بدلاً من الأكواد النمطية.


Last updated: July 2025. Supabase features depend on the installed CLI and project configuration.