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

157 lines
6.1 KiB
Markdown

# experiment-bun-sveltekit-todo
<table><tr><td>
A to-do list app built as an experiment in running [SvelteKit](https://kit.svelte.dev/) on [Bun](https://bun.sh/), with [Bun's native SQLite](https://bun.sh/docs/api/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](https://medium.hostux.net/whisperarts/lexorank-what-are-they-and-how-to-use-them-for-efficient-list-sorting-a48fc4e7849f)
- Server-side rendering with progressive enhancement
- Persistent storage in a local SQLite database
- Dark UI built with [Halfmoon](https://github.com/halfmoonui/halfmoon) CSS and [Phosphor](https://github.com/phosphor-icons/web) icons
- Optional compilation to a standalone executable
</td><td>
![](demo.gif)
</td></tr></table>
## Tech stack
| Layer | Technology |
|------------|-------------------------------------------------------------------------------------------------------------|
| Runtime | [Bun](https://bun.sh/) |
| Framework | [SvelteKit](https://kit.svelte.dev/) 2 + [Svelte](https://svelte.dev/) 5 |
| HTTP | [Axios](https://axios-http.com/) (fetch adapter; SvelteKit `event.fetch` on the server) |
| Database | [Bun SQLite](https://bun.sh/docs/api/sqlite) (`data/todo.sqlite`) |
| Validation | [Joi](https://joi.dev/) |
| Ordering | [lexorank](https://github.com/kvandake/lexorank-ts) |
| UI | [Halfmoon](https://github.com/halfmoonui/halfmoon), [Phosphor Icons](https://github.com/phosphor-icons/web) |
## Requirements
- [Bun](https://bun.sh/) 1.x
## Getting started
Install dependencies:
```bash
bun install
```
Start the development server:
```bash
bun start
```
The app is available at [http://localhost:5173](http://localhost:5173).
## Production
Build the app:
```bash
bun run build
```
Run the production server:
```bash
bun start:prod
```
Copy [`.env.example`](.env.example) to `.env` before running the production server. See [Environment variables](#environment-variables) below.
### Standalone binary
After building, you can compile a self-contained executable:
```bash
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](https://github.com/oven-sh/bun/issues/7951#issuecomment-1875361606), `.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`](.env.example) to get started:
```bash
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
```json
{
"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