overview

Ingin Menjadi Backend Dev Handal Namun Enggan Ngoding

December 19, 2025
13 min read
index

Emotion App - API Documentation

Base URL: http://localhost:3000/api Version: 1.0.0 Authentication: Bearer Token (JWT)


Table of Contents

  1. Authentication
  2. Moods
  3. Activities
  4. Entries
  5. Response Format
  6. Error Handling

Authentication

All endpoints except /auth/register and /auth/login require Bearer token authentication.

Header Format:

Authorization: Bearer <your-jwt-token>

POST /auth/register

Register a new user account.

Endpoint: /auth/register Method: POST Auth Required: No

Request Body

{
"email": "user@example.com",
"password": "Password123",
"confirmPassword": "Password123",
"name": "John Doe"
}

Validation Rules

FieldTypeRequiredValidation
emailstring✅ YesValid email format, max 255 chars
passwordstring✅ YesMin 8 chars, max 128 chars, must contain: uppercase, lowercase, number
confirmPasswordstring✅ YesMust match password
namestring❌ NoMin 1 char, max 100 chars

Success Response (201 Created)

{
"status": "success",
"message": "User registered successfully",
"data": {
"userId": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "John Doe",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}

Error Response (400 Bad Request)

{
"status": "failed",
"message": "Validation failed",
"errors": [
{
"field": "email",
"message": "Email already exists",
"value": "user@example.com"
}
]
}

POST /auth/login

Authenticate user and get JWT token.

Endpoint: /auth/login Method: POST Auth Required: No

Request Body

{
"email": "user@example.com",
"password": "Password123"
}

Validation Rules

FieldTypeRequiredValidation
emailstring✅ YesValid email format
passwordstring✅ YesRequired

Success Response (200 OK)

{
"status": "success",
"data": {
"userId": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "John Doe",
"role": "USER",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}

Error Response (401 Unauthorized)

{
"status": "failed",
"message": "Invalid email or password"
}

GET /auth/profile

Get authenticated user’s profile.

Endpoint: /auth/profile Method: GET Auth Required: ✅ Yes

Success Response (200 OK)

{
"status": "success",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"role": "USER",
"createdAt": "2024-12-01T10:00:00.000Z",
"profile": {
"name": "John Doe",
"reminderSettings": {
"enabled": true,
"time": "09:00",
"frequency": "daily"
},
"pushNotificationToken": "ExponentPushToken[...]"
}
}
}

PUT /auth/update-profile

Update user profile information.

Endpoint: /auth/update-profile Method: PUT Auth Required: ✅ Yes

Request Body

{
"email": "newemail@example.com",
"name": "John Smith",
"reminderSettings": {
"enabled": true,
"time": "09:00",
"frequency": "daily",
"days": ["monday", "wednesday", "friday"]
},
"pushNotificationToken": "ExponentPushToken[...]"
}

Validation Rules

FieldTypeRequiredValidation
emailstring❌ NoValid email, max 255 chars
rolestring❌ NoEnum: USER, ADMIN
namestring❌ NoMin 1 char, max 100 chars
reminderSettingsobject❌ NoValid JSON with allowed keys
reminderSettings.timestring❌ NoFormat: HH:MM (24-hour)
reminderSettings.frequencystring❌ NoEnum: daily, weekly, monthly
reminderSettings.daysarray❌ NoDay names (lowercase)
pushNotificationTokenstring❌ NoMin 1 char, max 255 chars

Note: At least one field must be provided.

Success Response (200 OK)

{
"status": "success",
"message": "Profile updated successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "newemail@example.com",
"profile": {
"name": "John Smith",
"reminderSettings": {
"enabled": true,
"time": "09:00",
"frequency": "daily",
"days": ["monday", "wednesday", "friday"]
}
}
}
}

Moods

Manage moods (global defaults + custom user moods).

Mood Rank System: 1-7

  1. Very Sad 😢
  2. Sad 😔
  3. Somewhat Sad 😟
  4. Neutral 😐 (default)
  5. Somewhat Happy 🙂
  6. Happy 😊
  7. Very Happy 😄

GET /moods

Get all available moods (global + user’s custom moods).

Endpoint: /moods Method: GET Auth Required: ✅ Yes

Success Response (200 OK)

