العودة للمقالات

إنشاء تطبيق CRUD متكامل باستخدام SvelteKit و Supabase: دليل المبتدئين

المؤلفMajd Muhtaseb09‏/11‏/202520 دقيقة

مقدمة

سيرشدك هذا الدليل خلال إنشاء تطبيق بسيط لقائمة المهام باستخدام SvelteKit للواجهة الأمامية ومسارات API الخلفية، و Supabase لقاعدة البيانات والمصادقة. سنغطي عمليات CRUD الأساسية: إنشاء وقراءة وتحديث وحذف عناصر قائمة المهام.

المتطلبات الأساسية

  • Node.js (أحدث إصدار LTS)
  • npm أو pnpm
  • حساب Supabase (الخطة المجانية كافية)

الإعداد

  1. إنشاء مشروع SvelteKit:

    npm create svelte@latest my-todo-app
    cd my-todo-app
    npm install
    

    اختر المشروع الهيكلي. سنضيف Typescript لضمان كتابة البيانات بشكل صحيح.

  2. تهيئة Supabase:

    أنشئ مشروعًا جديدًا على Supabase. بمجرد إنشائه، احصل على عنوان URL الخاص بـ Supabase ومفتاح anon من إعدادات API.

  3. تثبيت عميل JavaScript الخاص بـ Supabase:

    npm install @supabase/supabase-js
    npm install -D @sveltejs/adapter-auto
    

    أيضًا، قم بتثبيت حزم Typescript التي ستساعد في التطوير

    npm install -D typescript svelte-check svelte-preprocess
    

إنشاء عميل Supabase

أنشئ ملف src/lib/supabaseClient.js:

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

هام: قم بتخزين عنوان URL الخاص بـ Supabase ومفتاح anon في ملفات .env وارجع إليهما باستخدام import.meta.env. لا تقم أبدًا بتضمين هذه المفاتيح مباشرةً في مستودعك!

أنشئ ملفات .env و .env.development:

VITE_SUPABASE_URL="your_supabase_url"
VITE_SUPABASE_ANON_KEY="your_supabase_anon_key"

قد تحتاج إلى تحديث svelte.config.js إذا واجهت مشاكل مع المتغيرات البيئية. فيما يلي نموذج تكوين

import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	preprocess: vitePreprocess(),

	kit: {
		adapter: adapter(),
        vite: {
            define: {
                'process.env': process.env
            }
        }
	}
};

export default config;

بناء واجهة المستخدم (src/routes/+page.svelte)

<script>
    import { supabase } from '$lib/supabaseClient';
    import { onMount } from 'svelte';

    let todos = [];
    let newTodo = '';

    onMount(async () => {
        await fetchTodos();
    });

    async function fetchTodos() {
        const { data, error } = await supabase
            .from('todos')
            .select('*')
            .order('created_at', { ascending: false });

        if (error) {
            console.error('Error fetching todos:', error);
            return;
        }

        todos = data;
    }

    async function addTodo() {
        if (!newTodo.trim()) return;

        const { data, error } = await supabase
            .from('todos')
            .insert([{ task: newTodo }])
            .single();

        if (error) {
            console.error('Error adding todo:', error);
            return;
        }

        todos = [data, ...todos];
        newTodo = '';
    }

    async function deleteTodo(id) {
        const { error } = await supabase
            .from('todos')
            .delete()
            .eq('id', id);

        if (error) {
            console.error('Error deleting todo:', error);
            return;
        }

        todos = todos.filter(todo => todo.id !== id);
    }

    async function toggleComplete(todo) {
        const { error } = await supabase
            .from('todos')
            .update({ completed: !todo.completed })
            .eq('id', todo.id)
            .single();

        if (error) {
            console.error('Error updating todo:', error);
            return;
        }

        todo.completed = !todo.completed;
        todos = [...todos]; // Trigger reactivity
    }
</script>

<input type="text" bind:value={newTodo} placeholder="أضف مهمة جديدة" />
<button on:click={addTodo}>إضافة</button>

<ul>
    {#each todos as todo (todo.id)}
        <li>
            <input type="checkbox" checked={todo.completed} on:change={() => toggleComplete(todo)} />
            <span class:completed={todo.completed}>{todo.task}</span>
            <button on:click={() => deleteTodo(todo.id)}>حذف</button>
        </li>
    {/each}
</ul>

<style>
    .completed {
        text-decoration: line-through;
        color: gray;
    }
    ul {
        list-style: none;
        padding: 0;
    }

    li {
        display: flex;
        align-items: center;
        margin-bottom: 5px;
    }

    li > * {
        margin-right: 10px;
    }
</style>

إنشاء جدول 'todos' في Supabase

في لوحة معلومات Supabase الخاصة بك، انتقل إلى محرر SQL وقم بتشغيل SQL التالي:

CREATE TABLE todos (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()),
    task TEXT NOT NULL,
    completed BOOLEAN NOT NULL DEFAULT FALSE
);

قم بتمكين أمان مستوى الصف (RLS) وإنشاء سياسات بناءً على معرف المستخدم. في هذا البرنامج التعليمي، نفترض عدم وجود مصادقة وتعطيل RLS. تحذير: هذا غير آمن للغاية للاستخدام في الإنتاج

شرح

  • src/lib/supabaseClient.js: يقوم بتهيئة عميل Supabase.
  • src/routes/+page.svelte:
    • يجلب المهام عند التحميل.
    • addTodo ينشئ مهمة جديدة في Supabase.
    • deleteTodo يحذف مهمة من Supabase.
    • toggleComplete يقوم بتحديث حالة الإكمال في Supabase.
    • تستخدم واجهة المستخدم تفاعل Svelte لعرض المهام.

تشغيل التطبيق

npm run dev -- --open

سيعمل تطبيقك على http://localhost:5173/.

الخطوات التالية

  • تنفيذ مصادقة المستخدم باستخدام Supabase Auth.
  • إضافة ميزات أكثر تقدمًا مثل تعديل المهام وتحديد المواعيد النهائية وما إلى ذلك.
  • تحسين واجهة المستخدم باستخدام أطر عمل CSS مثل Tailwind CSS.