7.6 KiB
7.6 KiB
API Endpoint Refactoring - 2025-10-08
- Overview
- Changes Made
- Benefits
- Code Examples
- Testing Recommendations
- Frontend Updates Required
- Migration Notes
- Next Steps
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-outputfunction 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-libraryasteroid/admin/tracks→/api/asteroid/admin/tracks
Playlist API Endpoints
asteroid/playlists→/api/asteroid/playlistsasteroid/playlists/create(params: name, description) →/api/asteroid/playlists/createasteroid/playlists/add-track(params: playlist-id, track-id) →/api/asteroid/playlists/add-trackasteroid/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/playasteroid/player/pause→/api/asteroid/player/pauseasteroid/player/stop→/api/asteroid/player/stopasteroid/player/resume→/api/asteroid/player/resumeasteroid/player/status→/api/asteroid/player/status
Status API Endpoints
asteroid/auth-status→/api/asteroid/auth-statusasteroid/status→/api/asteroid/statusasteroid/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
- Framework Compliance: Now following Radiance's recommended patterns and best practices
- Automatic Routing: Endpoints automatically available at
/api/<name>without manual URI configuration - Parameter Handling: Clean lambda-list based parameter extraction instead of manual
post-varcalls - Dual Usage: Built-in support for both programmatic API access and browser-based usage
- Cleaner Code: Uses
api-outputfor consistent response formatting - Better Error Handling: Proper HTTP status codes (404, 500, etc.) for different error conditions
- 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
- Test all API endpoints with new URLs to ensure proper JSON responses
- Verify parameter passing works correctly (GET/POST parameters)
- Test browser detection: Add
browser=trueparameter to test redirect behavior - Verify error handling returns proper HTTP status codes (404, 500, etc.)
- Check that authentication/authorization still works on protected endpoints
- 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 playlistsstatic/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/getendpoint now requiresplaylist-idas 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
- Update Frontend Code: Modify all JavaScript files to use new API URLs
- Test Thoroughly: Run through all user workflows to ensure APIs work correctly
- Update Documentation: Update any API documentation for external consumers
- Monitor Logs: Watch for any 404 errors indicating missed endpoint updates
- Consider JSON Format: May want to configure Radiance to use JSON API format instead of default S-expressions