experiment-bun-sveltekit-todo/README.md
2026-07-12 15:19:24 +02:00

6.1 KiB

experiment-bun-sveltekit-todo

A to-do list app built as an experiment in running SvelteKit on Bun, with Bun's native SQLite for persistence.

The app works with and without JavaScript: the initial page is rendered on the server, and form actions provide a no-JS fallback. When JavaScript is enabled, subsequent interactions use the REST API for a smoother experience.

Features

  • Create, rename, toggle, reorder, and delete tasks
  • Task reordering powered by LexoRank
  • Server-side rendering with progressive enhancement
  • Persistent storage in a local SQLite database
  • Dark UI built with Halfmoon CSS and Phosphor icons
  • Optional compilation to a standalone executable

Tech stack

Layer Technology
Runtime Bun
Framework SvelteKit 2 + Svelte 5
HTTP Axios (fetch adapter; SvelteKit event.fetch on the server)
Database Bun SQLite (data/todo.sqlite)
Validation Joi
Ordering lexorank
UI Halfmoon, Phosphor Icons

Requirements

Getting started

Install dependencies:

bun install

Start the development server:

bun start

The app is available at http://localhost:5173.

Production

Build the app:

bun run build

Run the production server:

bun start:prod

Copy .env.example to .env before running the production server. See Environment variables below.

Standalone binary

After building, you can compile a self-contained executable:

bun run compile

This runs compile.sh, which uses bun build --compile to produce a binary at data/todo-<git-sha> (or data/todo-<VERSION> if the VERSION environment variable is set).

Environment variables

The only environment variable that belongs to this project is PORT — it sets which port the production server listens to.

However, because of a Bun issue with forwarded headers, .env must also define three additional variables as a workaround:

  • ORIGIN=http://localhost:{PORT} — must match the port above
  • PROTOCOL_HEADER=x-forwarded-proto
  • HOST_HEADER=x-forwarded-host

Copy .env.example to get started:

cp .env.example .env

If you change PORT, update ORIGIN to use the same port.

API

All endpoints return JSON and validate input with Joi.

Method Path Description
GET /api/tasks List tasks, ordered by rank. Optional query param: limit
POST /api/task Create a task. Optional query param: nextRank
GET /api/task/:id Get a single task
PATCH /api/task/:id Update name, isDone, and/or rank
DELETE /api/task/:id Delete a task
PATCH /api/task/:id/move Reorder a task. Query params: direction (up or down), targetRank1, optional targetRank2

Task shape

{
  "id": "abc123",
  "rank": "0|hzzzzz:",
  "isDone": false,
  "name": "Buy groceries"
}

Project structure

src/
├── hooks.server.js          # Initializes the SQLite database on startup
├── lib/
│   ├── api.js               # Axios client (browser + SvelteKit fetch)
│   ├── database.js          # SQLite queries
│   ├── hydrated.js          # Client hydration flag
│   └── taskHelpers.js       # LexoRank move helpers
└── routes/
    ├── +layout.svelte       # App shell and navigation
    ├── +page.svelte         # Tasks page
    ├── +page.js             # Loads tasks (SSR / client-aware)
    ├── +page.server.js      # Form actions (no-JS fallback)
    ├── Tasks.svelte         # Task list UI and client-side API calls
    ├── about/+page.svelte   # About page
    └── api/                 # REST endpoints
data/
└── todo.sqlite              # SQLite database (created automatically)

Scripts

Command Description
bun start Start the Vite dev server
bun run build Build for production
bun start:prod Run the built server (build/index.js)
bun run compile Compile the built server into a standalone binary

License

MIT