From Zero to Production: Building a Full-Stack App with SvelteKit and Supabase
Introduction
This guide walks you through building a basic full-stack application using SvelteKit and Supabase. SvelteKit provides a fantastic developer experience for building fast and SEO-friendly web applications, while Supabase offers a powerful and easy-to-use backend-as-a-service.
Prerequisites
- Node.js (v16 or higher)
- npm or pnpm
- Supabase Account
Setting up your SvelteKit Project
First, create a new SvelteKit project:
npm create svelte@latest my-sveltekit-app
cd my-sveltekit-app
npm install
Initializing Supabase
- Create a new project on Supabase (https://supabase.com/).
- Once your project is created, navigate to the project settings and find your project URL and public API key. We'll need these later.
- Create a table in your Supabase database, name it for example
todos
, and add columns likeid
(UUID, primary key),text
(text), andcompleted
(boolean).
Configuring Supabase in SvelteKit
Install the Supabase JavaScript client:
npm install @supabase/supabase-js
Create a .env
file in the root of your project and add your Supabase credentials:
VITE_SUPABASE_URL="YOUR_SUPABASE_URL"
VITE_SUPABASE_ANON_KEY="YOUR_SUPABASE_ANON_KEY"
Create a supabaseClient.js
file in your src/lib
directory:
// 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)
Implementing Authentication
Let's add authentication using Supabase Auth UI. Install the necessary package:
npm install @supabase/auth-ui-svelte @supabase/auth-ui-shared
Create an +page.svelte
file inside src/routes
folder
// src/routes/+page.svelte
<script>
import { Auth } from '@supabase/auth-ui-svelte'
import '@supabase/auth-ui-shared/dist/index.css'
import { supabase } from '$lib/supabaseClient'
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
let session = null
onMount(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
if(session){
goto('/todos');
}
})
supabase.auth.onAuthStateChange((_event, _session) => {
session = _session
if(session){
goto('/todos');
}
})
})
</script>
{#if !session}
<Auth supabaseClient={supabase} appearance={{ theme: 'dark' }} providers={[]} />
{/if}
Now create a /todos
route and a +page.svelte
file inside src/routes/todos
folder
// src/routes/todos/+page.svelte
<script>
import { supabase } from '$lib/supabaseClient';
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
let todos = [];
let newTodo = '';
onMount(async () => {
const { data, error } = await supabase
.from('todos')
.select('*')
.order('id', { ascending: false });
if (error) {
console.error('Error fetching todos:', error);
return;
}
todos = data;
});
async function addTodo() {
const { data, error } = await supabase
.from('todos')
.insert([{ text: newTodo }])
.select();
if (error) {
console.error('Error adding todo:', error);
return;
}
todos = [...data, ...todos];
newTodo = '';
}
async function toggleComplete(todo) {
const { error } = await supabase
.from('todos')
.update({ completed: !todo.completed })
.eq('id', todo.id);
if (error) {
console.error('Error updating todo:', error);
return;
}
todos = todos.map((t) => {
if(t.id === todo.id) {
return {...t, completed: !t.completed}
}
return t;
})
}
async function deleteTodo(todo) {
const { error } = await supabase
.from('todos')
.delete()
.eq('id', todo.id);
if (error) {
console.error('Error deleting todo:', error);
return;
}
todos = todos.filter((t) => t.id !== todo.id);
}
async function signOut() {
await supabase.auth.signOut()
goto('/')
}
</script>
<h1>My Todos</h1>
<input type="text" bind:value={newTodo} placeholder="Add a new todo" />
<button on:click={addTodo}>Add</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.text}</span>
<button on:click={() => deleteTodo(todo)}>Delete</button>
</li>
{/each}
</ul>
<button on:click={signOut}>Sign out</button>
<style>
.completed {
text-decoration: line-through;
color: gray;
}
</style>
Running Your Application
npm run dev -- --open
Your application should now be running at http://localhost:5173
.
Conclusion
You've successfully built a full-stack application using SvelteKit and Supabase. This is just the beginning! Explore SvelteKit and Supabase further to build more complex and feature-rich applications. Remember to secure your application, handle errors gracefully, and optimize performance for production.