This implementation introduces explicit DTOs for track and artist recommendations with explanation metadata, replacing the previous implicit any-shaped payloads. The changes make the recommendation API contract strongly typed and provide clients with clear information about why items were recommended.
File: backend/src/recommendations/dto/recommendation-response.dto.ts
Created comprehensive DTOs with Swagger/OpenAPI decorators:
-
RecommendationExplanationDto- Explanation metadata for recommendationssource: Type of recommendation (popular, collaborative, content-based)reason: Human-readable explanationconfidence: Confidence score (0-100)
-
TrackRecommendationDto- Individual track recommendation- All track fields (id, title, audioUrl, coverArtUrl, genre, artistId, artistName, score)
explanation: Embedded explanation metadata
-
ArtistRecommendationDto- Individual artist recommendation- All artist fields (id, artistName, genre, score, trackCount)
explanation: Embedded explanation metadata
-
TrackRecommendationsResponseDto- Wrapper for track recommendations listrecommendations: Array of track recommendationstotal: Count of recommendationsgeneratedAt: ISO timestamp
-
ArtistRecommendationsResponseDto- Wrapper for artist recommendations listrecommendations: Array of artist recommendationstotal: Count of recommendationsgeneratedAt: ISO timestamp
File: backend/src/recommendations/recommendations.service.ts
Key Changes:
- Changed return types from
any[]to typed DTOs - Added
buildExplanation()method to generate explanation metadata based on source type - Added
buildArtistRecommendation()to construct artist recommendations with explanations - Added
buildTrackRecommendationsResponse()to wrap track recommendations - Added
buildArtistRecommendationsResponse()to wrap artist recommendations - Updated
mapTrackRow()to include explanation metadata - All internal methods now return strongly typed DTOs
Explanation Logic:
- Popular: "Trending track popular among all users"
- Collaborative: "Recommended because users with similar taste also enjoyed this track"
- Content-based: "Matches your preferred genres and listening patterns"
- Confidence calculated as
min(round((score / 100) * 100), 100)
File: backend/src/recommendations/recommendations.controller.ts
Key Changes:
- Added explicit return types to all endpoints
- Added
@ApiResponsedecorators with DTO types for Swagger documentation getTrackRecommendations()returnsTrackRecommendationsResponseDtogetArtistRecommendations()returnsArtistRecommendationsResponseDto
File: backend/src/recommendations/recommendation-cache.service.ts
Key Changes:
- Replaced generic
CacheEntrywith typedCacheEntry<T> - Separated caches:
trackCacheandartistCachewith proper typing - Updated method signatures to use
TrackRecommendationDto[]andArtistRecommendationDto[] - Maintained backward compatibility with existing cache invalidation logic
File: backend/src/recommendations/recommendations.service.spec.ts
Key Changes:
- Updated all test cases to validate typed response structure
- Added tests for explanation metadata fields
- Validates response shape (recommendations, total, generatedAt)
- Tests for each recommendation source type (popular, collaborative, content-based)
- Added cache service mock to test suite
File: backend/src/recommendations/recommendations.controller.spec.ts
New Test Coverage:
- Validates controller returns properly typed responses
- Tests response shape matches DTO contract
- Validates explanation fields are present and properly typed
- Tests all recommendation source types
- Validates confidence is within valid range (0-100)
- Tests default limit behavior
{
"recommendations": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Midnight Dreams",
"audioUrl": "https://storage.example.com/tracks/audio.mp3",
"coverArtUrl": "https://storage.example.com/covers/image.jpg",
"genre": "Afrobeats",
"artistId": "660e8400-e29b-41d4-a716-446655440001",
"artistName": "John Doe",
"score": 42,
"explanation": {
"source": "collaborative",
"reason": "Recommended because users with similar taste also enjoyed this track",
"confidence": 85
}
}
],
"total": 1,
"generatedAt": "2024-01-15T10:30:00Z"
}{
"recommendations": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"artistName": "Jane Smith",
"genre": "Afrobeats",
"score": 156,
"trackCount": 5,
"explanation": {
"source": "content-based",
"reason": "Matches your preferred genres and listening patterns",
"confidence": 100
}
}
],
"total": 1,
"generatedAt": "2024-01-15T10:30:00Z"
}- Type Safety: Eliminates implicit
anytypes, providing compile-time type checking - API Documentation: Swagger/OpenAPI automatically generates accurate API documentation
- Client Clarity: Clients can understand why recommendations were made
- Future Ranking Work: Explanation metadata provides foundation for ranking improvements
- Maintainability: Explicit contracts make refactoring safer
- Testing: Strongly typed responses are easier to test and validate
The changes maintain the existing scoring logic and recommendation algorithms. The only breaking change is the response structure, which now includes:
- Wrapper objects with
recommendations,total, andgeneratedAtfields explanationobject within each recommendation instead of flatsourcefield
Clients will need to update their response parsing to handle the new structure.
✅ Controller responses use DTOs
✅ Recommendation payloads are strongly typed
✅ Clients can tell why an item was recommended (via explanation metadata)
✅ Unit tests validate response shape and explanation fields
✅ Controller tests validate typed responses
✅ Current scoring logic preserved
✅ Source information exposed (popular, collaborative, content-based)
backend/src/recommendations/recommendations.service.tsbackend/src/recommendations/recommendations.controller.tsbackend/src/recommendations/recommendation-cache.service.tsbackend/src/recommendations/recommendations.service.spec.ts
backend/src/recommendations/dto/recommendation-response.dto.tsbackend/src/recommendations/recommendations.controller.spec.ts
To complete the integration:
- Install dependencies:
npm install(in backend directory) - Run tests:
npm testto validate all changes - Update API documentation: The Swagger docs will automatically reflect the new types
- Update client applications to handle the new response structure
- Consider adding integration tests for the full recommendation flow