Mini Spotify is a robust backend API designed to emulate the core functionality of a music streaming service. It serves as a comprehensive catalog management system where administrators can curate content (Artists, Albums, Songs) and users can explore music and manage their own personalized playlists.
This project demonstrates a clean implementation of a layered architecture using .NET 9, focusing on role-based security, data integrity, and performance.
- Catalog Management: Full CRUD operations for Artists, their Details (Bio/Contact), Albums, and Songs.
- Playlist System: Users can create private or public playlists and manage their song collections.
- Role-Based Access Control (RBAC): Strict separation of concerns:
- Guests: View the music catalog.
- Users: Manage playlists and view song details.
- Admins: Curate the database (Add/Edit/Remove Music).
- Security: Implements JWT (JSON Web Token) with Access and Refresh token rotation.
- Resilience: Built-in Rate Limiting to prevent abuse.
- Docker Desktop (Recommended)
- .NET 9.0 SDK
- Postman (For testing)
-
Clone the Repository
git clone https://github.com/Alee053/MiniSpotifyAPI.git cd MiniSpotifyAPI -
Environment Configuration Create a
.envfile in the root directory. You can use the following template:POSTGRES_DB=miniSpotifydb POSTGRES_USER=miniSpotifyuser POSTGRES_PASSWORD=supersecret DATABASE_URL= JWT_KEY=7rHnYyq8iwZ2g7r+OeCZo7L3yS4Ud6ZcM9xT1HnGzOqv3oL/0KIZ6ZSYz6F5vQnFv3uKMe1d2IQoFmg8QvFhFQ== JWT_ISSUER=MiniSpotify JWT_AUDIENCE=MiniSpotifyClient
-
Run with Docker (Easiest Method) This command spins up the API and the PostgreSQL database container.
docker-compose up -d
-
Database Migrations If running locally without Docker for the API, or for the first setup:
dotnet tool install --global dotnet-ef dotnet ef database update
-
Access the API
- Swagger UI:
http://localhost:5037/swagger/index.html - API Root:
http://localhost:5037/api
- Swagger UI:
- Framework: .NET 9 (ASP.NET Core Web API)
- Database: PostgreSQL 16
- ORM: Entity Framework Core 9.0 (Code-First approach)
- Authentication: JWT Bearer (Access + Refresh Tokens)
- Documentation: OpenAPI / Swagger
- Utilities: DotNetEnv, BCrypt.Net
- Containerization: Docker & Docker Compose
The API is protected by a Fixed Window Limiter to ensure stability.
- Policy:
fixed - Limit: 50 requests per 60 seconds.
- Header:
Retry-Afteris provided when the limit is exceeded (HTTP 429).
The system uses a relational model with the following core relationships:
erDiagram
USER ||--o{ PLAYLIST : "1-N"
ARTIST ||--o{ ALBUM : "1-N"
ARTIST ||--|| ARTIST_DETAIL : "1-1"
ALBUM ||--o{ SONG : "1-N"
PLAYLIST o{--o{ SONG : "N-M"
USER {
Guid Id
string Username
string Email
string Role
}
ARTIST {
Guid Id
string Name
string Genre
}
ARTIST_DETAIL {
Guid Id
string Biography
string WebsiteUrl
string ManagerContact
}
ALBUM {
Guid Id
string Title
DateTime ReleaseDate
}
SONG {
Guid Id
string Title
int DurationSeconds
}
PLAYLIST {
Guid Id
string Name
bool IsPublic
}
All protected endpoints require a valid Bearer Token in the Authorization header.
| Role | Permissions |
|---|---|
| Guest | Can view the list of Artists, Albums, and Songs. |
| User | Can create and manage playlists. Can view song details. |
| Admin | Full control. Can create, update, and delete Artists, Albums, and Songs. |
| Method | Endpoint | Description | Access |
|---|---|---|---|
| POST | /api/Auth/register |
Create a new account (User or Admin). | Public |
| POST | /api/Auth/login |
Log in to get Access and Refresh tokens. | Public |
| POST | /api/Auth/refresh |
Refresh an expired Access Token. | Public |
| POST | /api/Auth/logout |
Invalidate the refresh token. | Public |
| Method | Endpoint | Description | Access |
|---|---|---|---|
| GET | /api/Artist |
Get all artists. | Public |
| GET | /api/Artist/{id} |
Get details of a specific artist. | Public |
| POST | /api/Artist |
Create a new artist. | Admin |
| PUT | /api/Artist/{id} |
Update artist information. | Admin |
| DELETE | /api/Artist/{id} |
Delete an artist. | Admin |
| Method | Endpoint | Description | Access |
|---|---|---|---|
| GET | /api/ArtistDetail/{artistId} |
Get bio and contact info for an artist. | Public |
| POST | /api/ArtistDetail |
Create details for an existing artist. | Admin |
| PUT | /api/ArtistDetail/{artistId} |
Update artist details. | Admin |
| Method | Endpoint | Description | Access |
|---|---|---|---|
| GET | /api/Album |
Get all albums. | Public |
| GET | /api/Album/{id} |
Get details of a specific album. | Public |
| POST | /api/Album |
Create a new album for an artist. | Admin |
| PUT | /api/Album/{id} |
Update album information. | Admin |
| DELETE | /api/Album/{id} |
Delete an album. | Admin |
| Method | Endpoint | Description | Access |
|---|---|---|---|
| GET | /api/Song |
Get all songs. | Public |
| GET | /api/Song/{id} |
Get details of a specific song. | Authenticated |
| POST | /api/Song |
Add a song to an album. | Admin |
| PUT | /api/Song/{id} |
Update song information. | Admin |
| DELETE | /api/Song/{id} |
Delete a song. | Admin |
| Method | Endpoint | Description | Access |
|---|---|---|---|
| GET | /api/Playlist |
Get my playlists (and public ones). | Authenticated |
| GET | /api/Playlist/{id} |
Get details of a specific playlist. | Authenticated |
| POST | /api/Playlist |
Create a new playlist. | Authenticated |
| PUT | /api/Playlist/{id} |
Update playlist name or visibility. | Owner |
| DELETE | /api/Playlist/{id} |
Delete a playlist. | Owner |
| POST | /api/Playlist/{id}/songs/{songId} |
Add a song to a playlist. | Owner |
| DELETE | /api/Playlist/{id}/songs/{songId} |
Remove a song from a playlist. | Owner |
POST /api/Auth/register
{
"username": "MusicFan123",
"email": "fan@example.com",
"password": "SecurePassword123!",
"role": "User"
}POST /api/Auth/login
{
"email": "fan@example.com",
"password": "SecurePassword123!"
}POST /api/Artist
{
"name": "The Weeknd",
"genre": "R&B"
}POST /api/Album
{
"title": "After Hours",
"releaseDate": "2020-03-20T00:00:00Z",
"coverUrl": "https://example.com/cover.jpg",
"artistId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}POST /api/Song
{
"title": "Blinding Lights",
"durationSeconds": 200,
"albumId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}POST /api/Playlist
{
"name": "Coding Vibes",
"isPublic": true
}POST /api/Playlist/{playlistId}/songs/{songId}
(No body required)
PUT /api/ArtistDetail/{artistId}
{
"biography": "Global superstar...",
"websiteUrl": "https://theweeknd.com",
"managerContact": "management@xo.com"
}