Laravel + Svelte ToDo App
Environment: WSL Ubuntu Backend: Laravel (php 8.4) Frontend: Svelte Database: MySQL
Setup 1 - sudo apt update
2 - Install php 8.4, includes composer /bin/bash -c "$(curl -fsSL https://php.new/install/linux/8.4)"
3 - Install Node, make sure to check nvm version on github curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
source ~/.bashrc
nvm install v24.14.1
4 - Install and Start MySQL
sudo apt install mysql-server
sudo systemctl start mysql.service
5 - Create User and Grant Privilege
sudo mysql
CREATE USER 'john'@'localhost' IDENTIFIED BY 'password';
CREATE DATABASE IF NOT EXISTS cytonn_to_do_app;
GRANT ALL PRIVILEGES ON cytonn_to_do_app.* TO 'john'@'localhost';
FLUSH PRIVILEGES;
6 - Create Laravel Project
laravel new backend
starter kit - none; testing framework - pest; laravel boost - no; database driver - mysql; run migrations - no; npm install - yes;
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=cytonn_to_do_app DB_USERNAME=john DB_PASSWORD=password
7 - Sanctum API
php artisan install:api
8 - Configure CORS
php artisan config:publish cors
'paths' => ['api/', 'sanctum/csrf-cookie'], [ 'allowed_methods' => [''], 'allowed_origins' => ['http://127.0.0.1:5173'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['*'], 'exposed_headers' => [], 'max_age' => 0, 'supports_credentials' => true, ]
9 - Create Sveltekit Project, following shadcn init (https://shadcn-svelte.com/docs/installation/sveltekit)
npx sv create frontend --add tailwindcss
npx shadcn-svelte@latest init
10 - Migrations
php artisan make:migration create_todo_tables
php artisan migrate
11 - Models
php artisan make:model Task
php artisan make:model TaskLog
php artisan make:model TaskReport
12 - Controllers
php artisan make:controller TaskController
php artisan make:controller ReportController
13 - Routes
// Public routes Route::post('/register', [AuthController::class, 'register']); Route::post('/login', [AuthController::class, 'login']);
// Protected routes Route::middleware('auth:sanctum')->group(function () {
... });
14 - Seeders
php artisan db:seed
15 - Auth
class User extends Authenticatable
{
/** @use HasFactory
php artisan make:controller AuthController
$user = User::where('email', $request->input('email'))->first(); $token = $user->createToken('auth_token')->plainTextToken;
interface AuthState { token: string | null; isAuthenticated: boolean; user: UserData | null; isLoading: boolean; error: string | null; }
interface UserData { id: number; email: string; name: string; }
let csrfToken: string | null = null;
16 - MySQL Data
mysql> USE cytonn_to_do_app; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A
Database changed mysql> SELECT * FROM users;
| id | name | email | email_verified_at | password | remember_token | created_at | updated_at | +----+-----------------+---------------------------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+ | 1 | Daniel Njaramba | danielnjaramba8@gmail.com | 2026-03-31 18:45:15 | $2y$12$B0WvYLF9mfHtg1pTRcse8.F/e7JnolrgR6JcTW94e6wqq158IEFFm | C4SWfUaScX | 2026-03-31 18:45:16 | 2026-03-31 18:45:16 |
1 row in set (0.00 sec)
mysql> SELECT * FROM tasks; +----+---------+------------------+------------+----------+-------------+---------------------+---------------------+ | id | user_id | title | due_date | priority | status | created_at | updated_at | +----+---------+------------------+------------+----------+-------------+---------------------+---------------------+ | 1 | 1 | Task 1 - Mar 31 | 2026-03-31 | high | pending | 2026-03-31 00:00:00 | 2026-03-31 00:00:00 | | 2 | 1 | Task 2 - Mar 31 | 2026-03-31 | high | done | 2026-03-31 00:00:00 | 2026-03-31 00:00:00 | | 3 | 1 | Task 3 - Mar 31 | 2026-03-31 | medium | in_progress | 2026-03-31 00:00:00 | 2026-03-31 00:00:00 | | 4 | 1 | Task 4 - Mar 31 | 2026-03-31 | low | in_progress | 2026-03-31 00:00:00 | 2026-03-31 00:00:00 | | 5 | 1 | Task 5 - Mar 31 | 2026-03-31 | medium | pending | 2026-03-31 00:00:00 | 2026-03-31 00:00:00 | | 6 | 1 | Task 6 - Mar 31 | 2026-03-31 | high | pending | 2026-03-31 00:00:00 | 2026-03-31 00:00:00 | | 7 | 1 | Task 1 - Mar 30 | 2026-03-30 | low | pending | 2026-03-30 00:00:00 | 2026-03-30 00:00:00 | | 8 | 1 | Task 2 - Mar 30 | 2026-03-30 | low | pending | 2026-03-30 00:00:00 | 2026-03-30 00:00:00 | | 9 | 1 | Task 3 - Mar 30 | 2026-03-30 | medium | in_progress | 2026-03-30 00:00:00 | 2026-03-30 00:00:00 | | 10 | 1 | Task 4 - Mar 30 | 2026-03-30 | low | in_progress | 2026-03-30 00:00:00 | 2026-03-30 00:00:00 | | 11 | 1 | Task 5 - Mar 30 | 2026-03-30 | low | in_progress | 2026-03-30 00:00:00 | 2026-03-30 00:00:00 |
mysql> SELECT * FROM task_logs; +----+---------+-----------+------+---------------------+------------+ | id | task_id | action | data | created_at | updated_at | +----+---------+-----------+------+---------------------+------------+ | 1 | 1 | created | NULL | 2026-03-31 00:00:00 | NULL | | 2 | 2 | completed | NULL | 2026-03-31 00:00:00 | NULL | | 3 | 3 | created | NULL | 2026-03-31 00:00:00 | NULL | | 4 | 4 | created | NULL | 2026-03-31 00:00:00 | NULL | | 5 | 5 | created | NULL | 2026-03-31 00:00:00 | NULL | | 6 | 6 | created | NULL | 2026-03-31 00:00:00 | NULL | | 7 | 7 | created | NULL | 2026-03-30 00:00:00 | NULL | | 8 | 8 | created | NULL | 2026-03-30 00:00:00 | NULL | | 9 | 9 | created | NULL | 2026-03-30 00:00:00 | NULL | | 10 | 10 | created | NULL | 2026-03-30 00:00:00 | NULL | | 11 | 11 | created | NULL | 2026-03-30 00:00:00 | NULL | | 12 | 12 | created | NULL | 2026-03-30 00:00:00 | NULL | | 13 | 13 | completed | NULL | 2026-03-30 00:00:00 | NULL | | 14 | 14 | completed | NULL | 2026-03-30 00:00:00 | NULL | | 15 | 15 | completed | NULL | 2026-03-30 00:00:00 | NULL | | 16 | 16 | created | NULL | 2026-03-30 00:00:00 | NULL |
mysql> SELECT * FROM task_reports; +----+---------+-------------+-----------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------------------+ | id | user_id | report_date | tasks_completed | completion_rate | summary | created_at | updated_at | +----+---------+-------------+-----------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------------------+ | 1 | 1 | 2026-03-31 | 1 | 0.17 | {"low": {"done": 0, "pending": 0, "in_progress": 1}, "high": {"done": 1, "pending": 2, "in_progress": 0}, "medium": {"done": 0, "pending": 1, "in_progress": 1}} | 2026-03-31 18:45:16 | 2026-03-31 18:45:16 | | 2 | 1 | 2026-03-30 | 3 | 0.30 | {"low": {"done": 2, "pending": 2, "in_progress": 3}, "high": {"done": 1, "pending": 0, "in_progress": 0}, "medium": {"done": 0, "pending": 1, "in_progress": 1}} | 2026-03-31 18:45:16 | 2026-03-31 18:45:16 | | 3 | 1 | 2026-03-29 | 6 | 0.67 | {"low": {"done": 4, "pending": 0, "in_progress": 1}, "high": {"done": 2, "pending": 2, "in_progress": 0}, "medium": {"done": 0, "pending": 0, "in_progress": 0}} | 2026-03-31 18:45:16 | 2026-03-31 18:45:16 | | 4 | 1 | 2026-03-28 | 3 | 0.50 | {"low": {"done": 1, "pending": 2, "in_progress": 1}, "high": {"done": 2, "pending": 0, "in_progress": 0}, "medium": {"done": 0, "pending": 0, "in_progress": 0}} | 2026-03-31 18:45:16 | 2026-03-31 18:45:16 | | 5 | 1 | 2026-03-27 | 0 | 0.00 | {"low": {"done": 0, "pending": 4, "in_progress": 0}, "high": {"done": 0, "pending": 1, "in_progress": 0}, "medium": {"done": 0, "pending": 1, "in_progress": 1}} | 2026-03-31 18:45:16 | 2026-03-31 18:45:16 | | 6 | 1 | 2026-03-26 | 1 | 0.14 | {"low": {"done": 1, "pending": 1, "in_progress": 2}, "high": {"done": 0, "pending": 1, "in_progress": 0}, "medium": {"done": 0, "pending": 0, "in_progress": 2}} | 2026-03-31 18:45:16 | 2026-03-31 18:45:16 | | 7 | 1 | 2026-03-25 | 2 | 0.25 | {"low": {"done": 1, "pending": 1, "in_progress": 1}, "high": {"done": 1, "pending": 0, "in_progress": 1}, "medium": {"done": 0, "pending": 2, "in_progress": 1}} | 2026-03-31 18:45:16 | 2026-03-31 18:45:16 | | 8 | 1 | 2026-03-24 | 3 | 0.43 | {"low": {"done": 2, "pending": 1, "in_progress": 0}, "high": {"done": 0, "pending": 1, "in_progress": 1}, "medium": {"done": 1, "pending": 0, "in_progress": 1}} | 2026-03-31 18:45:17 | 2026-03-31 18:45:17 | | 9 | 1 | 2026-03-23 | 2 | 0.29 | {"low": {"done": 0, "pending": 0, "in_progress": 1}, "high": {"done": 1, "pending": 2, "in_progress": 0}, "medium": {"done": 1, "pending": 0, "in_progress": 2}} | 2026-03-31 18:45:17 | 2026-03-31 18:45:17 | | 10 | 1 | 2026-03-22 | 2 | 0.33 | {"low": {"done": 1, "pending": 1, "in_progress": 0}, "high": {"done": 1, "pending": 0, "in_progress": 1}, "medium": {"done": 0, "pending": 1, "in_progress": 1}} | 2026-03-31 18:45:17 | 2026-03-31 18:45:17 | | 11 | 1 | 2026-03-21 | 3 | 0.50 | {"low": {"done": 0, "pending": 1, "in_progress": 0}, "high": {"done": 3, "pending": 0, "in_progress": 0}, "medium": {"done": 0, "pending": 0, "in_progress": 2}} | 2026-03-31 18:45:17 | 2026-03-31 18:45:17 | +----+---------+-------------+-----------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+---------------------+ 11 rows in set (0.01 sec)