Skip to content

Backend Endpoints (Draft)

Ozgur Savascioglu edited this page Apr 29, 2025 · 3 revisions

API Endpoints Documentation - Initial Draft (Under Construction)

HTTP Methods Explained:

  1. POST:

    • Purpose: Used to create a new resource. (Check all necessary fields are filled)
    • Usage: When adding a new recipe or registering a new user, a POST request is sent with the necessary data in the request body.
  2. PUT:

    • Purpose: Used to update an existing resource. (Check deletedOn NULL)
    • Usage: When updating a recipe's information, a PUT request is sent with the updated data.
  3. GET:

    • Purpose: Used to retrieve information from the server. (Check deletedOn NULL)
    • Usage: When fetching recipes or details of a specific recipe, a GET request is sent.
  4. DELETE:

    • Purpose: Used to delete a resource.
    • Usage: When deleting a recipe, a DELETE request is sent to remove the recipe.
    • **We'll never delete an item from our db! We will only set it's deletedOn attribute with currentTime. This is needed because we want to see what was deleted later on. Hence we need to check if deletedOn column is empty whenever we want to update (PUT) obtain (GET) any data from our db!

REST (Representational State Transfer):

  • REST is an architectural style that uses HTTP methods to perform CRUD (Create, Read, Update, Delete) operations on resources (e.g., recipes, users).
  • It is stateless, meaning each request contains all necessary information for processing.
  • Data is usually transferred in JSON format.

JSON (JavaScript Object Notation):

  • JSON is a lightweight data interchange format that is human-readable and machine-parsable.

  • In this API, data is exchanged using JSON format in both request bodies (to send data) and responses (to receive data).

    Example Request for Creating a Recipe with an Image:

{
  "name": "Chocolate Cake",
  "ingredients": [
    {
      "name": "Flour",
      "quantity": 2,
      "unit": "cups"
    },
    {
      "name": "Cocoa powder",
      "quantity": 0.5,
      "unit": "cup"
    },
    {
      "name": "Sugar",
      "quantity": 1.5,
      "unit": "cups"
    }
  ],
  "steps": [
    "Preheat the oven to 350°F.",
    "Mix the dry ingredients in a bowl.",
    "Add the wet ingredients and stir.",
    "Bake for 30 minutes."
  ],
  "prepTime": 15,
  "cookTime": 30,
  "mealType": "Dessert",
  "mediaFiles": [
    {
      "type": "image",
      "fileName": "chocolate_cake.jpg",
      "data": "binary_data_here"  // binary data of the image (base64 encoded)
    }
  ]
}

API Endpoints:

1. Authentication Endpoints:

1.1 POST /register

Registers either a standard user or a dietitian.

  • Request Body:
    {
      "username": "john_doe",             // Must be unique in db
      "email": "[email protected]",        // Must be in a valid email format
      "password": "securePassword",       // One of each: uppercase, lowercase, number; min length 8. Requirement: 2.2.1 
      "user_type": "dietitian",           // or "user"
      "dietitian_info": {
        "verification_document": "<binary>",
      }
    }
    • Note: Here the dietitian_info will be NULL if user type is user. If verification_document is filled, we will assume the PDF is a valid PDF and create the dietitian user.

1.2 PUT /user/{id}/profile (Update Profile)

This endpoint allows the user to update their profile information after registration. This would include details such as profilePhoto, foodAllergies, notificationPreferences, profileVisibility, etc.

  • Request Body:
{
  "profilePhoto": "<binary>",                 // The profile photo as binary data
  "foodAllergies": ["gluten", "peanuts"],    // List of food allergies
  "notificationPreferences": {
    "email_notifications": true,
    "sms_notifications": false
  },   
  "profileVisibility": "public",             // Profile visibility setting
  "typeOfCook": "home cook",                 // The type of cook ("home cook", "experienced cook")
}
  • NOTE: All of these fields will be optional. User might want to only update one of these or many at once. We will check all of them, if not null we will update.

1.3 PUT /dietitian/{id}/profile (Update Dietitian Profile)