{
"status": "success",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": null,
"name": "Very Happy",
"rank": 7,
"icon": "😄",
"createdAt": "2024-12-01T10:00:00.000Z"
},
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"userId": "user-uuid",
"name": "Excited",
"rank": 6,
"icon": "🤩",
"createdAt": "2024-12-10T15:30:00.000Z"
}
],
"count": 2
}

Note: userId: null = global mood, userId: <uuid> = custom mood


POST /moods

Create a custom mood.

Endpoint: /moods Method: POST Auth Required: ✅ Yes

Request Body

{
"name": "Excited",
"rank": 6,
"icon": "🤩"
}

Validation Rules

FieldTypeRequiredValidation
namestring✅ YesMin 1 char, max 50 chars, unique per user
rankinteger❌ No1-7, defaults to 4
iconstring❌ NoMax 100 chars (emoji)

Success Response (201 Created)

{
"status": "success",
"message": "Custom mood created successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"userId": "user-uuid",
"name": "Excited",
"rank": 6,
"icon": "🤩",
"createdAt": "2024-12-19T10:00:00.000Z"
}
}

PUT /moods/:id

Update a custom mood (cannot update global moods).

Endpoint: /moods/:id Method: PUT Auth Required: ✅ Yes

Request Body

{
"name": "Super Excited",
"rank": 7,
"icon": "🎉"
}

Validation Rules

Same as POST, all fields optional.

Success Response (200 OK)

{
"status": "success",
"message": "Mood updated successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Super Excited",
"rank": 7,
"icon": "🎉"
}
}

Error Response (404 Not Found)

{
"status": "failed",
"message": "Custom mood not found or you don't have permission"
}

DELETE /moods/:id

Delete a custom mood (cannot delete if used in entries).

Endpoint: /moods/:id Method: DELETE Auth Required: ✅ Yes

Success Response (200 OK)

{
"status": "success",
"message": "Mood deleted successfully"
}

Error Response (400 Bad Request)

{
"status": "failed",
"message": "Cannot delete mood that is used in entries"
}

Activities

Manage activities (global defaults + custom user activities).

70 Default Activities across 8 categories:

  • Work & Career
  • Health & Fitness
  • Social & Relationships
  • Entertainment & Hobbies
  • Personal Care
  • Relaxation & Leisure
  • Food & Nutrition
  • Learning & Development

GET /activities

Get all activities with optional filtering.

Endpoint: /activities Method: GET Auth Required: ✅ Yes

Query Parameters

ParameterTypeRequiredDescription
archivedstring❌ NoFilter by archived status: “true” or “false”
activityGroupstring❌ NoFilter by group name (e.g., “health”)

Success Response (200 OK)

{
"status": "success",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": null,
"name": "Exercise",
"icon": "🏃",
"activityGroup": "health",
"archived": false,
"createdAt": "2024-12-01T10:00:00.000Z"
},
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"userId": "user-uuid",
"name": "Morning Run",
"icon": "🌅",
"activityGroup": "health",
"archived": false,
"createdAt": "2024-12-15T08:00:00.000Z"
}
],
"count": 2
}

POST /activities

Create a custom activity.

Endpoint: /activities Method: POST Auth Required: ✅ Yes

Request Body

{
"name": "Morning Run",
"icon": "🌅",
"activityGroup": "health"
}

Validation Rules

FieldTypeRequiredValidation
namestring✅ YesMin 1 char, max 100 chars
iconstring❌ NoMax 100 chars (emoji)
activityGroupstring❌ NoMax 100 chars

Success Response (201 Created)

{
"status": "success",
"message": "Custom activity created successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"userId": "user-uuid",
"name": "Morning Run",
"icon": "🌅",
"activityGroup": "health",
"archived": false
}
}

PUT /activities/:id

Update a custom activity.

Endpoint: /activities/:id Method: PUT Auth Required: ✅ Yes

Request Body

{
"name": "Evening Run",
"icon": "🌆"
}

Validation Rules

Same as POST, all fields optional.

Success Response (200 OK)

{
"status": "success",
"message": "Activity updated successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Evening Run",
"icon": "🌆"
}
}

DELETE /activities/:id

Archive a custom activity (soft delete).

Endpoint: /activities/:id Method: DELETE Auth Required: ✅ Yes

