Skip to the content.
Home Accounts Setup Verify Play Hacks

Sprint 4 Database Blog

Data Integration and CRUD

Python

Gaheera Babbar P4 Popcorn Hacks 3.1

Full-Stack Feature Blog: API Requests, Database Interaction, and Algorithm Handling

In this blog, we will walk through various code snippets that demonstrate how a full-stack feature works. We’ll cover input/output requests via frontend and Postman, database interaction, algorithmic code for handling API requests, and the overall flow of data in the system. Let’s dive in!

1. Frontend: Input to API Request and Present API Response

The frontend HTML form enables users to create a profile by inputting their name, bio, favorite artists, and profile picture URL. Upon submission, the data is sent via a POST request to the backend API, which handles the creation of the profile.

Code Snippet: Profile Setup Form

<form id="profileForm" onsubmit="return handleFormSubmission(event)">
    <label for="name">Your Name:</label>
    <input type="text" id="name" name="name" required>

    <label for="bio">Bio:</label>
    <textarea id="bio" name="bio" rows="4" required></textarea>

    <label for="artists">Top 5 Favorite Artists:</label>
    <input type="text" id="artists" name="artists" placeholder="Comma-separated list" required>

    <label for="profilePicture">Profile Picture:</label>
    <input type="text" id="profilePicture" name="profilePicture" placeholder="Profile Picture URL" required>

    <button type="submit">Create Profile</button>
</form>

Description: This form collects user data such as name, bio, favorite artists, and profile picture URL. When the form is submitted, the JavaScript function handleFormSubmission processes the input data and sends it to the backend API to create the profile.


Code Snippet: JavaScript for API Request

const createProfile = async (name, artists, bio, profilePicture) => {
    const url = `http://127.0.0.1:8887/api/profile`;

    const data = {
        name: name,
        uid: name,
        pfp: profilePicture,
        bio: bio,
        favorite_artist: artists
    };

    try {
        const response = await fetch(url, {
            method: "POST",
            mode: "cors",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(data)
        });

        if (!response.ok) {
            const errorData = await response.json();
            console.error("Error:", errorData.message);
        } else {
            const result = await response.json();
            console.log("Success:", result);
            alert("Profile created successfully!");
        }
    } catch (error) {
        console.error("Fetch error:", error);
    }
};

Description: This JavaScript function sends a POST request to the backend API with the profile data. The response is logged to the console, and if successful, the user is notified.


2. Postman: API Request and Response

Using Postman, you can manually test the API by sending raw requests to the server. Below is how you can simulate the same request that the frontend sends.

Postman Request

  • Method: POST
  • URL: http://127.0.0.1:8887/api/profile
  • Body:
{
    "name": "John Doe",
    "uid": "JohnDoe",
    "pfp": "https://example.com/profile.jpg",
    "bio": "An avid music lover.",
    "favorite_artist": "The Beatles, Queen, Pink Floyd"
}

Postman Response

  • Success Response (200 OK):
{
    "message": "Profile created successfully"
}
  • Error Response (400 Bad Request):
{
    "error": "Invalid input data"
}

Description: In Postman, we test the API by sending a POST request with user profile data. The response is captured to check if the profile was created successfully or if there was an error.


3. Database Interaction: Handling Data with Lists and Dictionaries

The backend interacts with a database, typically storing data in rows and columns. In Python, lists can represent rows (individual records), while dictionaries represent columns (fields). Here’s how we handle database queries and format the response data into the DOM.

Code Snippet: Python Database Query

# Sample database query to fetch profile data (rows) from the database
import sqlite3

def get_profile_data(uid):
    connection = sqlite3.connect("profiles.db")
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM profiles WHERE uid=?", (uid,))
    profile = cursor.fetchone()  # Returns a tuple (row)
    connection.close()
    return profile

Description: This function queries the database to fetch a profile by its unique ID (uid). The result is returned as a row (a tuple), which will be processed later.


Code Snippet: Python Code to Format Data into JSON

from flask import jsonify

@app.route('/api/profile', methods=['GET'])
def get_profile():
    profile_data = get_profile_data("JohnDoe")
    return jsonify({
        "name": profile_data[0],  # Assuming the first column is the name
        "bio": profile_data[1],   # Bio column
        "favorite_artist": profile_data[2]  # Favorite artist column
    })

Description: This API endpoint fetches profile data from the database and formats it into a JSON response using jsonify. This data is then sent back to the frontend, where it can be displayed in the DOM.


4. Algorithmic Code: Handling API Requests

In the backend, we have a class that handles API requests (GET, POST, PUT, DELETE) for profile data. The class includes methods for sequencing, selection, and iteration over data.

