Back to articles

From Zero to Production: Building a Full-Stack App with SvelteKit and Supabase

AuthorMajd Muhtaseb06/18/202515 minutes
From Zero to Production: Building a Full-Stack App with SvelteKit and Supabase

Introduction

This guide walks you through building a simple full-stack application using SvelteKit and Supabase. SvelteKit offers a fantastic developer experience for building performant web applications, while Supabase provides a powerful and easy-to-use backend-as-a-service. We'll create a basic "To-Do" application to illustrate the process.

Prerequisites

  • Node.js and npm installed
  • A Supabase account (free tier available)
  • Basic understanding of JavaScript, HTML, and CSS

Step 1: Setting up your SvelteKit project

First, let's create a new SvelteKit project using the following command:

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

Choose the "SvelteKit app" and other default options during the setup process.

Step 2: Setting up Supabase

  1. Create a new project in your Supabase account.

  2. Once your project is created, navigate to the SQL editor and create a todos table with the following schema:

    CREATE TABLE todos (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        text VARCHAR(255) NOT NULL,
        completed BOOLEAN NOT NULL DEFAULT FALSE,
        created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now())
    );
    
  3. Go to your project settings and copy your Supabase URL and anon key. You'll need these to connect your SvelteKit app to your Supabase database.

Step 3: Connecting SvelteKit to Supabase

Install the Supabase JavaScript client:

npm install @supabase/supabase-js

Create a supabaseClient.js file in the src/lib directory with the following code, replacing the placeholders with your Supabase URL and anon key:

// src/lib/supabaseClient.js
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = 'YOUR_SUPABASE_URL'
const supabaseAnonKey = 'YOUR_SUPABASE_ANON_KEY'

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

Step 4: Building the To-Do List UI

Let's create a simple UI to display and manage our to-dos. Modify src/routes/+page.svelte as follows:

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

  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);
    } else {
      todos = data;
    }
  }

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

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

    if (error) {
      console.error('Error adding todo:', error);
    } else {
      todos = [...data, ...todos];
      newTodo = '';
    }
  }

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

    if (error) {
      console.error('Error updating todo:', error);
    } else {
      todos = todos.map(todo => todo.id === id ? {...todo, completed: !completed} : todo);
      todos = [...todos];
    }
  }

  async function deleteTodo(id) {
    const { error } = await supabase.from('todos').delete().eq('id', id);
    if (error) {
      console.error('Error deleting todo:', error);
    } else {
      todos = todos.filter(todo => todo.id !== id);
    }
  }
</script>

<main>
  <h1>To-Do List</h1>

  <input type="text" bind:value={newTodo} placeholder="Add a new to-do" on:keydown={(e) => e.key === 'Enter' ? addTodo() : null}>
  <button on:click={addTodo}>Add</button>

  <ul>
    {#each todos as todo (todo.id)}
      <li>
        <input type="checkbox" checked={todo.completed} on:click={() => toggleComplete(todo.id, todo.completed)}>
        <span class:completed={todo.completed}>{todo.text}</span>
        <button on:click={() => deleteTodo(todo.id)}>Delete</button>
      </li>
    {/each}
  </ul>
</main>

<style>
  .completed {
    text-decoration: line-through;
    color: gray;
  }

  ul {
    list-style: none;
    padding: 0;
  }

  li {
    display: flex;
    align-items: center;
    margin-bottom: 0.5rem;
  }

  li span {
    margin-left: 0.5rem;
    margin-right: auto;
  }
</style>

Step 5: Run Your Application

Start the development server:

npm run dev

Open your browser and navigate to http://localhost:5173. You should see your to-do application running.

Conclusion

This tutorial provides a basic example of building a full-stack application with SvelteKit and Supabase. You can extend this application by adding features like user authentication, more complex data structures, and a more polished UI. Happy coding!