Back to articles

Build a Full-Stack CRUD App with SvelteKit and Prisma

AuthorMajd Muhtaseb05/28/202515 minutes
Build a Full-Stack CRUD App with SvelteKit and Prisma

Introduction

This article demonstrates how to build a basic CRUD (Create, Read, Update, Delete) application using SvelteKit and Prisma. SvelteKit provides a powerful framework for building web applications, and Prisma simplifies database interactions.

Prerequisites

  • Node.js and npm (or pnpm, or yarn) installed
  • Basic knowledge of Svelte and SvelteKit
  • Docker (optional, for local database)

Setting up the Project

  1. Create a new SvelteKit project:

    npm create svelte@latest my-crud-app
    cd my-crud-app
    npm install
    
  2. Install Prisma:

    npm install @prisma/client prisma --save-dev
    npx prisma init --datasource-provider postgresql
    

    (Adjust the datasource-provider as needed, e.g., mysql, sqlite.)

  3. Update your .env file with the database URL. If you are using Docker, you can use something like: DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"

  4. Define your data model in prisma/schema.prisma. For example, a simple Todo model:

    generator client {
      provider = "prisma-client-js"
    }
    
    datasource db {
      provider = "postgresql" // or your chosen provider
      url      = env("DATABASE_URL")
    }
    
    model Todo {
      id        Int      @id @default(autoincrement())
      text      String
      completed Boolean  @default(false)
      createdAt DateTime @default(now())
    }
    
  5. Generate the Prisma client and run migrations:

    npx prisma generate
    npx prisma migrate dev --name init
    

Building the API Endpoints (SvelteKit Routes)

Create API endpoints in src/routes/api/todos/+server.ts (GET and POST) and src/routes/api/todos/[id]/+server.ts (GET, PUT, DELETE):

src/routes/api/todos/+server.ts (GET & POST)

import { json } from '@sveltejs/kit';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export async function GET() {
  const todos = await prisma.todo.findMany();
  return json(todos);
}

export async function POST({ request }) {
  const { text } = await request.json();
  const todo = await prisma.todo.create({
    data: { text },
  });
  return json(todo, { status: 201 });
}

src/routes/api/todos/[id]/+server.ts (GET, PUT & DELETE)

import { json, error } from '@sveltejs/kit';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export async function GET({ params }) {
  const { id } = params;
  const todo = await prisma.todo.findUnique({
    where: { id: parseInt(id) },
  });

  if (!todo) {
    throw error(404, 'Todo not found');
  }

  return json(todo);
}

export async function PUT({ params, request }) {
  const { id } = params;
  const { text, completed } = await request.json();

  const todo = await prisma.todo.update({
    where: { id: parseInt(id) },
    data: { text, completed },
  });

  return json(todo);
}


export async function DELETE({ params }) {
  const { id } = params;
  await prisma.todo.delete({
    where: { id: parseInt(id) },
  });

  return json({ success: true });
}

Creating the Svelte Frontend

  1. Displaying Todos (src/routes/+page.svelte)

    <script>
      import { onMount } from 'svelte';
    
      let todos = [];
      let newTodo = '';
    
      async function fetchTodos() {
        const response = await fetch('/api/todos');
        todos = await response.json();
      }
    
      async function addTodo() {
        if (newTodo.trim() === '') return;
        const response = await fetch('/api/todos', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ text: newTodo }),
        });
        if (response.ok) {
          newTodo = '';
          await fetchTodos();
        }
      }
    
      async function deleteTodo(id) {
        const response = await fetch(`/api/todos/${id}`, {
          method: 'DELETE',
        });
        if (response.ok) {
          await fetchTodos();
        }
      }
    
      onMount(fetchTodos);
    </script>
    
    <h1>Todo App</h1>
    <input bind:value={newTodo} placeholder="Add a todo" />
    <button on:click={addTodo}>Add</button>
    
    <ul>
      {#each todos as todo (todo.id)}
        <li>
          {todo.text}
          <button on:click={() => deleteTodo(todo.id)}>Delete</button>
        </li>
      {/each}
    </ul>
    

Running the Application

  1. Start your SvelteKit development server:

    npm run dev -- --open
    

Conclusion

This example provides a basic CRUD application using SvelteKit and Prisma. You can expand upon this foundation by adding features like editing todos, marking them as complete, and implementing user authentication. Remember to handle errors gracefully and add proper validation.