#+TITLE: API Endpoint Refactoring - 2025-10-08 #+AUTHOR: Cascade AI Assistant #+DATE: 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: #+BEGIN_SRC lisp (define-api name (lambda-list) (options) body...) #+END_SRC Key features: - API endpoints are accessed at =/api/= 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/= 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=): #+BEGIN_SRC lisp (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))))) #+END_SRC ** After (using Radiance's =define-api=): #+BEGIN_SRC lisp (define-api asteroid/player/pause () () "Pause current playback" (setf *player-state* :paused) (api-output `(("status" . "success") ("message" . "Playback paused") ("player" . ,(get-player-status))))) #+END_SRC ** Example with Parameters: #+BEGIN_SRC lisp (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)))) #+END_SRC * 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 #+BEGIN_SRC bash # 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 #+END_SRC * 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 #+BEGIN_SRC javascript // Old fetch('/api/playlists') // New fetch('/api/asteroid/playlists') #+END_SRC * Migration Notes ** Breaking Changes - All API endpoint URLs have changed from =/api/= to =/api/asteroid/= - 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: #+BEGIN_SRC lisp (define-route old-api-redirect "/api/tracks" () (redirect "/api/asteroid/tracks")) #+END_SRC * 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