Success Response (200 OK)

{
"status": "success",
"message": "Activity archived successfully"
}

Error Response (400 Bad Request)

{
"status": "failed",
"message": "Cannot delete activity that is used in entries"
}

Entries

Manage daily journal entries with mood tracking and activity logging.

GET /entries

Get user’s entries with advanced filtering and pagination.

Endpoint: /entries Method: GET Auth Required: ✅ Yes

Query Parameters

ParameterTypeRequiredDescription
monthinteger❌ NoFilter by month (1-12), requires year
yearinteger❌ NoFilter by year (e.g., 2024), requires month
dateFromstring❌ NoStart date (YYYY-MM-DD)
dateTostring❌ NoEnd date (YYYY-MM-DD)
moodRankinteger❌ NoFilter by mood rank (1-7)
limitinteger❌ NoResults per page (default: 50)
offsetinteger❌ NoSkip N entries (default: 0)
includeActivitiesstring❌ NoPopulate activity details: “true” or “false”

Example Queries

GET /entries?month=12&year=2024
GET /entries?dateFrom=2024-12-01&dateTo=2024-12-31
GET /entries?moodRank=7&limit=10
GET /entries?includeActivities=true

Success Response (200 OK)

Without includeActivities:

{
"status": "success",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": "user-uuid",
"moodId": "mood-uuid",
"entryDate": "2024-12-19T10:00:00.000Z",
"note": "Had a great day at work!",
"activityIds": [
"activity-uuid-1",
"activity-uuid-2"
],
"media": [
{
"type": "image",
"url": "https://example.com/photo.jpg"
}
],
"mood": {
"id": "mood-uuid",
"name": "Very Happy",
"rank": 7,
"icon": "😄"
},
"createdAt": "2024-12-19T10:00:00.000Z",
"updatedAt": "2024-12-19T10:00:00.000Z"
}
],
"count": 1
}

With includeActivities=true:

{
"status": "success",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"activityIds": ["activity-uuid-1", "activity-uuid-2"],
"activities": [
{
"id": "activity-uuid-1",
"name": "Work",
"icon": "💼",
"activityGroup": "work"
},
{
"id": "activity-uuid-2",
"name": "Exercise",
"icon": "🏃",
"activityGroup": "health"
}
],
"mood": {
"id": "mood-uuid",
"name": "Very Happy",
"rank": 7,
"icon": "😄"
},
"note": "Had a great day!",
"entryDate": "2024-12-19T10:00:00.000Z"
}
],
"count": 1
}

GET /entries/:id

Get a single entry by ID.

Endpoint: /entries/:id Method: GET Auth Required: ✅ Yes

Query Parameters

ParameterTypeDescription
includeActivitiesstringPopulate activity details: “true” or “false”

Success Response (200 OK)

Same structure as GET /entries

Error Response (404 Not Found)

{
"status": "failed",
"message": "Entry not found"
}

POST /entries

Create a new journal entry.

Endpoint: /entries Method: POST Auth Required: ✅ Yes

Request Body

{
"moodId": "550e8400-e29b-41d4-a716-446655440000",
"entryDate": "2024-12-19T10:00:00Z",
"note": "Had a great day at work!",
"activityIds": [
"550e8400-e29b-41d4-a716-446655440001",
"550e8400-e29b-41d4-a716-446655440002"
],
"media": [
{
"type": "image",
"url": "https://example.com/photo.jpg"
}
]
}

Validation Rules

FieldTypeRequiredValidation
moodIdstring (UUID)✅ YesMust be valid mood UUID
entryDatestring✅ YesISO 8601 date format
notestring❌ NoMax 1000 chars
activityIdsarray❌ NoArray of valid activity UUIDs
mediaarray❌ NoArray of objects with type and url

Activity IDs Validation:

  • Each element must be a valid UUID format
  • Each UUID must exist in database
  • Cannot use archived activities

Media Validation:

  • Each item must be an object
  • Required properties: type (string), url (string)

Success Response (201 Created)

{
"status": "success",
"message": "Entry created successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"userId": "user-uuid",
"moodId": "mood-uuid",
"entryDate": "2024-12-19T10:00:00.000Z",
"note": "Had a great day at work!",
"activityIds": ["activity-uuid-1", "activity-uuid-2"],
"media": [
{
"type": "image",
"url": "https://example.com/photo.jpg"
}
],
"createdAt": "2024-12-19T10:00:00.000Z"
}
}

