16 KiB
Laravel Jukebox API - Complete Documentation
Project Overview
- Framework: Laravel 13.0
- PHP Version: ^8.3
- Database: MySQL (configured via Docker at 172.17.0.1:3306)
- Authentication: Laravel Sanctum 4.0 (Token-based API authentication)
- Purpose: Music streaming/management API with role-based access control
1. API ROUTES
Location: /routes/api.php
Public Routes (No Authentication Required)
POST /register - User registration
POST /login - User login
Authenticated Routes (Requires Sanctum token)
Auth Endpoints:
POST /logout - Logout user
GET /me - Get current user info
User Likes (Authenticated):
GET /me/likes - Get user's liked tracks
POST /tracks/{track}/like - Like a track
DELETE /tracks/{track}/like - Unlike a track
Browse Routes (Read-only for authenticated users):
GET /labels - List all labels
GET /labels/{label} - Get label details
GET /genres - List all genres
GET /genres/{genre} - Get genre details
GET /artists - List all artists
GET /artists/{artist} - Get artist details
GET /albums - List all albums
GET /albums/{album} - Get album details
GET /tracks - List all tracks
GET /tracks/{track} - Get track details
Admin-Only Routes (Requires 'admin' role):
POST /labels - Create label
PUT /labels/{label} - Update label
DELETE /labels/{label} - Delete label
POST /genres - Create genre
PUT /genres/{genre} - Update genre
DELETE /genres/{genre} - Delete genre
POST /artists - Create artist
PUT /artists/{artist} - Update artist
DELETE /artists/{artist} - Delete artist
POST /albums - Create album
PUT /albums/{album} - Update album
DELETE /albums/{album} - Delete album
POST /tracks - Create track
PUT /tracks/{track} - Update track
DELETE /tracks/{track} - Delete track
GET /users - List all users
GET /users/{user} - Get user details
PUT /users/{user} - Update user
DELETE /users/{user} - Delete user
Web Routes (routes/web.php)
- Minimal: Single
GET /returning welcome view (for blade/HTML views)
2. CONTROLLERS & METHODS
AuthController (app/Http/Controllers/AuthController.php)
Methods:
-
register(Request $request): JsonResponse- Handles user registration- Validates: name, email, password (min 8 chars, confirmed)
- Creates user with default 'user' role
- Returns token + user object
- HTTP 201
-
login(Request $request): JsonResponse- Handles user login- Validates: email, password
- Returns token + user object if credentials valid
- Throws ValidationException if invalid
-
logout(Request $request): JsonResponse- Deletes current access token- Returns success message
-
me(Request $request): JsonResponse- Returns current authenticated user- Loads user role relationship
UserController (app/Http/Controllers/UserController.php)
Methods:
index(): JsonResponse- List all users with rolesshow(User $user): JsonResponse- Get single user with roleupdate(Request $request, User $user): JsonResponse- Validates: name, email (unique), password, role_id
- Returns updated user with role
destroy(User $user): JsonResponse- Delete user- Returns HTTP 204
ArtistController (app/Http/Controllers/ArtistController.php)
Methods:
index(): JsonResponse- List artists with label relationshipshow(Artist $artist): JsonResponse- Get artist with label, tracks, genresstore(Request $request): JsonResponse- Validates: name, cover_path (nullable), release_date (nullable), label_id, duration
- Returns HTTP 201
update(Request $request, Artist $artist): JsonResponse- Update artistdestroy(Artist $artist): JsonResponse- Delete artist
AlbumController (app/Http/Controllers/AlbumController.php)
Methods:
index(): JsonResponse- List albums with labelsshow(Album $album): JsonResponse- Get album with label, tracks, artists, genresstore(Request $request): JsonResponse- Validates: title, cover_path, release_date, duration_seconds, type (enum: album|single|ep), label_id
- Returns HTTP 201
update(Request $request, Album $album): JsonResponse- Update albumdestroy(Album $album): JsonResponse- Delete album
TrackController (app/Http/Controllers/TrackController.php)
Methods:
index(): JsonResponse- List tracks with album, artists, genresshow(Track $track): JsonResponse- Get track with album.label, artists, genresstore(Request $request): JsonResponse- Validates: title, file_path, duration_seconds (nullable), album_id (nullable), artist_ids (array), genre_ids (array)
- Syncs artist and genre relationships
- Returns HTTP 201
update(Request $request, Track $track): JsonResponse- Syncs artist and genre relationships
destroy(Track $track): JsonResponse- Delete track
LikeController (app/Http/Controllers/LikeController.php)
Methods:
like(Request $request, Track $track): JsonResponse- Uses
syncWithoutDetaching()to add like without removing existing ones
- Uses
unlike(Request $request, Track $track): JsonResponse- Uses
detach()to remove like
- Uses
index(Request $request): JsonResponse- Returns user's liked tracks with album, artists, genres relationships
GenreController (app/Http/Controllers/GenreController.php)
Methods:
index(): JsonResponse- List all genresshow(Genre $genre): JsonResponse- Get single genrestore(Request $request): JsonResponse- Create genre (validates: name, max 100 chars)update(Request $request, Genre $genre): JsonResponse- Update genredestroy(Genre $genre): JsonResponse- Delete genre
LabelController (app/Http/Controllers/LabelController.php)
Methods:
index(): JsonResponse- List all labelsshow(Label $label): JsonResponse- Get single labelstore(Request $request): JsonResponse- Create label (validates: name, max 100 chars)update(Request $request, Label $label): JsonResponse- Update labeldestroy(Label $label): JsonResponse- Delete label
3. MODELS & RELATIONSHIPS
User Model
Attributes: id, name, email, password (hashed), role_id, timestamps Fillable: name, email, password, role_id Hidden: password, remember_token Traits: HasApiTokens, HasFactory, Notifiable
Relationships:
role(): BelongsTo(Role) - User's rolelikes(): BelongsToMany(Track, 'likes') - Tracks user has liked
Role Model
Attributes: id, name, timestamps Fillable: name
Relationships:
users(): HasMany(User) - Users with this role
Artist Model
Attributes: id, name, cover_path, release_date, label_id, duration, timestamps Fillable: name, cover_path, release_date, label_id, duration
Relationships:
label(): BelongsTo(Label) - Artist's labeltracks(): BelongsToMany(Track, 'artist_track') - All tracks by artist
Album Model
Attributes: id, title, cover_path, release_date, duration_seconds, type (enum), label_id, timestamps Fillable: title, cover_path, release_date, duration_seconds, type, label_id
Relationships:
label(): BelongsTo(Label) - Album's labeltracks(): HasMany(Track) - All tracks in album
Track Model
Attributes: id, title, file_path, duration_seconds, album_id, timestamps Fillable: title, file_path, duration_seconds, album_id
Relationships:
album(): BelongsTo(Album) - Album containing trackartists(): BelongsToMany(Artist, 'artist_track') - Contributing artistsgenres(): BelongsToMany(Genre, 'track_genre') - Track genreslikedBy(): BelongsToMany(User, 'likes') - Users who liked track
Genre Model
Attributes: id, name, timestamps Fillable: name
Relationships:
tracks(): BelongsToMany(Track, 'track_genre') - Tracks in genre
Label Model
Attributes: id, name, timestamps Fillable: name
Relationships:
artists(): HasMany(Artist) - Artists on labelalbums(): HasMany(Album) - Albums on label
4. DATABASE SCHEMA & MIGRATIONS
Tables Created (in order)
1. roles
- id (PRIMARY)
- name (VARCHAR)
- timestamps
2. genres
- id (PRIMARY)
- name (VARCHAR 100)
- timestamps
3. labels
- id (PRIMARY)
- name (VARCHAR 100)
- timestamps
4. users
- id (PRIMARY)
- name (VARCHAR)
- email (VARCHAR)
- password (VARCHAR)
- role_id (FOREIGN KEY → roles.id)
- timestamps
5. artists
- id (PRIMARY)
- name (VARCHAR 255)
- cover_path (VARCHAR 255, nullable)
- release_date (DATETIME, nullable)
- label_id (FOREIGN KEY → labels.id, nullable)
- duration (INTEGER, nullable)
- timestamps
6. albums
- id (PRIMARY)
- title (VARCHAR 255)
- cover_path (VARCHAR 255, nullable)
- release_date (DATETIME, nullable)
- duration_seconds (INTEGER, nullable)
- type (ENUM: 'album', 'single', 'ep', default: 'album')
- label_id (FOREIGN KEY → labels.id, nullable)
- timestamps
7. tracks
- id (PRIMARY)
- title (VARCHAR 255)
- file_path (VARCHAR 255)
- duration_seconds (INTEGER, nullable)
- album_id (FOREIGN KEY → albums.id, nullable)
- timestamps
8. artist_track (pivot table)
- artist_id (FOREIGN KEY → artists.id)
- track_id (FOREIGN KEY → tracks.id)
- PRIMARY KEY: (artist_id, track_id)
- timestamps
9. track_genre (pivot table)
- track_id (FOREIGN KEY → tracks.id)
- genre_id (FOREIGN KEY → genres.id)
- PRIMARY KEY: (track_id, genre_id)
- timestamps
10. likes (pivot table)
- user_id (FOREIGN KEY → users.id)
- track_id (FOREIGN KEY → tracks.id)
- PRIMARY KEY: (user_id, track_id)
- timestamps
11. personal_access_tokens (Sanctum)
- id (PRIMARY)
- tokenable_id, tokenable_type (polymorphic)
- name (TEXT)
- token (VARCHAR 64, UNIQUE)
- abilities (TEXT, nullable)
- last_used_at (TIMESTAMP, nullable)
- expires_at (TIMESTAMP, nullable, indexed)
- timestamps
5. AUTHENTICATION & AUTHORIZATION
Sanctum Configuration (config/sanctum.php)
Stateful Domains:
- localhost, localhost:3000, 127.0.0.1, 127.0.0.1:8000, ::1
- Plus current application URL
Guard: web (session guard, though API uses bearer tokens)
Expiration: null (tokens don't expire automatically)
Token Prefix: env('SANCTUM_TOKEN_PREFIX', '')
Middleware:
- authenticate_session: Laravel\Sanctum\Http\Middleware\AuthenticateSession
- encrypt_cookies: Illuminate\Cookie\Middleware\EncryptCookies
- validate_csrf_token: Illuminate\Foundation\Http\Middleware\ValidateCsrfToken
Auth Configuration (config/auth.php)
Default Guard: web (session-based)
User Provider: Eloquent (uses App\Models\User)
Password Broker: users (uses users table)
Token Expiry: 60 minutes
Custom Middleware
EnsureAdmin (app/Http/Middleware/EnsureAdmin.php)
- Checks if user's role name is 'admin'
- Returns 403 Forbidden if not admin
- Applied to routes group via 'admin' alias in
bootstrap/app.php
6. SEEDERS & FACTORIES
DatabaseSeeder (database/seeders/DatabaseSeeder.php)
Currently creates a single test user:
Name: Test User
Email: test@example.com
Password: Uses UserFactory default
The seeder is commented to create 10 users but currently just creates 1.
UserFactory (database/factories/UserFactory.php)
Attributes Generated:
- name: fake()->name()
- email: fake()->unique()->safeEmail()
- email_verified_at: now()
- password: Hash::make('password')
- remember_token: Str::random(10)
Methods:
unverified(): Sets email_verified_at to null
Note: No factory exists for other models (Artist, Album, Track, Genre, Label)
7. CORS CONFIGURATION
Status: No explicit CORS configuration found
Implications:
- No
config/cors.phpfile - Sanctum handles CORS implicitly via stateful domains configuration
- Likely relying on Laravel default CORS handling or missing CORS setup
- Frontend on localhost:3000 should work (listed in stateful domains)
Recommendation: May need explicit CORS middleware for production cross-origin requests
8. RESOURCES & TRANSFORMERS
Status: No JSON:API Resources or Transformers found
Current Approach:
- Direct Eloquent model casting to JSON
- Raw
response()->json()returns from controllers - Relationships loaded via
load()andwith()methods - No API resource classes (Laravel Resource classes) implemented
Data Transformation:
- Manual eager loading in controllers
- Example:
$artist->load(['label', 'tracks.album', 'tracks.genres'])
9. ENVIRONMENT CONFIGURATION
.env (Development)
APP_NAME=Laravel
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=172.17.0.1
DB_PORT=3306
DB_DATABASE=jukebox
DB_USERNAME=jukebox_admin
DB_PASSWORD=Super
SESSION_DRIVER=database
CACHE_STORE=database
QUEUE_CONNECTION=database
Key Settings
- Debug mode enabled (local)
- MySQL via Docker (172.17.0.1)
- Database-backed sessions
- Database-backed queue
- Database-backed cache
10. DEPENDENCIES
Core:
- laravel/framework: ^13.0
- laravel/sanctum: ^4.0
- laravel/tinker: ^3.0
Dev:
- fakerphp/faker: ^1.23
- laravel/pail: ^1.2.5
- laravel/pint: ^1.27
- mockery/mockery: ^1.6
- nunomaduro/collision: ^8.6
- phpunit/phpunit: ^12.5.12
11. KEY OBSERVATIONS & NOTES
Strengths
- Clean Role-Based Architecture - Admin/User separation via middleware
- Proper Eloquent Relationships - All pivot tables correctly implemented
- Token-Based Auth - Sanctum for stateless API authentication
- Comprehensive Data Model - Well-structured music catalog schema
Areas for Enhancement
- No Resources/Transformers - Consider Laravel API Resources for consistent responses
- No CORS Config - Explicit CORS setup recommended for production
- Limited Factories - Only UserFactory; others could enable better testing
- No API Documentation - Consider OpenAPI/Swagger docs
- No Request DTOs - Could use Form Requests for centralized validation
- No Response Standardization - No consistent error/success response format
- No Pagination - List endpoints return all results (performance concern)
- No Query Filtering - No search/filter capabilities on GET endpoints
- No Rate Limiting - No API rate limiting configured
- Minimal Seeding - Only test user; could seed sample data
Security Considerations
- Password hashing properly handled via Laravel
- CSRF protection configured (though API uses tokens)
- Access control via admin middleware is solid
- Foreign key constraints prevent orphaned records
- Role-based authorization working correctly
API Response Format
Current format (no wrapper):
{
"id": 1,
"name": "Track Name",
"duration_seconds": 240,
"created_at": "2026-04-23T...",
"updated_at": "2026-04-23T..."
}
Could benefit from standardized wrapper:
{
"success": true,
"data": { ... },
"message": "Success"
}
12. TESTING STRUCTURE
Test Framework: PHPUnit 12.5.12
Test Directories:
tests/Feature/- Feature teststests/Unit/- Unit teststests/TestCase.php- Base test class
Example Tests: ExampleTest.php files (placeholder)
No comprehensive tests currently implemented
Summary
This is a music streaming/library API built with Laravel 13, featuring:
- ✅ User registration & login with Sanctum tokens
- ✅ Role-based access control (admin/user)
- ✅ Full CRUD for Artists, Albums, Tracks, Genres, Labels
- ✅ User favorites/likes system
- ✅ Proper database schema with relationships
- ⚠️ Minimal documentation, testing, and optimization
- ⚠️ Missing common API best practices
Ready for development and enhancement with proper testing, documentation, and optimization work.