OpenMusic is an API built using Express.js and MongoDB, originally inspired by the Dicoding: Belajar Fundamental Aplikasi Back-End course, which used Hapi.js and PostgreSQL. The API is now reimagined and improved with Express.js and MongoDB, using Mongoose as the ODM.
The main purpose of this project is to enhance understanding and learning of backend development using Express.js and MongoDB. The API allows you to manage songs, albums, playlists, and collaborations. In the future, it will serve as the backend for a website, so you are welcome to fork, clone, or contribute to this repository!
- Node.js
- Express.js
- MongoDB
- Mongoose
- nanoid
- bottlejs
- bcrypt
- jwt
- dotenv
The project follows the Clean Architecture principles, providing a clear separation between different layers of the application. The directory structure is organized as follows:
openmusic_express/
+---.env
+---.eslintrc.json
+---.gitignore
+---app.js
+---container.js
+---README.md
+---server.js
+---Applications
¦ +---usecases
¦ ¦ +---albums
¦ ¦ +---authentications
¦ ¦ +---playlists
¦ ¦ +---songs
¦ ¦ +---users
¦ +---validations
+---Domains
¦ +---Entities
¦ ¦
¦ +---Validations
+---Exceptions
¦
+---Frameworks
¦ +---express
¦ ¦ +---routes
¦ ¦
¦ +---mongoose
¦ +---connector
¦ +---models
¦
+---Infrastructures
¦ +---controllers
¦ +---repositories
¦ +---services
¦ +---validation
+---Interfaces
¦ +---contracts
¦ ¦
¦ +---repositories
¦ +---services
¦
+---Middleware
¦
+---Utils
The API provides the following endpoints:
-
/songs
: Perform CRUD operations for songs.*-
GET
└──
/
: Get all songs response (200):{ "status": String, "data": { "songs": [ { "id": String, "title": String, "year": Number, "performer": String, "genre": String, "duration": Number, }, { "id": String, "title": String, "year": Number, "performer": String, "genre": String, "duration": Number, }, ], }, }
└──
/:id
: Get song by id response (200):{ "status": String, "data": { "song": { "id": String, "title": String, "year": Number, "performer": String, "genre": String, "duration": Number, }, }, }
-
POST
└──
/
: Add new songtitle: String year: Number between 1900 and 3000 performer: String genre: String duration: Number
response (201):
{ "status": String, "data": { "songId": String } }
-
PUT
└──
/:id
: Update songtitle: String year: Number between 1900 and 3000 performer: String genre: String duration: Number
response (200):
{ "status": String }
-
DELETE
└──
/:id
: Delete song response (200):{ "status": String }
-
-
/albums
: Perform CRUD operations for albums.*-
GET
└──
/
: Get all albums response (200):{ "status": String, "data": { "albums": [ { "id": String, "name": String, "year": Number, "cover": String }, ], }, }
└──
/:id
: Get album by id response (200):{ "status": String, "data": { "albums": { "id": String, "name": String, "year": Number, "cover": String }, }, }
-
POST
└──
/
: Add new albumname: String year: Number between 1900 and 3000 cover: String (url)
response (201):
{ "status": String, "data": { "albumId": String } }
-
PUT
└──
/:id
: Update albumname: String year: Number between 1900 and 3000 cover: String (url)
response (200):
{ "status": String }
-
DELETE
└──
/:id
: Delete albumresponse (200):
{ "status": String }
-
-
/users
: Perform CRUD operations for users.-
GET
└──
/:id
: Get user by id response (200):{ "status": String, "data": { "user": [ { "id": String, "username": String, "fullname": String, }, ], }, }
-
POST
└──
/
: Create new userusername: String (unique) fullname: String password: String
response (201):
{ "status": String, "data": { "userId": String } }
-
PUT (coming soon)...
-
DELETE (coming soon)...
-
GET
/check-username/:username
: Perform check username availibilty operationresponse (200):
{ "status": String, "data": { "available": Boolean } }
-
-
/authentications
: Perform login and logout operation.*-
POST
└── Login
username: String password: String
response (201):
{ "status": String, "data": { "refreshToken": String "accessToken": String }a }
-
DELETE
└── Logout
refreshToken: String
response (200):
{ "status": String }
-
PUT
└── Refresh access token
refreshToken: String
response (200):
{ "status": String, "data": { "accessToken": String } }
-
-
/playlists
: Perform CRUD operations for playlists.*-
GET
└──
/:id
: Get playlist by idresponse (200):
{ "status": String, "data": { "playlist": { "id": String, "name": String, "owner": String, "songs": [ { "id": String, "title": String, "year": Number, "performer": String, "genre": String, "duration": Number, }, { "id": String, "title": String, "year": Number, "performer": String, "genre": String, "duration": Number, }, ], } } }
-
POST
└──
/
: Create new playlistname: String
response (201):
{ "status": String, "data": { "playlistId": String } }
-
PATCH^
└──
/:id
: update playlist namename: String
response (200):
{ "status": String, }
└──
/:id/songs
: add songs to playlistsongId: String
response (200):
{ "status": String, }
-
DELETE^
└──
/:id
: delete playlist response (200):{ "status": String, }
└──
/:id/songs
: delete song from playlist response (200):{ "status": String, }
-
-
/collaborations
: (Coming soon)...
*: Authentication required (bearer token authentication)
^: Only the user who originally created the data.
To run this project, you'll need to set up a .env file in the root directory with the following environment variables:
MONGODBHOST
: MongoDB host nameMONGODBNAME
: MongoDB database namePORT
: Server portHOST
: Server host nameACCESS_TOKEN_KEY
: Userequire('crypto').createHash('sha256').update('text').digest('hex')
in a Node.js terminal to generate the access token keyREFRESH_TOKEN_KEY
: Userequire('crypto').createHash('sha256').update('text').digest('hex')
in a Node.js terminal to generate the refresh token keyTOKEN_AGE
: Token expiration time in seconds
Make sure to replace 'text' with your preferred secret text when generating token keys.
Feel free to explore and contribute to the project. If you encounter any issues or have suggestions, don't hesitate to create an issue on this repository.