asteroid/docs/API-REFACTORING-2025-10-08.org

7.6 KiB

API Endpoint Refactoring - 2025-10-08

Overview

Successfully refactored all API endpoints to use Radiance's built-in define-api macro following the framework's best practices and conventions.

What Changed

  • All API endpoints now use define-api (15 endpoints converted)
  • Web pages still use define-page (correct usage for rendering HTML)
  • Static file serving still uses define-page (correct usage)

Separation of Concerns

  • define-api → Used for all JSON API endpoints (data access)
  • define-page → Used for HTML page rendering and static file serving

Radiance's define-api Pattern

Radiance's define-api signature:

(define-api name (lambda-list) (options) body...)

Key features:

  • API endpoints are accessed at /api/<name> automatically
  • Parameters are specified in lambda-list (required and optional)
  • Uses api-output function for responses
  • Supports browser detection via (post/get "browser") for dual user/API usage
  • Automatic JSON serialization based on configured API format

Changes Made

Converted Endpoints to Radiance's define-api

All API endpoints have been refactored from define-page with manual JSON handling to proper define-api definitions.

Admin API Endpoints

  • asteroid/admin/scan-library/api/asteroid/admin/scan-library
  • asteroid/admin/tracks/api/asteroid/admin/tracks

Playlist API Endpoints

  • asteroid/playlists/api/asteroid/playlists
  • asteroid/playlists/create (params: name, description) → /api/asteroid/playlists/create
  • asteroid/playlists/add-track (params: playlist-id, track-id) → /api/asteroid/playlists/add-track
  • asteroid/playlists/get (param: playlist-id) → /api/asteroid/playlists/get

Track API Endpoints

  • asteroid/tracks/api/asteroid/tracks

Player Control API Endpoints

  • asteroid/player/play (param: track-id) → /api/asteroid/player/play
  • asteroid/player/pause/api/asteroid/player/pause
  • asteroid/player/stop/api/asteroid/player/stop
  • asteroid/player/resume/api/asteroid/player/resume
  • asteroid/player/status/api/asteroid/player/status

Status API Endpoints

  • asteroid/auth-status/api/asteroid/auth-status
  • asteroid/status/api/asteroid/status
  • asteroid/icecast-status/api/asteroid/icecast-status

Pages Still Using define-page (Correct Usage)

These continue to use define-page because they render HTML pages or serve files:

HTML Pages

  • front-page - Main landing page (/)
  • admin - Admin dashboard (/admin)
  • users-management - User management page (/admin/user)
  • user-profile - User profile page (/profile)
  • register - Registration page (/register)
  • player - Web player page (/player)

File Serving

  • static - Static file serving (/static/*)
  • stream-track - Audio file streaming (/tracks/*/stream)

Helper Functions

  • api-get-track-by-id - Internal track lookup (not a public endpoint)

Benefits

  1. Framework Compliance: Now following Radiance's recommended patterns and best practices
  2. Automatic Routing: Endpoints automatically available at /api/<name> without manual URI configuration
  3. Parameter Handling: Clean lambda-list based parameter extraction instead of manual post-var calls
  4. Dual Usage: Built-in support for both programmatic API access and browser-based usage
  5. Cleaner Code: Uses api-output for consistent response formatting
  6. Better Error Handling: Proper HTTP status codes (404, 500, etc.) for different error conditions
  7. Reduced Boilerplate: No manual JSON encoding or header setting required

Code Examples

Before (using define-page):

(define-page api-pause #@"/api/pause" ()
  "Pause current playback"
  (setf *player-state* :paused)
  (setf (radiance:header "Content-Type") "application/json")
  (cl-json:encode-json-to-string
   `(("status" . "success")
     ("message" . "Playback paused")
     ("player" . ,(get-player-status)))))

After (using Radiance's define-api):

(define-api asteroid/player/pause () ()
  "Pause current playback"
  (setf *player-state* :paused)
  (api-output `(("status" . "success")
                ("message" . "Playback paused")
                ("player" . ,(get-player-status)))))

Example with Parameters:

(define-api asteroid/playlists/create (name &optional description) ()
  "Create a new playlist"
  (require-authentication)
  (handler-case
      (let* ((user (get-current-user))
             (user-id-raw (gethash "_id" user))
             (user-id (if (listp user-id-raw) (first user-id-raw) user-id-raw)))
        (create-playlist user-id name description)
        (if (string= "true" (post/get "browser"))
            (redirect "/asteroid/")
            (api-output `(("status" . "success")
                          ("message" . "Playlist created successfully")))))
    (error (e)
      (api-output `(("status" . "error")
                    ("message" . ,(format nil "Error: ~a" e)))
                  :status 500))))

Testing Recommendations

API Endpoint URLs Changed

All API endpoints now use the new URL structure. Update any frontend code or tests:

Old format: /api/tracks New format: /api/asteroid/tracks

Testing Checklist

  1. Test all API endpoints with new URLs to ensure proper JSON responses
  2. Verify parameter passing works correctly (GET/POST parameters)
  3. Test browser detection: Add browser=true parameter to test redirect behavior
  4. Verify error handling returns proper HTTP status codes (404, 500, etc.)
  5. Check that authentication/authorization still works on protected endpoints
  6. Test endpoints both programmatically and via browser

Example Test Commands

# Test auth status
curl http://localhost:8080/api/asteroid/auth-status

# Test with parameters
curl -X POST http://localhost:8080/api/asteroid/playlists/create \
  -d "name=MyPlaylist&description=Test"

# Test browser mode (should redirect)
curl -X POST http://localhost:8080/api/asteroid/playlists/create \
  -d "name=MyPlaylist&browser=true"

# Test player control
curl http://localhost:8080/api/asteroid/player/play?track-id=1

Frontend Updates Required

The frontend JavaScript code needs to be updated to use the new API endpoint URLs:

Files to Update

  • static/js/profile.js - Update API calls for playlists
  • static/js/auth-ui.js - Update auth-status endpoint
  • Any other JavaScript files making API calls

Example Changes

// Old
fetch('/api/playlists')

// New
fetch('/api/asteroid/playlists')

Migration Notes

Breaking Changes

  • All API endpoint URLs have changed from /api/<path> to /api/asteroid/<name>
  • Parameters are now passed via GET/POST variables, not URI patterns
  • The asteroid/playlists/get endpoint now requires playlist-id as a parameter instead of URI pattern

Backward Compatibility

Consider adding route redirects for old API URLs during transition period:

(define-route old-api-redirect "/api/tracks" ()
  (redirect "/api/asteroid/tracks"))

Next Steps

  1. Update Frontend Code: Modify all JavaScript files to use new API URLs
  2. Test Thoroughly: Run through all user workflows to ensure APIs work correctly
  3. Update Documentation: Update any API documentation for external consumers
  4. Monitor Logs: Watch for any 404 errors indicating missed endpoint updates
  5. Consider JSON Format: May want to configure Radiance to use JSON API format instead of default S-expressions