This endpoint allows a dietitian to update their specific profile fields, including verificationDocument, professionalInfo, clinicLocation, website, etc.

  • Request Body:
{
  "verification_document": "<binary>",  // Updated verification document as binary data
  "professional_info": {               // Updated professional information
    "field": "Nutrition Specialist",    // Example: Field of expertise
    "license": "123-456"                // Example: Professional license number
  },
  "clinic_location": "456 Healthy Way", // Updated clinic location
  "website": "https://nutritionexpert.com" // Updated website URL
}

1.4 GET /user/{id}/profile

Retrieve user profile information (including dietitian details if applicable).

  • Request Parameters:

    id: The unique ID of the user whose profile is being requested.

  • Response Body:

{
  "username": "john_doe",
  "email": "[email protected]",
  "profilePhoto": "<binary_data>",
  "foodAllergies": ["peanut", "dairy"],
  "notificationPreferences": {
    "email_notifications": true,
    "sms_notifications": false
  },
  "profileVisibility": "public",
  "recipeCount": 10,
  "avgRecipeRating": 4.5,
  "typeOfCook": "home",
  "followedUsers": ["user1", "user2"],
  "followerUsers": ["user3", "user4"],
  "dietitianDetails": {
    "clinicLocation": "123 Wellness Street",
    "website": "https://healthydiet.com",
    "professionalInfo": {
      "field": "Nutrition Specialist",
      "license": "123-456"
    }
  }
}

1.5 POST /login

Logs in an existing user.

  • Request Body:
    {
      "username": "john_doe",
      "password": "password123"
    }
  • Response Body:
{
  "access_token": "eyJhbGciOiJIUzI1...",
  "token_type": "Bearer"
}

1.6 POST /logout

Logs out the current user (invalidate the session or token).

  • Request Body: No request body, we will use header to get auth token and invalidate.
  • Response Body: No response will be returned, check status code. If 200 success, else fail.

1.7 DELETE /user/{id}

Deletes a user (including dietitians, as they are users).

  • Request Parameters:

    • id: The unique ID of the user to be deleted.
  • Request Body: This request doesn't require a body; the user's ID is passed in the URL path.

  • Response Body: None, Frontend will give "Sad to see you go" kind of response if status code is 200, "Failed" kind of if status code != 200.

2. Follow/Unfollow Endpoints:

2.1 POST /user/{id}/follow

Follow another registered user.

  • Headers: Authorization: Bearer <access_token> — used to identify the current authenticated user.
  • Request Body:
    {
      "target_user_id": 1234352
    }
  • Response Body: None, frontend will check status code. Success 200, else != 200.

2.2 POST /user/{id}/unfollow

Unfollow a registered user.

  • Headers:

    Authorization: Bearer <access_token>

  • Request Body:

    {
      "target_user_id": "user_456"
    }
  • Response Body: None, frontend will check status code. Success 200, else != 200.

2.3 GET /user/{id}/following

Retrieve the full profile data of users that the specified user is following.

  • Path Parameter: id: The ID of the user whose "following" list is being requested.

  • Headers: Authorization: Bearer <access_token>

  • Query Parameters (optional): page: Page number for pagination (default: 1) limit: Number of results per page (default: 10)

  • Request Body:

    {
       "page": 1,
       "size": 10
    }

Response Body Example:

{
  "following": [
    {
      "user_id": "user_123",
      "username": "cooklover1",
      "profilePhoto": "<binary-or-null>",
      "typeOfCook": "home cook",
      "recipeCount": 12,
      "avgRecipeRating": 4.5
    },
    {
      "user_id": "user_456",
      "username": "healthychef",
      "profilePhoto": "<binary-or-null>",
      "typeOfCook": "experienced cook",
      "recipeCount": 32,
      "avgRecipeRating": 4.8
    }
  ]
}

2.4 GET /user/{id}/followers

Returns public profile information of users who follow the given user.

  • Path Parameter id: The ID of the user whose followers are being requested.

  • Headers: Authorization: Bearer <access_token>

  • Query Parameters (optional): page: Page number (default: 1) limit: Results per page (default: 10)

  • Request Body:

    {
       "page": 1,
       "size": 10
    }
  • Response Body:

{
  "followers": [
    {
      "user_id": "user_654",
      "username": "healthy_bites",
      "profilePhoto": "<url-or-null>",
      "typeOfCook": "home cook",
      "recipeCount": 9,
      "avgRecipeRating": 3.8

    },
    {
      "user_id": "user_332",
      "username": "meal_master",
      "profilePhoto": null,
      "typeOfCook": "experienced cook",
      "recipeCount": 21,
      "avgRecipeRating": 4.4
    }
  ]
}

2.5 POST /user/{id}/unfollow

Unfollow a registered user.

  • Path Parameter
    id: The ID of the current user who is performing the unfollow action.

  • Headers:
    Authorization: Bearer <access_token> — used to identify the current authenticated user.

  • Request Body:

{
  "target_user_id": "user_456"  // The ID of the user to be unfollowed
}
  • Response Body: None, frontend will check the status code. A success status will be returned with a 200 OK code, else the status will indicate an error (!= 200).

3. Ingredient Endpoints:

3.1 POST /ingredient

Create a new ingredient.

  • Headers: Authorization: Bearer <access_token> — (Admins or authorized users only)
  • Request Body:
{
  "name": "Olive Oil",
  "quantity": 100.0,
  "unit": "ml",
  "category": "Oils",
  "allergens": [],
  "dietaryInfo": ["vegan", "vegetarian"]
}
  • Response Body: No response, check status code.

3.2 PUT /ingredient/{id}

Update an existing ingredient by its ID.

  • Path Parameter: id: The unique ID of the ingredient to update.

  • Headers: Authorization: Bearer <access_token> — (Admins or authorized users only)

  • Request Body (only fields to be updated need to be included):

{
  "name": "Olive Oil",
  "quantity": 250.0,
  "unit": "ml",
  "category": "Oils",
  "allergens": [],
  "dietaryInfo": ["vegan", "vegetarian", "gluten-free"]
}
  • NOTE: Only the necessary filed will be here others will be null.

  • Response Body: No response, check status code.

3.3 GET /ingredient/{id}

Retrieve full details of a specific ingredient by its ID.

  • Path Parameter: id: The unique ID of the ingredient to retrieve.

  • Headers: Authorization: Bearer <access_token>

  • Response Body:

{
  "id": "ingr_001",
  "name": "Almond Flour",
  "quantity": 500.0,
  "unit": "grams",
  "category": "Flours",
  "allergens": ["nuts"],
  "dietaryInfo": ["gluten-free", "vegan"]
}

3.4 GET /ingredient

Retrieve a paginated and optionally filtered list of ingredients.

  • Headers: Authorization: Bearer <access_token>

  • Query Parameters (optional):

    • name: Search by ingredient name (partial or full)
    • category: Filter by category (e.g., "Oils", "Spices")
    • allergen: Filter by allergen (if the given string inside the list) (e.g., "gluten", "nuts")
    • diet: Filter by dietary tag (if the given string inside the list) (e.g., "vegan", "gluten-free")
    • page: Page number (default: 1)
    • limit: Results per page (default: 10)

Response Body:

{
  "ingredients": [
    {
      "id": "ingr_001",
      "name": "Almond Flour",
      "quantity": 500.0,
      "unit": "grams",
      "category": "Flours",
      "allergens": ["nuts"],
      "dietaryInfo": ["gluten-free", "vegan"]
    },
    {
      "id": "ingr_002",
      "name": "Coconut Milk",
      "quantity": 400.0,
      "unit": "ml",
      "category": "Dairy Alternatives",
      "allergens": [],
      "dietaryInfo": ["vegan"]
    }
  ]
}

3.5 DELETE /ingredient/{id}

Delete a specific ingredient by its ID.

  • Path Parameter: id: The ID of the ingredient to be deleted.

  • Headers: Authorization: Bearer <access_token>

  • Response Body: None — frontend will check the status code.

    • Success: HTTP 200 OK
    • Failure: HTTP 4xx/5xx with appropriate error message

4. Recipe Endpoints:

4.1 POST /recipe