Error Response (400 Bad Request)

Invalid Activity IDs:

{
"status": "failed",
"message": "Validation failed",
"errors": [
{
"field": "activityIds",
"message": "All activity IDs must be valid UUIDs. Invalid: not-a-uuid, 123",
"value": ["not-a-uuid", "123"]
}
]
}

Media Structure Error:

{
"status": "failed",
"message": "Validation failed",
"errors": [
{
"field": "media",
"message": "Each media item must be an object with \"type\" and \"url\" properties"
}
]
}

PUT /entries/:id

Update an existing entry.

Endpoint: /entries/:id Method: PUT Auth Required: ✅ Yes

Request Body

All fields are optional (partial update):

{
"moodId": "mood-uuid-new",
"note": "Updated note text",
"activityIds": ["activity-uuid-3"],
"media": []
}

Validation Rules

Same as POST, all fields optional.

Success Response (200 OK)

{
"status": "success",
"message": "Entry updated successfully",
"data": {
"id": "entry-uuid",
"moodId": "mood-uuid-new",
"note": "Updated note text",
"activityIds": ["activity-uuid-3"],
"updatedAt": "2024-12-19T11:00:00.000Z"
}
}

DELETE /entries/:id

Delete an entry (hard delete).

Endpoint: /entries/:id Method: DELETE Auth Required: ✅ Yes

Success Response (200 OK)

{
"status": "success",
"message": "Entry deleted successfully"
}

Error Response (404 Not Found)

{
"status": "failed",
"message": "Entry not found"
}

Response Format

All API responses follow a consistent structure:

Success Response

{
"status": "success",
"message": "Optional success message",
"data": {}, // or []
"count": 10 // For array responses
}

Error Response

{
"status": "failed",
"message": "Error description",
"errors": [] // Optional, for validation errors
}

Error Handling

HTTP Status Codes

CodeMeaningWhen Used
200OKSuccessful GET, PUT, DELETE
201CreatedSuccessful POST (resource created)
400Bad RequestValidation errors, invalid data
401UnauthorizedMissing/invalid token, wrong credentials
403ForbiddenValid token but no permission
404Not FoundResource doesn’t exist
500Internal Server ErrorServer-side errors

Common Error Types

1. Validation Errors (400)

{
"status": "failed",
"message": "Validation failed",
"errors": [
{
"field": "email",
"message": "Please provide a valid email",
"value": "invalid-email"
},
{
"field": "password",
"message": "Password must be between 8-128 characters long",
"value": "short"
}
]
}

2. Authentication Errors (401)

{
"status": "failed",
"message": "Invalid token"
}
{
"status": "failed",
"message": "Token has expired"
}

3. Database Errors (400/500)

Unique Constraint:

{
"status": "failed",
"message": "email already exists"
}

Record Not Found:

{
"status": "failed",
"message": "Record not found"
}

4. Unexpected Fields (400)

{
"status": "failed",
"message": "Validation failed",
"errors": [
{
"message": "Unexpected fields: archived, userId. Only name, icon, and activityGroup are allowed."
}
]
}

Notes

  1. Authentication Token:

    • Expires after 2 hours
    • Include in Authorization header: Bearer <token>
    • Obtain from /auth/login or /auth/register
  2. Date Formats:

    • Requests: ISO 8601 (e.g., 2024-12-19T10:00:00Z)
    • Responses: ISO 8601 with timezone
  3. UUIDs:

    • All IDs are UUID v4 format
    • Example: 550e8400-e29b-41d4-a716-446655440000
  4. Pagination:

    • Default limit: 50
    • Maximum limit: 100
    • Use limit and offset for pagination
  5. Global vs Custom Resources:

    • Global: userId = null (available to all)
    • Custom: userId = <user-uuid> (user-specific)
    • Users cannot modify/delete global resources
  6. Soft Delete:

    • Activities: Soft deleted (archived = true)
    • Entries: Hard deleted
  7. Activity Relationship:

    • Entries store activity IDs as array
    • Use ?includeActivities=true to get full details
    • Prevents deletion of used activities

Discussion