Getting Started

Tutorial: Zero to Hero SaaS

Build a fully functional Customer Feedback SaaS in 15 minutes using OmniSvelte.

Build a SaaS in 15 Minutes

Welcome to the OmniSvelte Zero-to-Hero tutorial! In this guide, we won't just build a toy app—we will build a Customer Feedback SaaS (think Canny or UserVoice) from scratch.

We will cover setting up your database, defining relationships, scaffolding the UI, securing API endpoints with authentication, and utilizing SvelteKit remote functions for real-time reactivity.

Prerequisites
Before you start, make sure you have Node.js 18+ installed and a working terminal.

Initialize your project

First, use the OmniSvelte CLI to create a new project with our built-in authentication template.
bash
omni init feedback-saas --with-auth
cd feedback-saas

Here's the structure of your newly created OmniSvelte project:

        • schema.ts
        • db.ts
        • +page.svelte
    • omni.config.ts
    • package.json

Define your SaaS Schema & Relationships

A real SaaS needs complex data. Open `src/lib/schema.ts` and define a `Product` model and a `Feedback` model. We'll set up a one-to-many relationship so every piece of feedback belongs to a specific product.
typescript
import { defineSchema, field } from '@omni-svelte/core';
 
export const Product = defineSchema('products', {
  name: field.string(),
  slug: field.string().unique(),
});
 
export const Feedback = defineSchema('feedback', {
  title: field.string(),
  description: field.text(),
  status: field.enum(['open', 'planned', 'shipped']).default('open'),
  upvotes: field.integer().default(0),
  productId: field.references(() => Product.id),
  userId: field.references(() => User.id) // Automatically injected by BetterAuth
});

The framework will automatically generate the database migrations, TypeScript types, and Zod validation schemas for you.

Generate the UI and APIs

Instead of writing boilerplate, let OmniSvelte generate the CRUD operations, admin tables, and SvelteKit Remote Functions for your products.
bash
omni generate resource Product
omni generate resource Feedback

This single command wires up the backend APIs, generates accessible forms using Zod, and creates the frontend views using shadcn-svelte.

Client (SvelteKit)
Remote Function
Database Model

Secure your Remote Functions

SaaS applications need authorization. Let's modify the generated `src/lib/server/feedback.remote.ts` to ensure only logged-in users can submit feedback. We'll use OmniSvelte's `$auth/server` virtual module.
typescript
import { command } from '@omni-svelte/core/remote';
import { auth } from '$auth/server';
import { Feedback } from '$models/Feedback';
 
export const createFeedback = command()
  .schema(Feedback.insertSchema)
  .use(async ({ request, locals }) => {
    // Middleware: Ensure user is logged in
    const session = await auth.api.getSession({ headers: request.headers });
    if (!session) throw new Error("Unauthorized");
    
    locals.user = session.user;
  })
  .handler(async ({ input, locals }) => {
    // Handler: Create the feedback in the DB
    return await Feedback.create({
      ...input,
      userId: locals.user.id
    });
  });
Preview
This secure API endpoint is now accessible directly from your Svelte components!

Build a Real-Time Feed

Let's render a real-time list of feedback on our product page. OmniSvelte provides `query.live`, which streams updates directly to the client whenever the `Feedback` model is mutated.
svelte
<script lang="ts">
  import { getFeedbackLive } from '$lib/server/feedback.remote';
  
  let { data } = $props();
  
  // This store will automatically update when anyone submits new feedback!
  const feedbackFeed = getFeedbackLive({ productId: data.product.id });
</script>
 
<div class="space-y-4">
  {#each $feedbackFeed as item}
    <div class="p-4 border rounded-md shadow-sm">
      <h3 class="font-bold">{item.title}</h3>
      <p class="text-sm text-muted-foreground">{item.description}</p>
      <span class="text-xs bg-primary/10 text-primary px-2 py-1 rounded-full">
        {item.status}
      </span>
    </div>
  {/each}
</div>
Real-time Feed Preview

Add dark mode

We really need a dark mode for late night coding.

planned

Faster search

Search takes too long on large tables.

open

Run the application

Run your migrations to sync your schema with the database, then start the dev server!
bash
omni db push
npm run dev
You built a SaaS!
You've just built a secure, real-time Customer Feedback SaaS with database models, relationships, and authentication. As OmniSvelte adds new features like **Jobs** and **Payments**, this tutorial will be updated to show you how to charge your customers and send them email notifications!