Create a new recipe.

  • Headers:
  • Authorization: Bearer <access_token> — used to identify the current authenticated user.
  • Request Body:
{
  "name": "Chocolate Cake",      // Name of the recipe
  "ingredients": [
    {
      "id": 12353,       // Ingredient ID
      "name": "Flour",              // Ingredient name
      "quantity": 2,                // Quantity of the ingredient
      "unit": "cups",               // Unit of measurement
      "category": "Baking",         // Ingredient category
      "allergens": ["gluten"],      // List of allergens
      "dietaryInfo": ["vegan"]      // Dietary info (e.g., vegan, gluten-free)
    },
    {
      "id": 4444,
      "name": "Sugar",
      "quantity": 1,
      "unit": "cup",
      "category": "Sweetener",
      "allergens": [],
      "dietaryInfo": []
    }
  ],
  "steps": [
    "Preheat oven to 350°F.",
    "Mix all dry ingredients together.",
    "Add wet ingredients and stir.",
    "Bake for 45 minutes."
  ],
  "costPerServing": 3.5,          // Cost per serving in USD
  "prepTime": 30,                 // Preparation time in minutes
  "cookTime": 45,                 // Cooking time in minutes
  "mealType": "Breakfast",         // Type of meal (Breakfast, Dinner etc.)
  "mediaFiles": [
    {
      "id": 1234,
      "type": "image"  // Type of media (image, video, etc.)
      "url": "https://example.com/image.jpg",  // URL for the media (image/video), WE DON'T HAVE BINARY DATA, MAYBE UPDATE?
      "caption": String
      "uploadDate": Date
      "uploaderId": 1111
    }
  ],
  "creator": {
    "user_id": 1234,
    "username": "cooklover1",
    "profilePhoto": "<binary-or-null>",
    "typeOfCook": "home cook",
    "recipeCount": 12,
    "avgRecipeRating": 4.5
  }
}
  • Response Body: None, check status code.

4.2 PUT /recipe/{id}:

Update an existing recipe by its ID. All the request and response will be same as in the POST one. All the fields will be optional this time.

4.3 GET /recipes:

Retrieve a list of recipes with optional filters and pagination.

  • Query Parameters (optional):
    • category: Filter by recipe category ("Breakfast", "Lunch", "Dinner").
    • ingredientName: Filter by a specific ingredientName ("sugar", "chocolate").
    • max_time: Filter by maximum preparation time in minutes (e.g., "30").
    • min_time: Filter by minimum preparation time in minutes.
    • sort_by: Sort results by a specific field (e.g., "name", "prep_time").
    • order: Sort order, either "asc" for ascending or "desc" for descending.
    • page: The page number for pagination (defaults to 1).
    • limit: The number of results per page (defaults to 10).
  • Request Body:
{
  "category": "Dessert",               // Filter by recipe category (e.g., "Breakfast", "Lunch", "Dinner")
  "ingredientName": "chocolate",       // Filter by a specific ingredient (e.g., "sugar", "chocolate")
  "max_time": 30,                      // Filter by maximum preparation time in minutes (e.g., 30 minutes)
  "min_time": 10,                      // Filter by minimum preparation time in minutes
  "mealType": "Dessert",               // Filter by type of meal (e.g., "Breakfast", "Lunch", "Dinner")
  "difficultyRatingMin": 3,            // Minimum difficulty rating (1 to 5)
  "difficultyRatingMax": 5,            // Maximum difficulty rating (1 to 5)
  "tasteRatingMin": 4,                 // Minimum taste rating (1 to 5)
  "tasteRatingMax": 5,                 // Maximum taste rating (1 to 5)
  "healthRatingMin": 2,                // Minimum health rating (1 to 5)
  "healthRatingMax": 5,                // Maximum health rating (1 to 5)
  "isApproved": true,                  // Filter by approval status (true/false)
  "isFeatured": true,                  // Filter by featured status (true/false)
  "sort_by": "prepTime",               // Sort by a specific field (e.g., "name", "prep_time", "likes")
  "order": "asc",                      // Sort order: "asc" for ascending, "desc" for descending
  "page": 1,                           // Page number (default: 1)
  "limit": 10                           // Number of results per page (default: 10)
}

4.4 GET /recipes/{id}:

Retrieve a specific recipe by its ID.

  • Request Parameters:

    • id: The unique ID of the recipe to be deleted.
  • Request Body: This request doesn't require a body; the recipe's ID is passed in the URL path.

  • Response Body:

{
  "id": "recipe_12345",                            // Unique identifier for the recipe
  "name": "Chocolate Cake",                         // Name of the recipe
  "ingredients": [                                  // List of ingredients used in the recipe
    {
      "id": "ingredient_1",
      "name": "Flour",
      "quantity": 200,
      "unit": "grams",
      "category": "Dry",
      "allergens": ["gluten"],
      "dietaryInfo": ["vegetarian"]
    },
    {
      "id": "ingredient_2",
      "name": "Sugar",
      "quantity": 100,
      "unit": "grams",
      "category": "Sweetener",
      "allergens": [],
      "dietaryInfo": ["vegetarian"]
    }
  ],
  "steps": [                                       // List of preparation steps for the recipe
    "Preheat the oven to 180°C.",
    "Mix the dry ingredients together.",
    "Add eggs and milk, then stir until smooth.",
    "Bake for 30 minutes or until a toothpick comes out clean."
  ],
  "costPerServing": 2.5,                           // Cost per serving in USD (or currency)
  "prepTime": 20,                                  // Preparation time in minutes
  "cookTime": 30,                                  // Cooking time in minutes
  "difficultyRating": 3.5,                         // Difficulty rating (1-5)
  "tasteRating": 4.7,                              // Taste rating (1-5)
  "healthRating": 3.2,                             // Health rating (1-5)
  "mealType": "Dessert",                           // Type of meal (e.g., "Breakfast", "Lunch", "Dinner", "Dessert")
  "mediaFiles": [                                  // List of media files (images/videos) associated with the recipe
    {
      "id": 1234,
      "type": "image"  // Type of media (image, video, etc.)
      "url": "https://example.com/image.jpg",  // URL for the media (image/video), WE DON'T HAVE BINARY DATA, MAYBE UPDATE?
      "caption": String
      "uploadDate": Date
      "uploaderId": 1111
    }
  ],
  "comments": [                                    // List of comments on the recipe, I THINK WE SHOULDN'T IMPLEMENT THIS
    {
      "comment_id": "comment_1",
      "user_id": "user_456",
      "username": "foodie123",
      "content": "This cake was delicious! Will make it again.",
      "timestamp": "2025-04-25T14:30:00Z"
    }
  ],
  "likes": 150,                                    // Number of likes on the recipe
  "creator": {                                     // Information about the creator of the recipe
    "user_id": "user_789",
    "username": "chef_jane",
    "profilePhoto": "https://example.com/profile.jpg",
    "typeOfCook": "Experienced cook",
    "recipeCount": 25,
    "avgRecipeRating": 4.6
  },
  "isApproved": true,                              // Whether the recipe is approved by admins
  "isFeatured": true                               // Whether the recipe is featured
}

4.5 DELETE /recipe/{id}:

  • Request Parameters:

    • id: The unique ID of the recipe to be deleted.
  • Request Body: This request doesn't require a body; the recipe's ID is passed in the URL path.

  • Response Body: None, Frontend will give "Recipe has been deleted" kind of response if status code is 200, "Failed" kind of if status code != 200.

