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 |
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
- Bun 1.x
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 abovePROTOCOL_HEADER=x-forwarded-protoHOST_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
