{"id":16972193,"url":"https://github.com/jmcaluyafuentes/flask-recipe-api","last_synced_at":"2026-05-07T06:32:11.258Z","repository":{"id":245071898,"uuid":"815508418","full_name":"jmcaluyafuentes/flask-recipe-api","owner":"jmcaluyafuentes","description":"Term 2 Assignment (Coder Academy, Diploma of IT)","archived":false,"fork":false,"pushed_at":"2024-08-13T13:52:51.000Z","size":5673,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-21T20:30:34.370Z","etag":null,"topics":["authentication","authorization","flask","jwt-token","marshmallow","postgresql","psycopg2","python","restful-api","sqlalchemy"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jmcaluyafuentes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-15T11:18:57.000Z","updated_at":"2024-08-13T13:52:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"2f1a04a4-ef15-47df-b7e0-074e3f731b08","html_url":"https://github.com/jmcaluyafuentes/flask-recipe-api","commit_stats":null,"previous_names":["jmcaluyafuentes/flask-recipe-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jmcaluyafuentes/flask-recipe-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmcaluyafuentes%2Fflask-recipe-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmcaluyafuentes%2Fflask-recipe-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmcaluyafuentes%2Fflask-recipe-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmcaluyafuentes%2Fflask-recipe-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jmcaluyafuentes","download_url":"https://codeload.github.com/jmcaluyafuentes/flask-recipe-api/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmcaluyafuentes%2Fflask-recipe-api/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263826314,"owners_count":23516753,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","authorization","flask","jwt-token","marshmallow","postgresql","psycopg2","python","restful-api","sqlalchemy"],"created_at":"2024-10-14T00:57:57.091Z","updated_at":"2026-05-07T06:32:06.234Z","avatar_url":"https://github.com/jmcaluyafuentes.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# API Web Server\n\nTerm 2, Assignment 2  \nDiploma of IT - Web Development  \nCoder Academy\n\n## Table of Contents\n\n1. [Deployed API](#deployed-api)\n2. [Purpose of API](#purpose)\n3. [Installation and Set Up Instructions](#installation-and-set-up-instructions)\n4. [Requirements](#requirements)  \n    4.1 [Problem statement](#r1-explain-the-problem-that-this-app-will-solve-and-explain-how-this-app-solves-or-addresses-the-problem)  \n    4.2 [Project management](#r2-describe-the-way-tasks-are-allocated-and-tracked-in-your-project)  \n    4.3 [Packages and dependencies](#r3-list-and-explain-the-third-party-services-packages-and-dependencies-used-in-this-app)  \n    4.4 [Database benefits and drawbacks](#r4-explain-the-benefits-and-drawbacks-of-this-apps-underlying-database-system)  \n    4.5 [Functionality of ORM in this app](#r5-explain-the-features-purpose-and-functionalities-of-the-object-relational-mapping-system-orm-used-in-this-app)  \n    4.6 [Entity Relationship Diagram](#r6-design-an-entity-relationship-diagram-erd-for-this-apps-database-and-explain-how-the-relations-between-the-diagrammed-models-will-aid-the-database-design)  \n    4.7 [Models and their relationships](#r7-explain-the-implemented-models-and-their-relationships-including-how-the-relationships-aid-the-database-implementation)  \n    4.8 [Endpoints](#r8-explain-how-to-use-this-applications-api-endpoints-each-endpoint-should-be-explained-including-the-following-data-for-each-endpoint)\n\n\n\n\n\n\n\n\n\n\n## Deployed API\n\nYou can access the deployed API at: [https://flask-recipe-api.onrender.com/](https://flask-recipe-api.onrender.com/)\n\n## Purpose\n\nThis API web server is an assignment for Term 2 in the Coder Academy Diploma of IT. It aims to provide students with practical experience in developing a fully functional API web server using modern web development technologies and best practices. My project involves creating a recipe management system that allows users to manage and discover recipes, leveraging Flask for the web framework and PostgreSQL for the relational database management system.\n\n## Installation and Set Up Instructions\n\n### Prerequisites\n\n1. Windows Subsystem for Linux (WSL)\n\n    Make sure you have WSL installed and set up on your Windows machine.\n\n2. Python and pip\n\n    Python 3.7 or higher is required. Check if Python is installed:\n\n    ```\n    python3 --version\n    ```\n\n    Install Python if not already installed:\n\n    ```\n    sudo apt update  \n    sudo apt install python3 python3-pip\n    ```\n\n3. PostgreSQL\n\n    Install PostgreSQL database server:\n\n    ```\n    sudo apt update\n    sudo apt install postgresql\n    ```\n\n    Start PostgreSQL service:\n\n    ```\n    sudo service postgresql start\n    ```\n\n### Clone Repository\n\n1. Clone the Recipe API repository from GitHub\n\n    ```\n    git clone https://github.com/jmcaluyafuentes/flask-recipe-api\n    cd recipe-api\n    ```\n\n### Set Up Virtual Environment\n\n1. Create a virtual environment\n\n    ```\n    python3 -m venv venv\n    ```\n\n2. Activate the virtual environment\n\n    ```\n    source venv/bin/activate\n    ```\n\n### Install Dependencies\n\n1. Install required Python packages\n\n    ```\n    pip install -r requirements.txt\n    ```\n\n### Set Up Environment Variables\n\n1. Create a .env file in the root directory of the project\n\n    ```\n    touch .env\n    ```\n\n2. Add the following environment variables to .env file\n\n    ```\n    JWT_KEY='your_secret_key'\n    DB_URL='postgresql+psycopg2://username:password@localhost:5432/databasename\n    # Replace `username`, `password`, and `databasename` with your\n      PostgreSQL credentials and database name\n    ```\n\n### Database Setup\n\n1. Create the database and populate initial data\n\n    ```\n    flask db create\n    ```\n\n### Run the Application\n\n1. Start the Flask development server\n\n    ```\n    flask run\n    ```\n\n2. Access the API in your browser at http://localhost:5000.\n\n[Back to Top](#)\n\n## Requirements\n\n### R1: Explain the problem that this app will solve and explain how this app solves or addresses the problem\n\nIn our busy lives, many people, including my family, often struggle in planning meals and managing recipes. One of the problems that this app solves is the lack of organization in managing recipes. The abundance of recipes from diverse sources and blogs on the internet often overwhelms and presents challenges in organizing them (Laura 2010). The app offers user accounts, allowing individuals to consolidate and manage their recipes in a single, centralized location. This feature enables users to conveniently access their preferred recipes whenever they need them.\n\nMeal planning is also a challenge that this app aims to solve. Deciding what to cook, especially when busy, can be quite time-consuming. To solve this problem, the app includes a feature that enables users to randomly select a recipe from their collection or from publicly available recipes. This simplifies the decision-making process, making it easier for users to decide on their next meal quickly. Additionally, users have the option to save public recipes to their personal accounts, allowing them to build a curated collection of meals that align with their preferences and support their meal planning efforts.\n\nCustomized searching is helpful for users with specific dietary preferences or time constraints. This app has filtering options that allow users to search for recipes based on criteria such as type of cuisine and preparation time. This feature helps users find recipes that match their specific needs and preferences.\n\nTo maintain the integrity of the platform, this app includes several administrative features. Admins can add users to the platform to ensure a controlled and secure user base. The admins have the authority to delete public recipes that do not comply with the guidelines. Users also have control over their accounts, with the ability to create, update, and delete their own recipes.\n\n[Back to Top](#)\n\n### R2: Describe the way tasks are allocated and tracked in your project\n\nI used Trello in managing my tasks due to its good visual, straightforward and user-friendly components.\n\nLink to my Trello board --\u003e https://trello.com/b/lqrZakZf/t2a2-api-web-server\n\nI structured my Trello board into three primary lists: To Do, In Progress, and Completed. Before creating the tasks, I reviewed my notes from previous classes to plan my workflow. Initially, I placed all main tasks (represented by cards) into the To Do section. In order for me to manage the project effectively, I broke down each card into actionable steps using Trello's checklist feature. Additionally, I set due dates to manage my time effectively. Below is an example to illustrate how I organized a main task along with its corresponding subtasks:\n\n![Trello sample card with subtasks](./markdown-images/card-sample.png)\n\nAfter completing the planning phase, I moved the relevant cards from the To Do section to the In Progress section to initiate work. Here's a snapshot of my board as of June 15th:\n\n![A card with sub tasks](./markdown-images/trello-1-15June.png)\n\nOnce I completed all the subtasks on a card, I move it to the Completed section to clear my focus and concentrate on remaining tasks. Here's an example of a card \"Design the database \u0026 make ERD\" and its activity log:\n\n![Trello sample of completed card](./markdown-images/card-design-database.png)\n\n![Trello sample of card activity log](./markdown-images/card-design-database-log.png)\n\nUpdated progress on my board as of 19th of June:\n\n![Trello board on 19th June](./markdown-images/trello-2-19June.png)\n\nUpdated progress on my board as of 22nd of June:\n\n![Trello board on 22nd June](./markdown-images/trello-3-22June.png)\n\nThere were instances where I needed to extend the due dates because of the unforeseen complexities or personal reasons. In such cases, I made sure to review the tasks and adjust the timelines reasonably. Additionally, I encountered situations where I realized some subtasks were overlooked during the initial planning phase and prompted me to add them later. Below is the updated progress of my board as of 25th of June:\n\n![Trello board on 25th June](./markdown-images/trello-4-25June.png)\n\nIn addition to task management in Trello, we held daily standups on Discord. These standups were beneficial as it allowed me to evaluate my progress by answering key questions. It helped me process my thoughts and assess my remaining tasks to determine if I was on track, if there were other tasks I needed to add, or if I needed to adjust my due dates. Standups provided a daily reminder to keep me focused on my goals. Here are sample standups:\n\n![Daily standups on 24th June](./markdown-images/standups-1.png)\n\n![Daily standups on 25th June](./markdown-images/standups-2.png)\n\n![Daily standups on 26th June](./markdown-images/standups-3.png)\n\nI also used Git for source control and regularly pushed my changes to a GitHub repository. This allowed me to track the changes and their descriptions effectively. In case of errors while coding, I could revert to a previous version, which was particularly beneficial given the modular nature of my project. Additionally, the GitHub repository served as a backup of my codebase and I could retrieve it in case of issues with my local copies such as corruption or loss. Here is a sample snapshot of my commits in GitHub:\n\n![Github regular commits](./markdown-images/github-1.png)\n\n![Github regular commits](./markdown-images/github-2.png)\n\n[Back to Top](#)\n\n### R3: List and explain the third-party services, packages and dependencies used in this app\n\n* Flask - it is a lightweight web application framework in Python. It provides tools, libraries, and patterns to build web applications. Flask provides the core framework for routing, request handling, and response generation in your application.\n\n* Flask-SQLAlchemy - it integrates SQLAlchemy, a powerful SQL toolkit and Object-Relational Mapping (ORM) library, with Flask. SQLAlchemy simplifies database operations by allowing developers to work with database models and queries using Python objects. Flask-SQLAlchemy extends Flask's capabilities by providing ORM features, schema creation, and migration tools.\n\n* Psycopg2-binary - it is a PostgreSQL adapter for Python. It allows Flask applications to interact with PostgreSQL databases efficiently. Psycopg2-binary enables executing SQL queries, managing transactions, and handling database connections seamlessly within Flask-SQLAlchemy.\n\n* Python-dotenv - it is a Python library that manages environment variables from a .env file. It loads environment variables into the application's environment, making it easy to configure Flask applications across different environments (development, testing, production) without exposing sensitive information in version-controlled files.\n\n* Flask-marshmallow - it integrates the Marshmallow library with Flask. Marshmallow is a powerful library for object serialization and deserialization, commonly used in Flask applications for data validation, schema definition, and converting complex Python objects to JSON-compatible formats. Flask-marshmallow simplifies integrating Marshmallow schemas with Flask routes, enabling clean API responses and request handling.\n\n* Marshmallow - it is the core library for object serialization and deserialization in Python. It is used alongside flask-marshmallow for defining and validating API schemas, ensuring that the data exchanged between the client and server conforms to expected formats. \n\n* Flask-JWT-Extended - it is an extension that adds support for JSON Web Tokens (JWT) to Flask for authentication. It simplifies handling JWTs for protecting routes and controlling user access. It allows a secure access to protected routes based on token validation.\n\n* Bcrypt - it is a password hashing library that securely stores passwords. It employs the bcrypt hashing algorithm, which includes salting and multiple hashing rounds to protect against brute-force attacks. In the Recipe API, bcrypt is crucial for securely storing user passwords by hashing them before saving them to the database. It ensures that even if the database is compromised, passwords remain protected.\n\n* Sqlalchemy.orm - the mapped, mapped_column, relationship are SQLAlchemy ORM components used to define and manage database relationships. Mapped and mapped_column help define the columns of the database models, while relationship establishes relationships between different models, such as one-to-many or many-to-many associations.\n\n* Sqlalchemy.exc - it utilizes the IntegrityError and SQLAlchemyError exceptions used for error handling. IntegrityError is raised when a database integrity constraint (such as a unique constraint) is violated. SQLAlchemyError is a base class for all SQLAlchemy-specific exceptions.\n\n* Datetime - This module provides date-related functionalities in Python. It is used in the Recipe API to manage and manipulate date values, such as storing the creation date of recipes.\n\n* Typing - the List and Optional are type hinting tools from Python's typing module. List specifies that a variable is a list of a specific type, and Optional indicates that a variable can be of a specified type or None.\n\n* Random - This module implements pseudo-random number generators for various distributions. It is used in the Recipe API to generate random recipe.\n\n[Back to Top](#)\n\n### R4: Explain the benefits and drawbacks of this app’s underlying database system\n\nThe Recipe API uses PostgreSQL as its database system and it is interface with SQLAlchemy and psycopg2-binary. Below are the benefits and drawbacks of using PostgreSQL for this application:\n\n#### Benefits of PostgreSQL\n\n* Reliability and ACID Compliance - PostgreSQL is known for its robustness and adherence to ACID (Atomicity, Consistency, Isolation, Durability) principles. This ensures data integrity and reliability, which is crucial for applications managing sensitive data, such as user information and recipes.\n\n* Advanced SQL Features - PostgreSQL supports a wide range of advanced SQL features like window functions, common table expressions (CTEs), and full-text search. These features allow for complex queries and data manipulations that enhance the application's capability to handle sophisticated data operations.\n\n* Extensibility- PostgreSQL is highly extensible that allow users to define custom functions, data types, and operators. This flexibility can be leveraged to implement specific application logic directly within the database to improve the performance and maintainability.\n\n* Strong Community and Support - PostgreSQL has a large and active community that provides extensive documentation, plugins, and support. This community-driven development ensures continuous improvements, timely bug fixes, and security patches.\n\n* Security Features - PostgreSQL offers robust security features that includes user authentication, role-based access control, and encryption. These features are essential for protecting sensitive user data and ensuring that only authorized users can access certain parts of the application.\n\n#### Drawbacks of PostgreSQL\n\n* Complexity - PostgreSQL's rich feature set and advanced capabilities can introduce complexity, particularly for new users or those migrating from simpler database systems. Learning to utilize all of its features effectively can require a steep learning curve.\n\n* Performance Overhead - while PostgreSQL is highly performant, certain features, such as full-text search or complex query operations, can introduce performance overhead. Proper indexing and query optimization are necessary to maintain high performance, which can add to development and maintenance efforts.\n\n* Resource Intensive - PostgreSQL can be resource-intensive in terms of memory and CPU usage, especially under heavy loads or when performing complex operations. Ensuring the underlying infrastructure can support PostgreSQL's requirements is important for maintaining performance and stability.\n\n* Configuration Complexity - Optimizing PostgreSQL for specific use cases often requires fine-tuning various configuration parameters. This process can be complex and requires a deep understanding of the database's internals and the application's workload.\n\n[Back to Top](#)\n\n### R5: Explain the features, purpose and functionalities of the object-relational mapping system (ORM) used in this app.\n\nIn the Recipe API application, SQLAlchemy is used as the Object-Relational Mapping (ORM) system.\n\n#### 1. Model Definition and Schema Generation\n\nFeature: SQLAlchemy ORM allows us to define database schemas using Python classes. These classes represent tables in the database, with attributes corresponding to columns.\n\nFunctionality: Automatically generate the schema from the model definitions.\n\nExample:\n\n![Models](./markdown-images/images-models/model-category.png)\n\n![Models](./markdown-images/images-models/create-table.png)\n\nIn this example, the Category class defines a table named categories with columns category_id and cuisine_name. The db.create_all method generates the corresponding table in the postgres database.\n\n#### 2. Relationships and Foreign Keys\n\nFeature: SQLAlchemy ORM supports defining relationships between tables using foreign keys. This includes one-to-one, one-to-many, and many-to-many relationships.\n\nFunctionality: Establish and navigate relationships between models.\n\n![Models](./markdown-images/images-models/model-category.png)\n\n![Models](./markdown-images/images-models/category-recipe.png)\n\nIn this example, a one-to-many relationship is established between Category and Recipe. Each Recipe belongs to one Category, and each Category can have multiple Recipe objects.\n\n#### 3. Data Validation and Constraints\n\nFeature: SQLAlchemy supports various data types and constraints, ensuring data integrity at the model level.\n\nFunctionality: Enforce data types and constraints in the database schema.\n\n![Models](./markdown-images/images-models/model-instruction.png)\n\nIn this example, the Instruction model includes a check constraint to ensure that the step_number must be an integer. If the provided value is not an integer, an error will be raised.\n\n#### 4. Session Management\n\nFeature: SQLAlchemy ORM uses a session to manage database operations, handling transactions, and ensuring data consistency.\n\nFunctionality: Perform database operations within a managed context.\n\n![Models](./markdown-images/images-models/query-recipes-delete.png)\n\nIn this example, the session `db.session.delete(recipe)` is used to delete a recipe in the database. If an error occurs, the session is rolled back using `db.session.rollback` to maintain data consistency.\n\n#### 5. Querying the Database\n\nFeature: SQLAlchemy ORM provides a high-level API for building and executing queries. This includes support for filtering, ordering, and joining tables.\n\nFunctionality: Perform complex queries without writing raw SQL.\n\n![Models](./markdown-images/images-models/query-recipes-user.png)\n\nIn this example, a query is performed to retrieve the recipe by the user's ID. The `db.get_or_404(Recipe, recipe_id)` will retrieve the recipe with the specified ID or it will return a 404 error if the recipe is not found.\n\n[Back to Top](#)\n\n### R6: Design an entity relationship diagram (ERD) for this app’s database, and explain how the relations between the diagrammed models will aid the database design.\n\n#### This should focus on the database design BEFORE coding has begun, eg. during the project planning or design phase.\n\nThis process begins with identifying the entities and their attributes, followed by applying normalization to organize the data to eliminate redundancy and improve data integrity. It involves decomposing a set of data into multiple related tables based on rules that ensure the data is logically stored and reduces redundancy.\n\nOnce the data is normalized, the ERD is created. The ERD is a visual representation of the entities (tables) and the relationships between them. It shows how the data will be structured in the database, including primary keys, foreign keys, and other constraints that ensure data integrity. The ERD helps to visualize the logical structure of the database and how different tables connect with each other.\n\n### Step 1 - Identify the entities and their attributes\n\nSince the primary purpose of the database is to manage recipes, we begin by identifying the key entities and their attributes.\n\n1. Users\n\n    Users are individuals who create, manage, and access recipes. Each user has the following attributes.\n\n    * name: The name of the user\n    * email: The email address of the user\n    * password: The password used for login\n\n2. Recipes\n\n    Recipes are the main content of the application, created and managed by users. Initially, in a non-normalized form, each recipe might include the following attributes.\n\n    * title: The title or name of the recipe\n    * description: A description of the recipe\n    * ingredients: The ingredients required for the recipe\n    * instructions: The step-by-step instructions for preparing the recipe\n    * preparation time: The preparation time in minutes\n    * category: The category to which the recipe belongs\n    * user: The user who created the recipe\n    * date created: The date when the recipe was created\n    * is_public: A boolean indicating if the recipe is public or private\n\n### Step 2 - Normalization Process\n\nNormalization is a systematic approach to organize data in a database to reduce redundancy and improve data integrity. The normalization process involves several steps as follows.\n\n2.1 Non-Normalized Data\n\nInitially, all data might be stored in a single table, leading to redundancy and potential anomalies such as duplication.\n\nEach entity is represented by a table and the entity's attributes are represented in columns. The instance of an entity is represented in rows.\n\nUsers table:\n\n![ERD design](./markdown-images/erd/users-0.png)\n\nIn my database design, the name can only be a first name or nickname which does not matter if the user put any name. What matters most is the email address which must be unique for each user since this identifies them when logging in to the platform. The password is required for authentication while the is_admin flag determines whether the user has an admin privileges or not.\n\nRecipes table:\n\n![ERD design](./markdown-images/erd/0-recipes.png)\n\nThe recipes table has several redundancy. There are multiple values in ingredients and instructions columns.\n\n2.2 First Normal Form (1NF)\n\nThe rules of 1NF are the following:\n\n* Column names must be unique\n* Order of records does not matter\n* Records of on column must be of same type\n* Must have atomic values in columns (single values)\n\nUsers table: No change required since it already meets 1NF criteria.\n\nRecipes table:\n\n![ERD design](./markdown-images/erd/1-recipes.png)\n\nBy applying the 1NF rules, the ingredients are separated into two columns for the ingredient name and quantity. The instructions are also separated into step number and the instruction. The result has many rows but it more clear and it will be reduce the chances of mistake (anomalies) when updating the data.\n\n2.3 Second Normal Form (2NF)\n\nThe rules of 2NF are the following:\n\n* Table is in 1NF\n* All columns depend on the table's primary key\n\nUsers table: No change\n\nThe recipes table will be divided into recipes, ingredients and instructions tables so that each table has a single purpose.\n\nRecipes table:\n\n![ERD design](./markdown-images/erd/2-recipes.png)\n\nEach column will now depend on the the primary key recipe_id. Each recipe will be linked to a user on user_id.\n\nIngredients table:\n\n![ERD design](./markdown-images/erd/2-ingredients.png)\n\nEach column will depend on the ingredient_id. All rows with the same recipe_id belongs to that recipe.\n\nInstructions table:\n\n![ERD design](./markdown-images/erd/2-instructions.png)\n\nEach column will depend on the instruction_id. All rows with the same recipe_id belongs to that recipe.\n\n2.4 Third Normal Form (3NF)\n\nRules:\n\nThe rules of 3NF are the following:\n\n* Table is in 2NF\n* Contains only columns that are non-transitively dependent on the primary key.\n\nUsers table: No change\n\nThe category in recipes table does not uniquely identifies a recipe since multiple recipes can share the same category. So we separate the recipes and categories tables.\n\nRecipes table:\n\n![ERD design](./markdown-images/erd/3-recipes.png)\n\nCategories table:\n\n![ERD design](./markdown-images/erd/3-categories.png)\n\nIngredients table: No change\n\nInstructions table: No change\n\n### Step 3 - Entity Relationship Diagram (ERD)\n\n#### List of tables and attributes\n\nSome attribute's names are changed for clarity.\n\nUsers:\n\n* user_id (primary key)\n* email\n* password\n* name\n* is_admin\n\nCategoories:\n\n* category_id (primary key)\n* cuisine_name\n\nRecipes:\n\n* recipe_id (primary key)\n* title\n* description\n* preparation_time\n* is_public\n* date_created\n* user_id (foreign key)\n* category_id (foreign key)\n\nIngredients:\n\n* ingredient_id (primary key)\n* name\n* quantity\n* recipe_id (foreign key)\n\nInstructions:\n\n* instruction_id (primary key)\n* step_number\n* task\n* recipe_id (foreign key)\n\n#### Relationships between tables\n\n* Users to recipes table: It has a one-to-many relationship, indicating that a user can create multiple recipes.\n\n* Categories to recipes table: It has a one-to-many relationship, indicating that a category can have many recipes.\n\n* Recipes to ingredients table: It has a one-to-many relationship, indicating that a recipe can have many ingredients.\n\n* Recipes to instructions table: It has a one-to-many relationship, indicating that a recipe can have many instructions.\n\n#### ERD Diagram\n\n![ERD design](./markdown-images/erd/ERD-recipe-api.png)\n\n[Back to Top](#)\n\n### R7: Explain the implemented models and their relationships, including how the relationships aid the database implementation.\n\n#### This should focus on the database implementation AFTER coding has begun, eg. during the project development phase.\n\nDuring the project development phase, the implemented models and their relationships play a crucial role in defining the structure of the database and ensuring efficient data management. Here’s an overview of the models used in the application and their relationships, leveraging SQLAlchemy for ORM and Marshmallow for serialization.\n\n#### 1. User Model\n\nThe User model represents users who interact with the application, including creating and managing recipes.\n\n![Models](./markdown-images/images-models/model-user.png)\n\n* The \\__tablename__ specifies the name of the table in the database where instances of this model will be stored ('users').\n\n* The user_id, email, password, name and is_admin are attributes (columns) of the User model mapped to corresponding fields in the database table. Each attribute represents a column with its respective data type (String, Boolean, etc.).\n\n* The recipes is a relationship defined in the User model using SQLAlchemy's relationship function. The Mapped[List['Recipe']] specifies the type of data this relationship will return. It indicates that this relationship will return a list (List) of Recipe objects. The back_populates parameter ensures that changes made to one side of the relationship (either User or Recipe) are automatically reflected on the other side.\n\n#### Category Model\n\nThe Category model represents the structure of the categories table in the database.\n\n![Models](./markdown-images/images-models/model-category.png)\n\n* The \\__tablename__ specifies the name of the table in the database where instances of this model will be stored ('categories').\n\n* The category_id and cuisine_name are attributes (columns) of the Category model mapped to corresponding fields in the database table. category_id serves as the primary key, ensuring each category has a unique identifier.\n\n* The recipes is a relationship defined in the Category model using SQLAlchemy's relationship function. The Mapped[List['Recipe']] specifies the type of data this relationship will return. The back_populates parameter ensures that changes made to one side of the relationship (either Category or Recipe) are automatically reflected on the other side.\n\n#### Recipe Model\n\nThe Recipe model represents the structure of the recipes table in the database.\n\n![Models](./markdown-images/images-models/models-recipe.png)\n\n* The \\__tablename__ specifies the name of the table in the database where instances of this model will be stored ('recipes').\n\n* The recipe_id, title, description, is_public, preparation_time and date_created are attributes (columns) of the Recipe model mapped to corresponding fields in the database table.\n\n* The 'user_id' is defined as a foreign key (ForeignKey('users.user_id')) linking to the user_id column in the users table. The 'user' establishes a relationship between the Recipe and User models using SQLAlchemy's relationship function. This represents a one-to-many relationship where each recipe is associated with one user (author).\n\n* The 'category_id' is defined as a foreign key (ForeignKey('categories.category_id')) linking to the category_id column in the categories table. The 'category' establishes a relationship between the Recipe and Category models using SQLAlchemy's relationship function. This represents a many-to-one relationship where multiple recipes can belong to the same category.\n\n* The Recipe model has one-to-many relationships with the Ingredient and Instruction models respectively. Each recipe can have multiple ingredients and instructions associated with it. The \"cascade='all, delete-orphan'\" option ensures that when a recipe is deleted, all associated ingredients and instructions are also deleted (delete-orphan), maintaining referential integrity.\n\n#### Ingredient Model\n\nThe Ingredient model represents the structure of the ingredients table in the database.\n\n![Models](./markdown-images/images-models/model-ingredient.png)\n\n* \\__tablename__ specifies the name of the table in the database where instances of this model will be stored ('ingredients').\n\n* The ingredient_id, name and quantity are attributes (columns) of the Ingredient model mapped to corresponding fields in the database table.\n\n* The 'recipe_id' is defined as a foreign key (ForeignKey('recipes.recipe_id')) linking to the recipe_id column in the recipes table. The \"relationship(back_populates='ingredients')\" establishes a relationship between the Ingredient and Recipe models using SQLAlchemy's relationship function. This defines a many-to-one relationship where multiple ingredients can belong to the same recipe (Recipe model).\n\n#### Instruction Model\n\nThe Instruction model represents the structure of the instructions table in the database.\n\n![Models](./markdown-images/images-models/model-instruction.png)\n\n* \\__tablename__ specifies the name of the table in the database where instances of this model will be stored ('instructions').\n\n* The instruction_id, step_number and task are attributes (columns) of the Instruction model mapped to corresponding fields in the database table.\n\n* The 'recipe_id' is defined as a foreign key (ForeignKey('recipes.recipe_id')) linking to the recipe_id column in the recipes table. The \"relationship(back_populates='instructions')\" establishes a relationship between the Instruction and Recipe models using SQLAlchemy's relationship function. This defines a many-to-one relationship where multiple instructions can belong to the same recipe (Recipe model).\n\n#### Examples of querying data using SQLAlchemy relationships\n\n#### 1. Querying user and their recipes\n\nTo retrieve a user and all their associated recipes, we can utilize SQLAlchemy's relationship attribute defined in the User model.\n\n![Models](./markdown-images/images-models/query-recipes-user.png)\n\n* `@recipes_bp.route(\"/\u003cint:recipe_id\u003e` this decorator defines a route for accessing a specific recipe based on its recipe_id. The `\u003cint:recipe_id\u003e` part specifies that recipe_id will be an integer parameter in the URL path.\n\n* The `@jwt_required()` is a decorator ensuring that JWT (JSON Web Token) authentication is required to access this route. The presence of a valid JWT in the request headers is checked before the route function is executed.\n\n* From the statement `recipe = db.get_or_404(Recipe, recipe_id)`, the `db.get_or_404()` retrieves a recipe record from the database based on Recipe model and recipe_id. If no recipe with the specified ID is found, it returns a 404 HTTP error.\n\n* The `current_user_id = get_jwt_identity()` retrieves the current user's ID from the JWT payload.\n\n* The `if current_user_id != recipe.user_id and not current_user_is_admin()` will checks if the current user is either the author of the recipe or an admin. If not authorized, returns a 403 Forbidden error.\n\n* Finally, the `return RecipeSchema().dump(recipe)` serializes the recipe object into a JSON representation. This ensures that only relevant fields defined in RecipeSchema are returned in the response.\n\n#### 2. Deleting a recipe and its associated ingredients and instructions\n\nThis Flask route handler (delete_recipe) is designed to delete a recipe from the database. It includes authorization checks to ensure that only the author of the recipe or an admin can perform the deletion.\n\n![Models](./markdown-images/images-models/query-recipes-delete.png)\n\n* `@recipes_bp.route(\"/\u003cint:recipe_id\u003e, methods=[\"DELETE\"])` defines a route that accepts DELETE requests at `/recipes/\u003crecipe_id\u003e`. The `\u003cint:recipe_id\u003e` part in the route URL specifies that recipe_id is expected as an integer parameter.\n\n* The `@jwt_required()` is a decorator that ensures the request includes a valid JWT token. This means only authenticated users can access this endpoint.\n\n* The `def delete_recipe(recipe_id)` defines the function delete_recipe which takes recipe_id as a parameter, representing the ID of the recipe to be deleted.\n\n* The `try` begins a try block to handle potential errors during the deletion process.\n\n* The `recipe = Recipe.query.filter_by(recipe_id=recipe_id).one()` uses SQLAlchemy's query method to fetch a recipe from the database based on recipe_id. The `.one()` is used assuming that exactly one result is expected; it raises NoResultFound otherwise.\n\n* The `current_user_id = get_jwt_identity()` retrieves the current user's ID from the JWT token. The authorization check verifies if the current user is either the author of the recipe (current_user_id == recipe.user_id) or an admin (current_user_is_admin() function).\n\n* The `db.session.delete(recipe)` marks the recipe object for deletion within the current session.\n\n* The `db.session.commit()` commits the transaction to permanently delete the recipe from the database. Upon successful deletion, returns an empty dictionary {} with an HTTP 200 OK status code. If unauthorized, returns a 403 Forbidden error with an error message.\n\n[Back to Top](#)\n\n### R8: Explain how to use this application’s API endpoints. Each endpoint should be explained, including the following data for each endpoint:\n\n- HTTP verb  \n- Path or route  \n- Any required body or header data  \n- Response\n\nBruno app is an API client that allows you to construct, test, and debug HTTP requests. The following are endpoints of the Flask Recipe API and their details.\n\n![Bruno app snapshot](./markdown-images/endpoints/bruno.png)\n\n### 1. Route: /users/login\n\nHTTP Request Verb: POST  \nURL Parameters: None\n\nRequired Body:\n\n* email: User's email address  \n* password: User's password (minimum 8 characters)\n\nHeader Data: None  \nExpected Response: JWT token  \nStatus Code: 200 OK\n\nDescription: This endpoint allows users (and admin) to log in by providing their email and password. Upon successful authentication, a JWT token is returned for use in subsequent authenticated requests.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_login-admin_Success.png)\n\n#### Possible Errors\n\n400 Bad Request: If either email or password is not provided.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_login-admin_No-Email-or-Password.png)\n\n400 Bad Request: If request has no body.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_login-admin_No-Body.png)\n\n401 Unauthorized: Invalid email or password.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_login-admin_Invalid-Email_Password.png)\n\n### 2. Route: /users/register\n\nHTTP Request Verb: POST  \nURL Parameters: None\n\nRequired Body:\n\n* email: User's email address\n* password: User's password (minimum 8 characters)\n* name: User's name\n\nHeader Data: Admin's JWT token  \nExpected Response: The created user data  \nStatus Code: 201 Created\n\nDescription: This endpoint allows an admin user to register a new user by providing the required user information in the request body. The admin must include a valid JWT token in the request header for authorization.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_register-Success.png)\n\n#### Possible Errors\n\n400 Bad Request: The provided email address already exists.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_register-Email-Exists.png)\n\n400 Bad Request: Email, password, and name are not provided in the request body.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_register-Missing-Data.png)\n\n401 Unauthorized: The JWT token has expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_register-Token-Expired.png)\n\n401 Unauthorized: JWT token not provided in the request header.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_register-No-Header.png)\n\n403 Forbidden: The JWT token does not belong to an admin user.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_register-Token-Not-Admin.png)\n\n500 Internal Server Error: A general database error occurs during the creation of the user. This error was not encountered during testing but may potentially occur.\n\n### 3. Route: /recipes\n\nHTTP Request Verb: POST  \nURL Parameters: None\n\nRequired Body:\n\n* title: Title of the recipe (string)\n* description: Description of the recipe (string, optional)\n* preparation_time: Preparation time in minutes (integer, optional)\n* is_public: Whether the recipe is public (default is true for public)\n* category: An array containing cuisine_name field (from categories table)\n* ingredients: List of ingredients with name and quantity fields (from ingredients table)\n* instructions: List of instructions with step_number and task fields (from instructions table)\n\nHeader Data: JWT token of user  \nExpected Response: The created recipe data with nested  category, ingredients and instructions data.  \nStatus Code: 201 Created\n\nDescription: This endpoint allows users to create a recipe by providing details such as title, description, preparation time, category, ingredients and instructions. Authentication via a JWT token is required, and only authorized users can perform this action.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_create_Success.png)\n\n#### Possible Errors\n\n400 Bad Request: Missing required fields or invalid data format\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_create_Missing-Field.png)\n\n400 Bad Request: The provided title already exists\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_create_Title-Exists.png)\n\n401 Unauthorized: JWT token expired or not provided\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_create-Token-Expired.png)\n\n500 Internal Server Error: General server error during recipe creation\n\n### 4. Route: /users\n\nHTTP Request Verb: GET  \nURL Parameters: None  \nRequired Body: None  \nHeader Data: Admin's JWT token  \nExpected Response: A list of all users' data\nStatus Code: 200 OK\n\nDescription: This endpoint fetches all registered users. This is accessible only by admin users, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/users-get-all_Success.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_get-all_Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to an admin.\n\n![RBruno app snapshot](./markdown-images/endpoints/users_get-all_Token-Not-Admin.png)\n\n### 5. Route: /users/{int:user_id}\n\nHTTP Request Verb: GET\n\nURL Parameters:\n\n* user_id (int): The ID of the user to retrieve\n\nRequired Body: None  \nHeader Data: User's or Admin's JWT token  \nExpected Response: User data  \nStatus Code: 200 OK\n\nDescription: Retrieves details of a specific user identified by user_id. This endpoint can be accessed by the admin or the user themself, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_get-one_OK.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_get-one_Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to the admin or the user themself.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_get-one_Unauthorized.png)\n\n404 Not Found: User with the specified user_id does not exist.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_get-one_Not-Found.png)\n\n### 6. Route: /recipes/all\n\nHTTP Request Verb: GET  \nURL Parameters: None  \nRequired Body: None  \nHeader Data: Admin's JWT token  \nExpected Response: List of all recipes (public and private)  \nStatus Code: 200 OK\n\nDescription: Fetches all recipes from the database, including both public and private recipes. This endpoint is accessible only by the admin, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-all_Success.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-all_Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to the admin.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-all_Token-Not-Admin.png)\n\n### 7. Route: /recipes/user\n\nHTTP Request Verb: GET  \nURL Parameters: None  \nRequired Body: None  \nHeader Data: User's JWT token  \nExpected Response: List of all recipes owned by the user  \nStatus Code: 200 OK\n\nDescription: Fetches all recipes owned by a specific user from the database, including both public and private recipes. This endpoint is accessible by the user who created the recipes, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-all-user_Success.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-all-by-user_Token-Expired.png)\n\n### 8. Route: /recipes/user/{user_id}/category/{category_id}\n\nHTTP Request Verb: GET\n\nURL Parameters:  \n\n* user_id: (int) ID of the user whose recipes are to be fetched.\n* category_id: (int) ID of the category to filter recipes by.\n\nRequired Body: None  \nHeader Data: User's or Admin's JWT token  \nExpected Response: List of recipes owned by the specified user and filtered by the specified category  \nStatus Code: 200 OK\n\nDescription: Retrieves all recipes owned by a specific user and filtered by a specified category from the database. This endpoint is accessible by the admin or the user themselves, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-user-category_Success.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-by-user-category_Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to the admin or the user themselves.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-by-user-category_Unauthorized.png)\n\n404 Not Found: User with the specified user_id not found, or category not found.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-by-user-category_Not-Found.png)\n\n### 9. Route: /recipes/user/random\n\nHTTP Request Verb: GET\n\nURL Parameters: None  \nRequired Body: None  \nHeader Data: User's JWT token  \nExpected Response: A single recipe randomly selected from the specified user's recipes  \nStatus Code: 200 OK\n\nDescription: Retrieves a randomly selected recipe from the recipes owned by the specified user. This endpoint is accessible by the user who created the recipes, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-random_Success.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-by-user-random_Token-Expired.png)\n\n404 Not Found: User with the specified user_id not found, or no recipes found for the user.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_user-random.png)\n\n### 10. Route: /recipes/{recipe_id}\n\nHTTP Request Verb: GET\n\nURL Parameters:\n\nrecipe_id: (int) ID of the recipe to retrieve.\n\nRequired Body: None  \nHeader Data: User's or Admin's JWT token  \nExpected Response: Details of the specified recipe  \nStatus Code: 200 OK\n\nDescription: Retrieves the details of a specific recipe identified by its unique recipe_id. This endpoint is accessible by the admin or the user who created the recipe, authenticated via their JWT token provided in the request header.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-one_Success.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-one_Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to the admin or the user who created the recipe.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-one_Unauthorized.png)\n\n404 Not Found: Recipe with the specified recipe_id not found.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-one_Not-Found.png)\n\n### 11. Route: /recipes/public\n\nHTTP Request Verb: GET  \nURL Parameters: None  \nRequired Body: None  \nHeader Data: None  \nExpected Response: List of all public recipes  \nStatus Code: 200 OK\n\nDescription: Retrieves a list of all recipes marked as public in the database. This endpoint is accessible to anyone without requiring authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-all-public.png)\n\n### 12. Route: /recipes/public/filter\n\nHTTP Request Verb: GET\n\nQuery Parameters:\n\n* title: Title or name of the recipe (string)\n* prep_time: Maximum preparation time in minutes (integer)\n* ingredient_name: Name of the ingredient (string)\n* cuisine_name: Name of the cuisine category (string)\n\nRequired Body: None  \nHeader Data: None  \nExpected Response: List of filtered public recipes  \nStatus Code: 200 OK\n\nDescription: Retrieves a list of public recipes filtered by title, preparation time, ingredient name, and cuisine name criteria from the database. This endpoint is accessible to anyone without requiring authentication.\n\nSample URL:\n\n* /recipes/public/filter?title=menudo\n* /recipes/public/filter?prep_time=25\n* /recipes/public/filter?ingredient_name=bacon\n* /recipes/public/filter?cuisine_name=filipino\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-filter_Success.png)\n\n#### Possible Errors\n\n400 Bad Request: Invalid query parameters.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-filter_Invalid-Parameter.png)\n\n400 Bad Request: Invalid query type.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-filter_Invalid-Value.png)\n\n404 Not Found: The recipe with the specified value of given parameter does not exists.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-filter_Not-Found.png)\n\n### 13. Route: /recipes/public/random\n\nHTTP Request Verb: GET  \nURL Parameters: None  \nRequired Body: None  \nHeader Data: None  \nExpected Response: A randomly selected public recipe  \nStatus Code: 200 OK\n\nDescription: Retrieves a randomly selected recipe from the public recipes in the database. This endpoint does not require authentication and can be accessed by anyone.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_get-public-random_Success.png)\n\n#### Possible Errors\n\n404 Not Found: No public recipes are available.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_public-random.png)\n\n### 14. Route: /categories\n\nHTTP Request Verb: GET  \nURL Parameters: None  \nRequired Body: None  \nHeader Data: None  \nExpected Response: A list of all categories  \nStatus Code: 200 OK\n\nDescription: Retrieves all categories from the database. This endpoint does not require authentication and can be accessed by anyone.\n\n![Bruno app snapshot](./markdown-images/endpoints/categories_get-all_Success.png)\n\n#### Possible Errors\n\n404 Not Found: No categories are available.\n\n![Bruno app snapshot](./markdown-images/endpoints/categories_get-all_Not-Found.png)\n\n### 15. Route: /categories/{category_id}\n\nHTTP Request Verb: GET\n\nURL Parameters:\n\n* category_id: (int) ID of the category to retrieve.\n\nRequired Body: None  \nHeader Data: None  \nExpected Response: The details of the specified category  \nStatus Code: 200 OK\n\nDescription: Retrieves the details of a specific category by its ID from the database. This endpoint does not require authentication and can be accessed by anyone.\n\n![Bruno app snapshot](./markdown-images/endpoints/categories_get-one_Success.png)\n\n#### Possible Errors\n\n404 Not Found: Category with the specified ID not found.\n\n![Bruno app snapshot](./markdown-images/endpoints/categories_get-one_Not-Found.png)\n\n### 16. Route: /users/{user_id}\n\nHTTP Request Verb: PUT, PATCH\n\nURL Parameters:\n\n* user_id: (int) ID of the user to update.\n\nRequired Body:\n\n* email: User's new email address (string)\n* password: User's new password (string)\n* name: User's new name (string, optional)\n* is_admin: Whether the user is an admin (boolean, default False; only admin can update this field)\n\nHeader Data: JWT token of user or admin  \nExpected Response: The updated user data  \nStatus Code: 200 OK\n\nDescription: Updates the details of a specific user by their ID. This endpoint is accessible by the admin or the user themselves, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_update-Success.png)\n\n#### Possible Errors\n\n400 Bad Request: Missing required fields or invalid data format\n\n![Bruno app snapshot](./markdown-images/endpoints/users_update-Missing-Field.png)\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_update-Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to the admin or the user themselves.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_update-Unauthorized.png)\n\n404 Not Found: User with the specified ID not found.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_update-Not-found.png)\n\n500 Internal Server Error: General server error during user update.\n\n### 17. Route: /recipes/{recipe_id}\n\nHTTP Request Verb: PUT, PATCH\n\nURL Parameters:\n\n* recipe_id: (int) ID of the recipe to update.\n\nRequired Body:\n\n* title: New title of the recipe (string, optional)\n* description: New description of the recipe (string, optional)\n* preparation_time: New preparation time in minutes (integer, optional)\n* is_public: Whether the recipe is public (boolean, optional)\n* cuisine_name: New cuisine_name of the recipe (string, optional)\n* ingredients: List of new ingredients with name and quantity fields (array, optional)\n* instructions: List of new instructions with step_number and task fields (array, optional)\n\nHeader Data: JWT token of user or admin  \nExpected Response: The updated recipe data  \nStatus Code: 200 OK\n\nDescription: Updates the details of a specific recipe by its ID. This endpoint is accessible by the admin or the user who created the recipe, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_update-Success.png)\n\n#### Possible Errors\n\n400 Bad Request: Missing required fields, invalid data format or title already exists\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_update-Recipe-Exists.png)\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_update-Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to the admin or the user themselves.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_update-Unauthorized.png)\n\n404 Not Found: Recipe with the specified ID not found.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_update-Not-Found.png)\n\n500 Internal Server Error: General server error during recipe update.\n\n### 18. Route: /users/{user_id}\n\nHTTP Request Verb: DELETE\n\nURL Parameters:\n\n* user_id: (int) ID of the user to delete.\n\nRequired Body: None  \nHeader Data: JWT token of user or admin  \nExpected Response: Empty response indicating successful deletion  \nStatus Code: 200 OK\n\nDescription: Deletes a user by their ID. This endpoint is accessible by the admin or the user themselves, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_delete_Success.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_delete_Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to the admin or the user themselves.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_delete_Unauthorized.png)\n\n404 Not Found: User with the specified ID not found.\n\n![Bruno app snapshot](./markdown-images/endpoints/users_delete_Not-Found.png)\n\n500 Internal Server Error: General server error during user deletion.\n\n### 19. Route: /recipes/{recipe_id}\n\nHTTP Request Verb: DELETE\n\nURL Parameters:\n\n* recipe_id (int): The ID of the recipe to be deleted\n\nRequired Body: None  \nHeader Data: JWT token of user or admin  \nExpected Response: Empty response indicating successful deletion  \nStatus Code: 200 OK\n\nDescription: Deletes a specific recipe from the database. Accessible by the admin or the user who created the recipe, who must provide their JWT token in the request header for authentication.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_delete-Success.png)\n\n#### Possible Errors\n\n401 Unauthorized: JWT token not provided or expired.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_delete-Token-Expired.png)\n\n403 Forbidden: JWT token does not belong to the admin or the user who created the recipe.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_delete-Unauthorized.png)\n\n404 Not Found: Recipe with the specified recipe_id not found.\n\n![Bruno app snapshot](./markdown-images/endpoints/recipes_delete-Not-Found.png)\n\n500 Internal Server Error: General server error during user deletion.\n\n[Back to Top](#)\n\n### Reference List\n\nLaura, 2010, *How to Organize Recipes You Find Online*, viewed 27 June 2024, https://orgjunkie.com/2010/07/how-to-organize-recipes-you-find-online.html","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjmcaluyafuentes%2Fflask-recipe-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjmcaluyafuentes%2Fflask-recipe-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjmcaluyafuentes%2Fflask-recipe-api/lists"}