Questions in my mind:

    1. We had an authContext variable for almost any function when I was working in a backend intern. And we were getting all the token and header related stuff using this authContext. I'm not sure frontend can easily supply this info with us, but I think that it shouldn't be a huge burden for them. If so, we should get authContext FOR ALL OF THE FUNCTIONS of ours and check if the user id is the same as in the bearer token user id. We will probably use JWT for tokenization purposes.
    1. All the classes should have createdAt, updatedAt, deletedOn. I didn't put everywhere. But every item should have it. User, recipe, comment, ingredient and so on.
    1. For media in class diagram we don't have any data in binary but a url. I'm okay wtih holding a url. But where will we store them? We also need AWS s3 for that I guess, so I suggest to hold binary data in base64 format. For the User endpoints I've said directly binary data in string but we can have a media class and have a binary field inside that.
    1. All id's will be int. If you see it in the "user_id" or "recipe_id" or something else, it's not a string. It will be INT!
    1. Recipe commenting can be postponed to 451. We need to add many things (post, put, get, gets, delete) We can simply return NULL for comments in our recipes. I'm not talking about community forum comments.
    1. I couldn't find community forum post and community forum comments structures in class diagrams??
    1. Ingredient class also need price double. For the given size (1L), 15 tl.
    1. Dietitian class will have a user foreign key, and the other mentioned dietitian specific fields. Primary key will be a user id foreign key.
    1. We can implement rate, like, comment on a receipt. But I think that we can't complete all of them. Only these stated endpoints will be problematic enough. I strongly suggest that we don't implement any of the mentioned functionalities. We will have follow, unfollow user at the end, and it's like one of these concepts.
    1. Besides these we can implement community post and comment endpoints as planned before. I left the task of writing these endpoints to you. You can refer to UML Class Diagram for class models.
    1. User must have user type param which will be diet or user. We can use it to call whether dietitian profile update or user profile update.
    1. I guess we should remove cook time from recipe. We can only hold prepTime and act as if it's the total time
    1. I think we should remove media class. We will only hold images not any videos. So we can simple hold profile_photo_data or recipe_photo_data as base64 string.

CEM ANSWERS:

ÖZGÜR ANSWERS:

Thank you for preparing this — really great effort for the team! I truly appreciate the time and energy spent on organizing these details.

⚠️ Note:
No need to implement the following ingredient endpoints, since ingredients will always be fixed. I confirmed this with the teaching assistant during our customer meeting:

  • (3.1) POST /ingredient

  • (3.2) PUT /ingredient/{id}

  • (3.5) DELETE /ingredient/{id}

Questions:

  1. Authentication Context and JWT Tokens
    I think we should use authContext and JWT tokens for authorization. The frontend team must be informed so they can handle token management correctly in both Flutter and React environments.

  2. Timestamps in Database
    Agreed — we should update all models and the database schema to include:

  • created_at
  • updated_at
  • deleted_on (for soft deletes)
  1. Media Storage and Cost Concerns
    Since we are working with a free-tier account, it's best not to risk hitting storage or bandwidth limits. I agree with using the cheapest and simplest solution available. 😊 However, I'm unsure about using base64 format for cost-effectiveness, as storing photos in this format increases the file size by approximately 33%, which could lead to faster consumption of storage and bandwidth.

  2. ID Field Types
    All IDs (e.g., user_id, recipe_id) will be of type INT.
    Our current DB already implements all IDs as auto-incremented integers.

  3. Postponing Recipe Commenting
    Agreed — we can postpone implementing recipe commenting to CmpE 451. 😊

  4. Community Forum Classes in UML
    The Discussion and DiscussionComment classes serve as our current community forum structure. However, we can consider renaming them for clarity.

  5. Ingredient Pricing Design
    Ingredient prices are currently tracked under the Market class, as prices vary by market. The design is structured like this:

class Market {
    ...
    productList: List<Ingredient, double>
    ...
}
  1. Dietitian–User Relationship Agreed. This is already implemented as follows:
FOREIGN KEY (user_id) REFERENCES registered_users(user_id) ON DELETE CASCADE
  1. Scope for CmpE 451
    Agreed. We can focus on additional features such as ratings, likes, and recipe commenting in CmpE 451.

  2. Community Post and Comment Endpoints
    Noted — I’ll work on implementing these endpoints based on the UML class models.

  3. User Type Field
    OK — I’ll update both the register endpoint and database to include the user_type field to distinguish between dietitians and regular users.

  4. Cook Time Field
    OK — we can remove the cook_time field and treat prep_time as the total time.

  5. Simplifying Media Handling
    I think we should keep the class for now and discuss alternative solutions.

👥 Team Members

📌 Milestone Report

💬 Communication Plan

📋 Meeting Agendas

📅 Meeting Notes

📂 Backend Subgroup Meeting Notes

📂 Frontend Subgroup Meeting Notes

📂 Mobile Subgroup Meeting Notes

📚 Lecture Notes

🛠️ Team Best Practices

✍️ Guidance

❗ Issues

🚀 Project

🧱 Diagrams

👩‍💼 User Scenarios

Click to Expand ⬇️

🗂️ Templates

Clone this wiki locally