Skip to content

Development Guide

This guide shows you how to build your own SaaS application on top of FastSvelte.

Building Your Application

FastSvelte includes an AI Note Improver demo to showcase the platform's capabilities. Here's how to replace it with your own application:

Step 1: Plan Your Application

Define your core business entities and features:

  • What is your main domain model? (e.g., Projects, Tasks, Documents, etc.)
  • What actions can users perform? (Create, edit, share, export, etc.)
  • What billing model will you use? (Per-seat, usage-based, tiered, etc.)

Step 2: Design Your Database Schema

Create Sqitch migrations for your domain:

cd db
sqitch add your_feature -n "Add your feature tables"

Edit deploy/your_feature.sql:

-- Example: Replace notes with your domain
CREATE TABLE IF NOT EXISTS fastsvelte.project (
    id SERIAL PRIMARY KEY,
    organization_id INTEGER NOT NULL REFERENCES fastsvelte.organization(id),
    user_id INTEGER NOT NULL REFERENCES fastsvelte."user"(id),
    name TEXT NOT NULL,
    description TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_project_org ON fastsvelte.project(organization_id);
CREATE INDEX idx_project_user ON fastsvelte.project(user_id);

Deploy your migration:

./sqitch.sh dev deploy

Step 3: Implement Backend Logic

Follow FastSvelte's layered architecture (use the note implementation as a template):

1. Create models (backend/app/model/your_model.py):

from pydantic import BaseModel
from datetime import datetime

class Project(BaseModel):
    id: int
    organization_id: int
    user_id: int
    name: str
    description: str | None
    created_at: datetime
    updated_at: datetime

class ProjectCreate(BaseModel):
    name: str
    description: str | None = None

2. Create repository (backend/app/data/repo/your_repo.py):

from app.data.db_config import DbConfig
from app.model.your_model import Project

class ProjectRepo:
    def __init__(self, db_config: DbConfig):
        self.db_config = db_config
        self.schema = db_config.schema

    async def create_project(self, org_id: int, user_id: int, data: ProjectCreate) -> int:
        query = f"""
            INSERT INTO {self.schema}.project (organization_id, user_id, name, description)
            VALUES ($1, $2, $3, $4)
            RETURNING id
        """
        return await self.db_config.fetch_val(query, org_id, user_id, data.name, data.description)

    async def get_by_id(self, project_id: int) -> Project | None:
        query = f"SELECT * FROM {self.schema}.project WHERE id = $1"
        row = await self.db_config.fetch_one(query, project_id)
        return Project(**row) if row else None

3. Create service (backend/app/service/your_service.py):

from app.data.repo.your_repo import ProjectRepo
from app.model.your_model import Project, ProjectCreate

class ProjectService:
    def __init__(self, project_repo: ProjectRepo):
        self.project_repo = project_repo

    async def create_project(self, org_id: int, user_id: int, data: ProjectCreate) -> Project:
        project_id = await self.project_repo.create_project(org_id, user_id, data)
        project = await self.project_repo.get_by_id(project_id)
        return project

4. Create API routes (backend/app/api/route/your_route.py):

from fastapi import APIRouter, Depends
from app.service.your_service import ProjectService
from app.model.your_model import Project, ProjectCreate
from app.util.auth_util import CurrentUser, get_current_user

router = APIRouter(prefix="/api/project", tags=["project"])

@router.post("", response_model=Project)
async def create_project(
    data: ProjectCreate,
    current_user: CurrentUser = Depends(get_current_user),
    project_service: ProjectService = Depends()
):
    return await project_service.create_project(
        current_user.organization_id,
        current_user.id,
        data
    )

5. Register in DI container (backend/app/config/container.py):

# Add to Container class
self.project_repo = providers.Factory(ProjectRepo, db_config=self.db_config)
self.project_service = providers.Singleton(ProjectService, project_repo=self.project_repo)

# Add to wiring_config
wiring_config = containers.WiringConfiguration(
    modules=[
        "app.api.route.your_route",  # Add this
        # ... existing modules
    ]
)

6. Register route (backend/app/main.py):

from app.api.route import your_route

app.include_router(your_route.router)

7. Run the backend server to test your new API:

uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

Step 4: Build Frontend UI

1. Create API client types:

cd frontend
npm run generate  # Auto-generates TypeScript client from your OpenAPI spec

2. Create Svelte pages (replace frontend/src/routes/(protected)/notes/ with your feature):

<!-- frontend/src/routes/(protected)/projects/+page.svelte -->
<script lang="ts">
  import { projectApi } from '$lib/api/gen/projects';
  import { onMount } from 'svelte';

  let projects = $state([]);

  onMount(async () => {
    const response = await projectApi.listProjects();
    projects = response.data;
  });
</script>

<h1>My Projects</h1>
{#each projects as project}
  <div>{project.name}</div>
{/each}

Step 5: Remove Demo Code

If you've finished setting up your project, clean up the note demo:

# Remove note files
rm backend/app/model/note_model.py
rm backend/app/service/note_service.py
rm backend/app/service/note_organizer_service.py
rm backend/app/api/route/note_route.py
rm -rf frontend/src/routes/(protected)/notes

# Remove note migrations
cd db
sqitch revert --to @HEAD^  # Or manually remove note-related migrations

# Remove OpenAI dependency if not needed
# Edit backend/requirements.in and remove openai

Common Development Workflows

When making changes, follow the DB → Backend → Frontend flow:

Database Changes

cd db
sqitch add feature_name -n "Description"
# Edit deploy/, revert/, verify/ SQL files
./sqitch.sh dev deploy

Backend Changes

cd backend
source .venv/bin/activate

# Add or remove dependencies
# Edit requirements.dev.in (for development) or requirements.in (for production), then:
pip-compile --no-strip-extras requirements.dev.in
pip install -r requirements.dev.txt

# Run linter
ruff check .

# Run tests
pytest

# Start server (auto-reloads on code changes)
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

Frontend Changes

After backend API changes, regenerate the API client:

cd frontend

# Generate TypeScript client (backend must be running)
npm run generate

# Run linter and formatter
npm run format    # Prettier
npm run check     # Type checking

# Run tests
npm run test        # All tests
npm run test:unit   # Unit tests only
npm run test:e2e    # E2E tests only

# Start dev server (hot-reloads on code changes)
npm run dev

Next Steps