Files
jukebox/www/api/API_DOCUMENTATION.md
T
2026-06-04 12:44:22 +02:00

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 roles
  • show(User $user): JsonResponse - Get single user with role
  • update(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 relationship
  • show(Artist $artist): JsonResponse - Get artist with label, tracks, genres
  • store(Request $request): JsonResponse
    • Validates: name, cover_path (nullable), release_date (nullable), label_id, duration
    • Returns HTTP 201
  • update(Request $request, Artist $artist): JsonResponse - Update artist
  • destroy(Artist $artist): JsonResponse - Delete artist

AlbumController (app/Http/Controllers/AlbumController.php)

Methods:

  • index(): JsonResponse - List albums with labels
  • show(Album $album): JsonResponse - Get album with label, tracks, artists, genres
  • store(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 album
  • destroy(Album $album): JsonResponse - Delete album

TrackController (app/Http/Controllers/TrackController.php)

Methods:

  • index(): JsonResponse - List tracks with album, artists, genres
  • show(Track $track): JsonResponse - Get track with album.label, artists, genres
  • store(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
  • unlike(Request $request, Track $track): JsonResponse
    • Uses detach() to remove like
  • 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 genres
  • show(Genre $genre): JsonResponse - Get single genre
  • store(Request $request): JsonResponse - Create genre (validates: name, max 100 chars)
  • update(Request $request, Genre $genre): JsonResponse - Update genre
  • destroy(Genre $genre): JsonResponse - Delete genre

LabelController (app/Http/Controllers/LabelController.php)

Methods:

  • index(): JsonResponse - List all labels
  • show(Label $label): JsonResponse - Get single label
  • store(Request $request): JsonResponse - Create label (validates: name, max 100 chars)
  • update(Request $request, Label $label): JsonResponse - Update label
  • destroy(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 role
  • likes(): 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 label
  • tracks(): 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 label
  • tracks(): 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 track
  • artists(): BelongsToMany(Artist, 'artist_track') - Contributing artists
  • genres(): BelongsToMany(Genre, 'track_genre') - Track genres
  • likedBy(): 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 label
  • albums(): 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.php file
  • 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() and with() 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

  1. Clean Role-Based Architecture - Admin/User separation via middleware
  2. Proper Eloquent Relationships - All pivot tables correctly implemented
  3. Token-Based Auth - Sanctum for stateless API authentication
  4. Comprehensive Data Model - Well-structured music catalog schema

Areas for Enhancement

  1. No Resources/Transformers - Consider Laravel API Resources for consistent responses
  2. No CORS Config - Explicit CORS setup recommended for production
  3. Limited Factories - Only UserFactory; others could enable better testing
  4. No API Documentation - Consider OpenAPI/Swagger docs
  5. No Request DTOs - Could use Form Requests for centralized validation
  6. No Response Standardization - No consistent error/success response format
  7. No Pagination - List endpoints return all results (performance concern)
  8. No Query Filtering - No search/filter capabilities on GET endpoints
  9. No Rate Limiting - No API rate limiting configured
  10. Minimal Seeding - Only test user; could seed sample data

Security Considerations

  1. Password hashing properly handled via Laravel
  2. CSRF protection configured (though API uses tokens)
  3. Access control via admin middleware is solid
  4. Foreign key constraints prevent orphaned records
  5. 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 tests
  • tests/Unit/ - Unit tests
  • tests/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.