Code Snippet: API Class with CRUD Operations

class ProfileAPI:
    def create_profile(self, data):
        # Create profile in the database
        pass
    
    def get_profile(self, uid):
        # Retrieve profile from the database
        pass

    def update_profile(self, uid, data):
        # Update profile data in the database
        pass

    def delete_profile(self, uid):
        # Delete profile from the database
        pass

Description: This class encapsulates the logic for CRUD operations. Each method handles a specific HTTP request, interacting with the database as needed.


Code Snippet: Method with Sequencing, Selection, and Iteration

class ProfileAPI:
      
    class _CRUD(Resource):
        """
        API for Create, Read, Update, Delete operations on the PublicProfile model.
        """

        # def post(self):
        #     return "jamal"
        
        def post(self):
            """
            Create a new public profile.
            """
            body = request.get_json()
            
            print(body)

            # Validate name
            name = body.get('name')
            if name is None or len(name) < 2:
                return {'message': 'Name is missing or is less than 2 characters'}, 400

            # Validate uid
            uid = body.get('uid')
            if uid is None or len(uid) < 2:
                return {'message': 'User ID is missing or is less than 2 characters'}, 400

            # Setup PublicProfile object
            profile_obj = PublicProfile(
                name=name,
                uid=uid,
                pfp=body.get('pfp', ''),
                bio=body.get('bio', ''),
                favorite_artist=body.get('favorite_artist', '')
            )

            profile = profile_obj.create()  # pass the body elements to be saved in the database
            if not profile:  # failure returns error message
                return {'message': f'Processed {name}, either a format error or User ID {uid} is duplicate'}, 400
            
            return jsonify(profile.read())

        @token_required
        def get(self):
            """
            Return the current user's profile as a JSON object.
            """
            profile = g.current_user
            profile_data = profile.read()
            if not profile:
                return {'message': 'No profile found'}, 404
            return jsonify(profile_data)

        @token_required
        def put(self):
            """
            Update a public profile.
            """
            current_profile = g.current_user
            body = request.get_json()

            # Allow admins to update other profiles
            if current_profile and current_profile.uid == 'admin_user':  # Assuming 'admin_user' identifies admins
                uid = body.get('uid')
                if uid and uid != current_profile.uid:
                    profile = PublicProfile.query.filter_by(_uid=uid).first()
                    if not profile:
                        return {'message': f"Profile with UID {uid} not found"}, 404
                else:
                    profile = current_profile  # Admin is updating their own profile
            else:
                profile = current_profile  # Regular users can only update their own profile

            # Update profile fields
            profile.update(body)
            return jsonify(profile.__dict__)

        @token_required
        def delete(self):
            """
            Delete a public profile.
            """
            current_profile = g.current_user
            if not current_profile:
                return {'message': 'No profile found to delete'}, 404

            try:
                current_profile.delete()
                return {'message': f"Profile {current_profile.uid} deleted successfully"}, 200
            except Exception as e:
                return {'message': f"Error occurred: {str(e)}"}, 500

Description: This method iterates over the rows fetched from the database, selects the necessary columns, and formats them as dictionaries before appending them to a list. The final list is returned as a JSON response.


5. Call to Algorithm Request: Fetching Data from Endpoint

Now let’s look at how we make a call to the backend API to fetch profile data.

Code Snippet: Fetch Request to Backend API

const fetchProfile = async () => {
    const response = await fetch('http://127.0.0.1:8887/api/profile', {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json'
        }
    });

    const data = await response.json();
    console.log(data);
};

Description: This JavaScript function sends a GET request to fetch the profile data from the backend. The returned data is logged to the console for review.


Handling Different Responses Based on Method Changes

When data or the method changes, it can trigger different responses. For example, sending a PUT request to update a profile would result in a success response if the data is valid.

Code Snippet: Handling PUT Request Response

const updateProfile = async (uid, updatedData) => {
    const response = await fetch(`http://127.0.0.1:8887/api/profile/${uid}`, {
        method: 'PUT',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(updatedData)
    });

    if (response.ok) {
        const result = await response.json();
        alert("Profile updated successfully!");
    } else {
        const errorData = await response.json();
        console.error("Error:", errorData.message);
        alert("An error occurred while updating the profile.");
    }
};

Description: This function handles the response from a PUT request, either alerting the user of a successful update or logging an error message if the request fails.


Conclusion

In this blog, we covered how a full-stack feature handles input/output requests, database interaction, and algorithmic code to process API requests. We explored frontend forms, Postman testing, database queries, and how CRUD operations are structured in a class. By handling different request types and responses, we ensure our application responds efficiently under various conditions.