https://github.com/keidsid/dicoding-back-end-intermediate
Project task from dicoding.com intermediate Back-End class
https://github.com/keidsid/dicoding-back-end-intermediate
hapi-js node-js postgre-sql rabbitmq-producer redis
Last synced: about 1 year ago
JSON representation
Project task from dicoding.com intermediate Back-End class
- Host: GitHub
- URL: https://github.com/keidsid/dicoding-back-end-intermediate
- Owner: KeidsID
- License: mit
- Created: 2023-01-22T15:26:58.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2023-02-27T07:25:56.000Z (about 3 years ago)
- Last Synced: 2025-01-31T22:09:07.708Z (about 1 year ago)
- Topics: hapi-js, node-js, postgre-sql, rabbitmq-producer, redis
- Language: JavaScript
- Homepage:
- Size: 971 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[class-link]: https://www.dicoding.com/academies/271
[pm-v1]: https://github.com/dicodingacademy/a271-backend-menengah-labs/raw/099-shared-files/03-submission-content/01-open-music-api-v1/OpenMusic%20API%20V1%20Test.zip
[pm-v2]: https://github.com/dicodingacademy/a271-backend-menengah-labs/raw/099-shared-files/03-submission-content/02-open-music-api-v2/OpenMusic%20API%20V2%20Test.zip
[pm-v3]: https://github.com/dicodingacademy/a271-backend-menengah-labs/raw/099-shared-files/03-submission-content/03-open-music-api-v3/OpenMusic%20API%20V3%20Test.zip
[my-consumer]: https://github.com/KeidsID/dicoding-back-end-intermediate-mq-consumer
# dicoding-back-end-intermediate
Project task from [dicoding.com Back-End Intermediate Class][class-link].
This project is a learning outcome through the platform dicoding.com. The aim is to enable students to create a RestfulAPI rich in features, including data validations, database relations, auth with JWT Token, MQ, File Storage, and Server-Side Cache.
Link to MQ Consumer: [dicoding-back-end-intermediate-mq-consumer][my-consumer]
### **Postman collections and envs for testing this project:**
- [API.v1 test zip][pm-v1].
- [API.v2 test zip][pm-v2].
- [API.v3 test zip][pm-v3].
### **List of README.md Contents:**
- [Project Set Up](#project-set-up)
- [TO DO API.v1](#to-do-apiv1)
- [TO DO API.v1 Details](#to-do-apiv1-details)
- [TO DO API.v2](#to-do-apiv2)
- [TO DO API.v2 Details](#to-do-apiv2-details)
- [TO DO API.v3](#to-do-apiv3)
- [TO DO API.v3 Details](#to-do-apiv3-details)
# Project Set Up
- Create the "**.env**" file with the following data below:
```sh
# Server config
HOST=localhost
PORT=
# node-postgres config
PGUSER=
PGHOST=
PGPASSWORD=
PGDATABASE=
PGPORT=
# JWT config
ACCESS_TOKEN_KEY=
REFRESH_TOKEN_KEY=
ACCESS_TOKEN_AGE=
# RabbitMQ config
RABBITMQ_SERVER=
# Redis config
REDIS_SERVER=
```
- Then run this command
```
npm install
npm run pgm up
```
**Note**:
If you do TRUNCATE tables, make sure to re-add default album data (id: album-unknown) into "**albums**" table.
```sql
INSERT INTO albums
VALUES('album-unknown', 'Unknown', 1945)
```
# TO DO API.v1
[See TO DO API.v1 Details](#to-do-apiv1-details).
## Mandatory Tasks
- [x] Albums endpoint.
- [x] Songs endpoint.
- [x] Data validation.
- [x] Error handling.
- [x] Using Database.
## Optional Tasks
- [x] "/albums/{id}" endpoint response array of Song on Album too.
- [x] Query params for songs endpoint.
# TO DO API.v2
[See TO DO API.v2 Details](#to-do-apiv2-details).
## Mandatory Tasks
- [x] Registration and Authentication Users.
- [x] Playlist endpoint.
- [x] Implement Foreign Key on Database Tables.
- [x] Data validation for new endpoints.
- [x] Error handling for new endpoints.
- [x] Keep features from API.v1.
## Optional Tasks
- [x] Collaborations on Playlists Feature.
- [x] Activities endpoint for Playlist Log History.
- [x] Keep optional features from API.v1.
# TO DO API.v3
[See TO DO API.v3 Details](#to-do-apiv3-details).
## Mandatory Tasks
- [x] Export playlist feature. MQ Consumer: [dicoding-back-end-intermediate-mq-consumer][my-consumer]
- [x] Upload album cover feature.
- [x] Like and Unlike albums feature.
- [x] Server-Side cache.
- [x] Keep features from API.v2.
## Optional Tasks
No optional task yay.
# TO DO API.v1 Details
Back to Top |
Optional Tasks |
TO DO API.v2 Details |
TO DO API.v3 Details
## Mandatory Tasks
### **1. Albums Endpoint**

*any: Any string, but not null.
Album obj structure:
```json
{
"id": "album-",
"name": "lorem ipsum",
"year": 2012
}
```
### **2. Songs Endpoint**

*any: Any string, but not null.
*?: Can be null or undefined.
Song obj structures:
- Main structure.
```json
{
"id": "song-",
"title": "Lorem Ipsum",
"year": 2008,
"performer": "John Doe",
"genre": "Indie",
"duration": 120,
"albumId": "album-id"
}
```
- Only for GET /songs endpoint.
```json
{
"id": "song-",
"title": "Life in Technicolor",
"performer": "Coldplay"
}
```
### **3. Data Validations**
- POST /albums
- **name**: string, required.
- **year**: number, required.
- PUT /albums
- **name**: string, required.
- **year**: number, required.
- POST /songs
- **title**: string, required.
- **year**: number, required.
- **genre**: string, required.
- **performer**: string, required.
- **duration**: number.
- **albumId**: string.
- PUT /songs
- **title**: string, required.
- **year**: number, required.
- **genre**: string, required.
- **performer**: string, required.
- **duration**: number.
- **albumId**: string.
### **4. Error Handling**
- Validation Error Response:
- status code: **400 (Bad Request)**
- response body:
```json
{
"status": "fail",
"message": ,
}
```
- Not Found Error Response:
- status code: **404 (Not Found)**
- response body:
```json
{
"status": "fail",
"message": ,
}
```
- Server Error Response:
- status code: **500 (Internal Server Error)**
- response body:
```json
{
"status": "error",
"message": ,
}
```
### **5. Using Database**
- Use PostgreSQL to store data. So the data will not be lost if the server is down.
- Use [dotenv](https://www.npmjs.com/package/dotenv) to manage environment variables that store credentials for accessing database.
## Optional Tasks
Back to Top |
Back to Mandatory Tasks |
TO DO API.v3 Details
### **1. "/albums/{id}" endpoint response array of Song on Album too**
Example:
```json
{
"status": "success",
"data": {
"album": {
"id": "album-Mk8AnmCp210PwT6B",
"name": "Viva la Vida",
"year": 2008,
"songs": [
{
"id": "song-Qbax5Oy7L8WKf74l",
"title": "Life in Technicolor",
"performer": "Coldplay"
}
]
}
}
}
```
### **2. Query Params for Songs Endpoint**
Make the **GET /songs** support query params for searching.
- **?title**: Search song based on title.
- **?performer**: Search song based on performer.
**Note**: Both queries can be combined ( ".../songs?title=lmao&performer=pisan" )
# TO DO API.v2 Details
Back to Top |
Optional Tasks |
TO DO API.v1 Details |
TO DO API.v3 Details
## Mandatory Tasks
### **1. Registration and Authentication Users**

*any: Any string, but not null.
**Conditions**:
- **Username** must unique.
- Using JWT token for Auth.
- JWT token payload contains **userId**.
- JWT token secret key value stored on envs as **ACCESS_TOKEN_KEY** and **REFRESH_TOKEN_KEY**.
### **2. Playlist endpoint**

*any: Any string, but not null.
**Conditions**:
- **Restrict** endpoint (Need "access token" to access).
- **GET /playlists** returns owned playlists (And collab playlists if exist).
- Collaborator (if exist) can access **songs** (add, get, and delete) from playlist, but only owners can delete their own playlists.
- Only valid **songId** can be add/delete to/from playlist.
**Responses**:
- GET /playlists
```json
{
"status": "success",
"data": {
"playlists": [
{
"id": "playlist-Qbax5Oy7L8WKf74l",
"name": "Lagu Indie Hits Indonesia",
"username": "dicoding"
},
{
"id": "playlist-lmA4PkM3LseKlkmn",
"name": "Lagu Untuk Membaca",
"username": "dicoding"
}
]
}
}
```
- GET /playlists/{id}/songs
```json
{
"status": "success",
"data": {
"playlist": {
"id": "playlist-Mk8AnmCp210PwT6B",
"name": "My Favorite Coldplay",
"username": "dicoding",
"songs": [
{
"id": "song-Qbax5Oy7L8WKf74l",
"title": "Life in Technicolor",
"performer": "Coldplay"
},
{
"id": "song-poax5Oy7L8WKllqw",
"title": "Centimeteries of London",
"performer": "Coldplay"
},
{
"id": "song-Qalokam7L8WKf74l",
"title": "Lost!",
"performer": "Coldplay"
}
]
}
}
}
```
**Obj Playlist for Database**:
```json
{
"id": "playlist-Qbax5Oy7L8WKf74l",
"name": "Lagu Indie Hits Indonesia",
"owner": "user-Qbax5Oy7L8WKf74l"
}
```
### **3. Implement Foreign Key**
- Table **songs** related to **albums**.
- Table **playlists** related to **users**.
- etc.
### **4. Data Validation**
- POST /users
- **username**: string, required.
- **password**: string, required.
- **fullname**: string, required.
- POST /authentications
- **username**: string, required.
- **password**: string, required.
- PUT /authentications
- **refreshToken**: string, required.
- DELETE /authentications
- **refreshToken**: string, required.
- POST /playlists
- **name**: string, required.
- POST /playlists/{playlistId}/songs
- **songId**: string, required.
### **5. Error Handling**
The previous error handler is still in use, but there is a new handler for Auth.
- Authorization Error:
- status code: **401 (Unauthorized)**
- response body:
```json
{
"status": "fail",
"message":
}
```
- Restrict Error:
- status code: **403 (Forbidden)**
- response body:
```json
{
"status": "fail",
"message":
}
```
### **6. Keep Features from API.v1**
- Albums Feature.
- Songs Feature.
- Validations for Songs and Albums Endpoints.
## Optional Tasks
Back to Top |
Back to Mandatory Tasks |
TO DO API.v1 Details |
TO DO API.v3 Details
### **1. Playlists Collaboration Feature**

*any: Any string, but not null.
**Conditions**:
- Only the owner of the playlist can add or remove collaborators from the playlist.
**Collaborator access rights**:
- Collaborated playlists also shown on "GET /playlists" endpoint data.
- Can add/get/delete songs to/from playlist.
- Can see playlist activities too (If already implemented).
### **2. Activities endpoint for Playlist Log History**
This feature is used to record the history of **adding and removing** **songs from playlists** by users or collaborators.
Endpoint: **GET /playlists/{id}/activities**
Response example:
- Status Code: 200
- Body:
```json
{
"status": "success",
"data": {
"playlistId": "playlist-Mk8AnmCp210PwT6B",
"activities": [
{
"username": "dicoding",
"title": "Life in Technicolor",
"action": "add",
"time": "2021-09-13T08:06:20.600Z"
},
{
"username": "dicoding",
"title": "Centimeteries of London",
"action": "add",
"time": "2021-09-13T08:06:39.852Z"
},
{
"username": "dimasmds",
"title": "Life in Technicolor",
"action": "delete",
"time": "2021-09-13T08:07:01.483Z"
}
]
}
}
```
### **3. Keep optional features from API.v1**
- Songs list from album detail.
- Query param for search songs.
# TO DO API.v3 Details
Back to Top |
TO DO API.v1 Details |
TO DO API.v2 Details
### **1. Export Playlist Feature**
**Server Route**:
- Endpoint: **POST /export/playlists/{id}**
- Body Req (JSON):
```json
{
targetEmail:
}
```
**Server Response**:
- Status code: **201**
- Body Res:
```json
{
"status": "success",
"message":
}
```
**Conditions**:
- Using RabbitMQ.
- The RabbitMQ server host value must be stored in the environment variable as **RABBITMQ_SERVER**.
- Only owner can export his/her own playlists.
- The export result is in JSON format file.
```json
{
"playlist": {
"id": "playlist-Mk8AnmCp210PwT6B",
"name": "My Favorite Coldplay Song",
"songs": [
{
"id": "song-Qbax5Oy7L8WKf74l",
"title": "Life in Technicolor",
"performer": "Coldplay"
},
{
"id": "song-poax5Oy7L8WKllqw",
"title": "Centimeteries of London",
"performer": "Coldplay"
},
{
"id": "song-Qalokam7L8WKf74l",
"title": "Lost!",
"performer": "Coldplay"
}
]
}
}
```
- Also create the consumer source code in a new project, not in this one. With condition below.
- Send the export result through email using [nodemailer](https://nodemailer.com/).
- SMTP credentials value must be stored in the environment variable as **MAIL_ADDRESS** and **MAIL_PASSWORD**.
- SMTP server value must be stored in the environment variable as **MAIL_HOST** and **MAIL_PORT**.
### **2. Upload Album Cover Feature**
**Server Route**:
- Endpoint: **POST /albums/{id}/covers**
- Body Req:
```json
{
"cover":
}
```
**Server Response**:
- Status code: **201**
- Body Res:
```json
{
"status": "success",
"message":
}
```
**Conditions**:
- Only [MIME types of images](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types) are allowed to upload.
- File max size is **512000 Bytes**.
- Local storage or AWS S3 are allowed to use.
- When using AWS S3 Bucket, the bucket name must be stored in the environment variable as **AWS_BUCKET_NAME**.
- **GET /albums/{id}** must response uploaded **coverUrl** too.
```json
{
"status": "success",
"data": {
"album": {
"id": "album-Mk8AnmCp210PwT6B",
"name": "Viva la Vida",
"year": 2018
"coverUrl": "http://...."
}
}
}
```
- coverUrl response an uploaded image.
- If cover not uploaded yet, then response null on coverUrl instead.
- When uploadig cover to an album that already has cover, the old cover are replaced.
### **3. Like and Unlike Albums Feature**

*any: Any string, but not null.
**Conditions**:
- Liking or unliking an album is a strict resource so authentication is required to access it. This aims to find out whether the user has liked the album.
- If the user hasn't liked the album, then the "**POST action /albums/{id}/likes**" is to like the album. If the user has already liked the album, then the action is unliking.
### **4. Server-Side Cache**
- Implement a server-side cache on the number of likes on an album (**GET /albums/{id}/likes**).
- Cache expiration time: 30 minutes.
- The response header for cache result is "**X-Data-Source**" with "cache" value.
- Cache should be deleted every time there is a change in the number of likes on an album.
- Must use Redis (Memurai for Windows) memory caching engine.
- Redis **Host** value must be stored in the environment variable as **REDIS_SERVER**.
### **5. Keep Features from API.v2**
Of course 🙄.
Back to Top |
TO DO API.v1 Details |
TO DO API.v2 Details |
TO DO API.v3 Details