157 lines
6.1 KiB
Markdown
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>
|
|
|
|

|
|
|
|
</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
|