Introduction

Frontend and backend are two essential components of a web application. The frontend is the part of the application that interacts with the user, whereas the backend is the part that handles the logic and data processing behind the scenes.

The frontend, also known as the client-side, typically consists of HTML, CSS, and JavaScript code that runs in the user's web browser. The frontend handles the user interface, page layout, and overall look of the application. It also handles user interactions, such as submitting forms, clicking buttons, and navigating between pages.

On the other hand, the backend, also known as the server-side, typically consists of a server, a database, and, in our case, APIs. The backend handles the processing and storage of data, manages user authentication and authorization, and handles business logic and rules. The backend also communicates with the frontend, providing the necessary data to render the user interface and processing user inputs.

Backend

In our class we mainly use Python and SQL/JSON to create APIs and databases. Here is a simple example of creating a SQL database and using CRUD as well.

What is CRUD

  • C: The 'C' stands for create, meaning to create a new entry in a database. In this case, creating a new entry about a certain movie or TV show.

  • R: Read, or to retrieve data from the database. In this case it is selecting the movie/TV shwo that you choose to display.

  • U: Update, or changing an existing entry in the database. In this case it is selecting the preexisting movie/TV show and changing the values to match what you want.

  • D: Delete, or removing data from the database. In this case it is selecting the preexisting movie/TV show and removing the entry from the database.

Films API

This API is intended to be used as a list of movies and TV shows that a person has watched. It includes attributes for the Film name(key), the year released, the language, the number of episodes, A list of the number of episodes(using pickletype), and a youtube url for the trailer. The CRUD works as follows: Create: Enter the above mentioned attributes Read: Returns all of the films and their attributes Update: Takes in new episodes watched, and a list of their names, and adds them to their respective attibutes Delete: Option for deleting every film, also takes in a name to delete that film if it exists

from flask import Flask
import sqlite3

app = Flask(__name__)
# Connect to the SQLite database using SQLite3
conn = sqlite3.connect('instance/films.db')

# Create a cursor object to execute SQL commands
cursor = conn.cursor()

# Create a table in the database
cursor.execute('''CREATE TABLE movies
                 (id INTEGER PRIMARY KEY, title TEXT, year INTEGER, epcount INTEGER, language TEXT, trailer TEXT, eplist TEXT)''')

# Commit the changes to the database and close the connection
conn.commit()
conn.close()
import sqlite3

def create():
    # Ask the user for movie details
    title = input("Enter the movie/tv show title: ")
    year = input("Enter the movie/tv show release year: ")
    epcount = input("Enter the movie/tv show epcount: ")
    language = input("Enter the movie/tv show language: ")
    eplist = input("Enter the movie/tv show episode names: ")
    trailer = input("Enter the link movie/tv show trailer: ")

    # Connect to the database and create a cursor to execute SQL commands
    database = 'instance/films.db'
    connection = sqlite3.connect(database)
    cursor = connection.cursor()

    try:
        # Execute SQL to insert record into db
        cursor.execute("INSERT INTO movies (title, year, epcount, language, eplist, trailer) VALUES (?, ?, ?, ?, ?, ?)", (title, year, epcount, language, eplist, trailer))
        # Commit the changes
        connection.commit()
        print(f"{title} has been added to the list of movies.")

    except sqlite3.Error as error:
        print("Error while inserting record:", error)

    # Close cursor and connection
    cursor.close()
    connection.close()

create()
SpongeBob SquarePants has been added to the list of movies.
def read(id):
    # Connect to the database and create a cursor to execute SQL commands
    database = 'instance/films.db'
    connection = sqlite3.connect(database)
    cursor = connection.cursor()

    # Execute SQL to select a record from db by id
    cursor.execute("SELECT * FROM movies WHERE id=?", (id,))

    # Fetch the record from the cursor
    movie = cursor.fetchone()

    # If movie exists, print its details, else print message
    if movie:
        print(f"{movie[0]}. {movie[1]}, {movie[2]}, {movie[3]}, {movie[4]}, {movie[5]}, {movie[6]}")
    else:
        print("Movie not found.")

    # Close cursor and connection
    cursor.close()
    connection.close()

read(id=1)
1. SpongeBob SquarePants, 1999, 565, English, no, Help Wanted
def update(id):
    # Connect to the database and create a cursor to execute SQL commands
    database = 'instance/films.db'
    connection = sqlite3.connect(database)
    cursor = connection.cursor()

    # Ask the user for movie details to update
    title = input("Enter the updated movie/tv show title: ")
    year = input("Enter the updated movie/tv show release year: ")
    epcount = input("Enter the updated movie/tv show epcount: ")
    language = input("Enter the updated movie/tv show language: ")
    eplist = input("Enter the updated movie/tv show episode names: ")
    trailer = input("Enter the updated link movie/tv show trailer: ")

    try:
        # Execute SQL to update the record in db
        cursor.execute("UPDATE movies SET title=?, year=?, epcount=?, language=?, eplist=?, trailer=? WHERE id=?", (title, year, epcount, language, eplist, trailer, id))
        # Commit the changes
        connection.commit()
        print("Movie updated successfully.")

    except sqlite3.Error as error:
        print("Error while updating record:", error)

    # Close cursor and connection
    cursor.close()
    connection.close()

update(id=1)
Movie updated successfully.
def delete(id):
    # Connect to the database and create a cursor to execute SQL commands
    database = 'instance/films.db'
    connection = sqlite3.connect(database)
    cursor = connection.cursor()

    try:
        # Execute SQL to delete the record from db by id
        cursor.execute("DELETE FROM movies WHERE id=?", (id,))
        # Commit the changes
        connection.commit()
        print("Movie deleted successfully.")

    except sqlite3.Error as error:
        print("Error while deleting record:", error)

    # Close cursor and connection
    cursor.close()
    connection.close()

delete(id=2)
Movie deleted successfully.

Fetching

Overview

  • Involves retrieving data from a server or database
  • Can use different HTTP methods, such as GET, POST, PUT, and DELETE, to perform different types of operations on the server.
  • Fetching can be done through a variety of ways including AJAX, XHR, and Axios
  • In APCSP we tend to use the Fetch API over anything else
  • Fetching involves sending a request to a server using a URL (Uniform Resource Locator), which identifies the location of the resource being requested.
  • Can receive data in various formats, including JSON
  • JSON data can be parsed into objects and arrays in JavaScript, making it easy to work with and manipulate in the frontend

Python Fetch Using Request

import requests

url = "https://moviesdatabase.p.rapidapi.com/titles?page=1"

headers = {
	"content-type": "application/octet-stream",
	"X-RapidAPI-Key": "8401db6433msh3a46dd5bf23ad2ep19a280jsn48536a994246",
	"X-RapidAPI-Host": "moviesdatabase.p.rapidapi.com"
}

response = requests.get(url, headers=headers)

print(response.json())

This is a functional fetch of a movies API from Rapid API, but the data isn't very readable. Below is an example of using Pandas to format the key values as a dataframe.

import requests
import pandas as pd

url = "https://moviesdatabase.p.rapidapi.com/titles?page=1"

headers = {
    "content-type": "application/octet-stream",
    "X-RapidAPI-Key": "8401db6433msh3a46dd5bf23ad2ep19a280jsn48536a994246",
    "X-RapidAPI-Host": "moviesdatabase.p.rapidapi.com"
}

response = requests.get(url, headers=headers)
data = response.json()

# Create an empty DataFrame
df = pd.DataFrame()

# Extract the required information and store it in a list of dictionaries
results = data["results"]
entries = []
for result in results:
    entry = {
        "id": result["id"],
        "title": result["titleText"]["text"],
        "release_year": result["releaseYear"]["year"],
    }
    entries.append(entry)

# Convert the list of dictionaries into a DataFrame
df = pd.DataFrame(entries)



# ADD YOUR OWN COLUMN TO THE DATAFRAME

results = data["results"]
entries = []
for result in results:
    entry = {
        "id": result["id"],
        "title": result["titleText"]["text"],
        "release_year": result["releaseYear"]["year"],
        "isSeries": result['titleType']['isSeries']
    }
    entries.append(entry)

df = pd.DataFrame(entries)

print(df)
          id                                    title  release_year  isSeries
0  tt0001922                That Winsome Winnie Smile          1911     False
1  tt0001539                             Caïn et Abel          1911     False
2  tt0001636                                  Galileo          1911     False
3  tt0002148                   The Drummer's Vacation          1912     False
4  tt0001702               The Indian Maiden's Lesson          1911     False
5  tt0001856             The Revenue Man and the Girl          1911     False
6  tt0001790  Les misérables - Époque 1: Jean Valjean          1913     False
7  tt0000543                 Christian IXS bisættelse          1906     False
8  tt0002089                                   Carmen          1912     False
9  tt0000548                       Fiskerliv i Norden          1906     False

For the hack, I added a column for "isSeries".

Using Pandas to format a request obtained from a 3rd Party API makes it much easier to read and you can select what you want to display as well. Pandas makes it easy to access data that you feel is important.

Backend and Frontend Example

NOTES AND TAKEAWAYS:

  • CRUD is considered part of the backend
  • Data cleaning should happen in both the frontend and the backend, but definitely the backend since it could break the server
  • Communication between a local backend and public HTTPS frontend is valid
  • The context of a certain attribute matters: years within a certain timeframe, names with a certain length limit, etc. help to make sure a backend database is structured correctly and not erroneous
  • Data types matter, especially with basic SQLite3 tables
  • When fetching from larger APIs, there are content limits
    • When I tried fetching the movie API without limits, it timed out
    • Instead, look for specific pages to work with or make a search function or something

Hacks

  1. Create a completely unique API with all 4 CRUD features (Create, Read, Update, Delete)
  2. Create a Fetch API request for your corresponding API
  3. Attempt a complete website on GitHub Pages including HTML

1. Unique API with CRUD Features

Model File

""" database dependencies to support sqliteDB examples """
from random import randrange
from datetime import date
import os, base64
import json

from __init__ import app, db
from sqlalchemy.exc import IntegrityError

class Event(db.Model):
    __tablename__ = 'events'  # table name is plural, class name is singular

    # Define the User schema with "vars" from object
    id = db.Column(db.Integer, primary_key=True)
    _name = db.Column(db.String(255), unique=False, nullable=False)
    #_uid = db.Column(db.String(255), unique=True, nullable=False)
    _email = db.Column(db.String(255), unique=False, nullable=False)
    _event_name = db.Column(db.String(255), unique=False, nullable=False)
    _event_details = db.Column(db.String(255), unique=False, nullable=False)
    _date = db.Column(db.String(255), unique=False, nullable=False)
    _start_time = db.Column(db.String(255), unique=False, nullable=False)
    _end_time = db.Column(db.String(255), unique=False, nullable=False)
    _password = db.Column(db.String(255), unique=False, nullable=False)

    # constructor of a User object, initializes the instance variables within object (self) 
    def __init__(self, name, email, event_name, event_details, date, start_time, end_time, password):
        self._name = name
        self._email = email
        self._event_name = event_name
        self._event_details = event_details
        self._date = date
        self._start_time = start_time
        self._end_time = end_time
        self._password = password
    
    #here's the name getter
    @property
    def name(self):
        return self._name

    #here's the name setter
    @name.setter
    def name(self, name):
        self._name = name
    
    #here's the email getter
    @property
    def email(self):
        return self._email
    
    #here's the email setter
    @email.setter
    def email(self, email):
        self._email = email
    
    #here's the event_name getter
    @property
    def event_name(self):
        return self._event_name
    
    #here's the event_name setter
    @event_name.setter
    def event_name(self, event_name):
        self._event_name = event_name
    
    #here's the event_details getter
    @property
    def event_details(self):
        return self._event_details
    
    #here's the event_details setter
    @event_details.setter
    def event_details(self, event_details):
        self._event_details = event_details
    
    #here's the date getter
    @property
    def date(self):
        return self._date
    
    #here's the date setter
    @date.setter
    def date(self, date):
        self._date = date

    #here's the start_time getter
    @property
    def start_time(self):
        return self._start_time
    
    #here's the start_time setter
    @start_time.setter
    def start_time(self, start_time):
        self._start_time = start_time

    #here's the end_time getter
    @property
    def end_time(self):
        return self._end_time
    
    #here's the end_time setter
    @end_time.setter
    def end_time(self, end_time):
        self._end_time = end_time

    #here's the password getter
    @property
    def password(self):
        return self._password
    
    #here's the password setter
    @password.setter
    def password(self, password):
        self._password = password
    
    # output content using str(object) in human readable form, uses getter
    # output content using json dumps, this is ready for API response
    def __str__(self):
        return json.dumps(self.read())

    # CRUD create/add a new record to the table
    # returns self or None on error
    def create(self):
        try:
            # creates a person object from User(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Users table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None

    # CRUD read converts self to dictionary
    # returns dictionary
    def read(self):
        return {
            "id": self.id,
            "name": self.name,
            "email": self.email,
            "event_name": self.event_name,
            "event_details": self.event_details,
            "date": self.date,
            "start_time": self.start_time,
            "end_time": self.end_time,
            "password": self.password
        }

    # CRUD update: updates user name, password, phone
    # returns self
    def update(self, name="", email="", event_name="", event_details="", date="", start_time="", end_time="", password=""):
        """only updates values with length"""
        if len(name) > 0:
            self.name = name
        if len(email) > 0:
            self.email = email
        if len(event_name) > 0:
            self.event_name = event_name
        if len(event_details) > 0:
            self.event_details = event_details
        if len(date) > 0:
            self.date = date
        if len(start_time) > 0:
            self.start_time = start_time
        if len(end_time) > 0:
            self.end_time = end_time
        if len(password) > 0:
            self.password = password
        db.session.commit()
        return self

    # CRUD delete: remove self
    # None
    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return None


"""Database Creation and Testing """


# Builds working data for testing
def initEvents():
    with app.app_context():
        """Create database and tables"""
        db.create_all()
        """Tester data for table
        e1 = Event(name="Thomas Edison", email="tedison@lightbulb.edu",
            event_name="The Edison Troupe Meet",
            event_details="We 10 selected geniuses will meet in the events room for a convergence.",
            date="02/23/2023", start_time="13:00", end_time="14:00", password="tedisonrules20")
        e2 = Event(name="John Mortensen", email="jmortensen@powayusd.com",
            event_name="Extra Credit Code Meetup",
            event_details="Come to work on ideation and any confusion with the Full Stack CPT project. No phones.",
            date="02/25/2023", start_time="10:00", end_time="12:00", password="codec0decod3bro")
        e3 = Event(name="Karl Giant", email="giantrichguy@wallstreet.org",
            event_name="Karl and Cats",
            event_details="Karl would like to see cats with friends (if he can fit in the building).",
            date="02/26/2023", start_time="16:00", end_time="17:00", password="i_am-the-f4th3r")
        
        events = [e1, e2, e3]

        #Builds sample events data
        for event in events:
            try:
                event.create()
            except IntegrityError:
                '''fails with bad or duplicate data'''
                db.session.remove()
                print(f"Records exist, duplicate data, or error: {event.event_name}")
        """

def deleteID(event_id):                
    event = Event.query.get(event_id) #getting an event from the database based on ID

    #user = Wordle.query.filter_by(name=name).first()
    if event != None:
        print("Query 1:", event) #prints the one query if it finds one
        db.session.delete(event) #deletes the event with the db.session innate function
        db.session.commit() #commits the change
        return True
    else:
        print("event "+str(event_id)+" not found") #if the event isn't found, the display is provided
        return False

Explanations

At the top of the class are the specifications for the SQLite columns and the constructor __init__ function.

__tablename__ = 'events'  # table name is plural, class name is singular

# Define the User schema with "vars" from object
id = db.Column(db.Integer, primary_key=True)
_name = db.Column(db.String(255), unique=False, nullable=False)
#_uid = db.Column(db.String(255), unique=True, nullable=False)
_email = db.Column(db.String(255), unique=False, nullable=False)
_event_name = db.Column(db.String(255), unique=False, nullable=False)
_event_details = db.Column(db.String(255), unique=False, nullable=False)
_date = db.Column(db.String(255), unique=False, nullable=False)
_start_time = db.Column(db.String(255), unique=False, nullable=False)
_end_time = db.Column(db.String(255), unique=False, nullable=False)
_password = db.Column(db.String(255), unique=False, nullable=False)

# constructor of a User object, initializes the instance variables within object (self) 
def __init__(self, name, email, event_name, event_details, date, start_time, end_time, password):
    self._name = name
    self._email = email
    self._event_name = event_name
    self._event_details = event_details
    self._date = date
    self._start_time = start_time
    self._end_time = end_time
    self._password = password

Below these are a bunch of setters and getters for each of the attributes above.

@property
def name(self):
    return self._name

#here's the name setter
@name.setter
def name(self, name):
    self._name = name

Below the property decorators are each of the CRUD functions.

The create function adds the given Event object to the database specified in the SQLite session and commits the change.

def create(self):
    try:
        # creates a person object from User(db.Model) class, passes initializers
        db.session.add(self)  # add prepares to persist person object to Users table
        db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
        return self
    except IntegrityError:
        db.session.remove()
        return None

The read function returns each of the given Event object's attributes in the form of a dictionary (JSON).

def read(self):
    return {
        "id": self.id,
        "name": self.name,
        "email": self.email,
        "event_name": self.event_name,
        "event_details": self.event_details,
        "date": self.date,
        "start_time": self.start_time,
        "end_time": self.end_time,
        "password": self.password
    }

The update function assigns the given changes to each of the attributes after some minor validity checks. It then commits the change to the session.

def update(self, name="", email="", event_name="", event_details="", date="", start_time="", end_time="", password=""):
    """only updates values with length"""
    if len(name) > 0:
        self.name = name
    if len(email) > 0:
        self.email = email
    if len(event_name) > 0:
        self.event_name = event_name
    if len(event_details) > 0:
        self.event_details = event_details
    if len(date) > 0:
        self.date = date
    if len(start_time) > 0:
        self.start_time = start_time
    if len(end_time) > 0:
        self.end_time = end_time
    if len(password) > 0:
        self.password = password
    db.session.commit()
    return self

I struggled to get the original delete function to work, so I made the one below.

delete_ID finds a given event based on its primary key, the id attribute, deletes the row, and then commits to the session.

def deleteID(event_id):                
    event = Event.query.get(event_id) #getting an event from the database based on ID

    #user = Wordle.query.filter_by(name=name).first()
    if event != None:
        print("Query 1:", event) #prints the one query if it finds one
        db.session.delete(event) #deletes the event with the db.session innate function
        db.session.commit() #commits the change
        return True
    else:
        print("event "+str(event_id)+" not found") #if the event isn't found, the display is provided
        return False

API File

from flask import Flask, Blueprint, request, jsonify
from flask_restful import Api, Resource #restful building
from datetime import *
from flask_cors import CORS

from model.events import Event, deleteID

event_api = Blueprint('event_api', __name__,
                   url_prefix='/api/events')

# API docs https://flask-restful.readthedocs.io/en/latest/api.html
api = Api(event_api)

class EventAPI:        
    class _Create(Resource):
        def post(self):
            ''' Read data for json body '''
            body = request.get_json()
            
            ''' Avoid garbage in, error checking '''
            # validate name
            name = body.get('name')
            if name is None or len(name) < 2:
                return {'message': f'Submitter\'s name is missing, or is less than 2 characters'}, 210
            # validate uid
            email = body.get('email')
            if email is None or len(email) < 2:
                return {'message': f'Email is missing, or is less than 2 characters'}, 210
            # look for the rest of the data
            event_name = body.get('event_name')
            event_details = body.get('event_details')
            date = body.get('date')
            if (date is None) or (len(date) != 10) or (int(date[6:10]) < 2023) or (int(date[6:10]) > 2024):
                return {'message': f'Date is missing, formatted incorrectly, or within an invalid time range.'}, 210
            start_time = body.get('start_time')
            end_time = body.get('end_time')
            password = body.get('password')

            ''' #1: Key code block, setup USER OBJECT '''
            eo = Event(name=name, 
                      email=email,
                      event_name=event_name,
                      event_details=event_details,
                      date=date,
                      start_time=start_time,
                      end_time=end_time,
                      password=password)
            
            ''' #2: Key Code block to add user to database '''
            # create user in database
            event = eo.create()
            # success returns json of user
            if event:
                return jsonify(event.read())
            # failure returns error
            return {'message': f'Processed {event_name}, either a format error or the event "{event_name}" is a duplicate'}, 210

    class _Read(Resource):
        def get(self):
            events = Event.query.all()    # read/extract all users from database
            json_ready = [event.read() for event in events]  # prepare output in json
            return jsonify(json_ready)  # jsonify creates Flask response object, more specific to APIs than json.dumps
    
    class _Delete(Resource):
        def delete(self):
            body = request.get_json() #getting the database data for a given event
            event_id = body.get('id') #isolating the ID of the given entry
           
            status = deleteID(event_id) #uses the deleteID function from the model
            if status:
                return {'message': f'Successfully deleted event with id {event_id} '}
            else:
                return {'message': f'Event with id {event_id} not found'}, 240
    
    class _Update(Resource):
        def update(self):
            body = request.get_json()
            event_id = body.get('id')
            eu = Event.query.get(event_id)
            if eu == None:
                return {'message': f'Event with id {event_id} could not be found.'}

            ''' Avoid garbage in, error checking '''
            # validate name
            name = body.get('name')
            if name is None or len(name) < 2:
                return {'message': f'Submitter\'s name is missing, or is less than 2 characters'}, 210
            # validate uid
            email = body.get('email')
            if email is None or len(email) < 2:
                return {'message': f'Email is missing, or is less than 2 characters'}, 210
            # look for the rest of the data
            event_name = body.get('event_name')
            event_details = body.get('event_details')
            date = body.get('date')
            if (date is None) or (len(date) != 10) or (int(date[6:10]) < 2023) or (int(date[6:10]) > 2024):
                return {'message': f'Date is missing, formatted incorrectly, or within an invalid time range.'}, 210
            start_time = body.get('start_time')
            end_time = body.get('end_time')
            password = body.get('password')

            status = eu.update(self, name=name, email=email, event_name=event_name, event_details=event_details, date=date, start_time=start_time, end_time=end_time, password=password)
            if status != None:
                return {'message': f'Successfully updated event with id {event_id}.'}
            else:
                return {'message': f'An error occurred when updating the event.'}

    # building RESTapi endpoint
    api.add_resource(_Create, '/create')
    api.add_resource(_Read, '/')
    api.add_resource(_Delete, '/delete')
    api.add_resource(_Update, '/update')

The functions behind each of the resource procedures above are kind of self-explanatory. As a general explanation, these give instructions on what do with given information when sent to various specified endpoints from the frontend.

Fetch API Request

The frontend code behind the video I show later contains multiple fetches to the database, but I'll point out the ones that are more important to look at.

This function starts with the get fetch, based on the base endpoint urls provided above. In this case, the get is used to validate the data being entered into the database, but you can see in the video that it also gets used to create the table. You can see the full frontend code here.

The function below the read fetch contains the code for a frontend post request. It uses the /create resource link to provide a body of JSON data to the backend and commit it to the database.

const read_url = "http://127.0.0.1:8239/api/events/";
const read_options = {
    method: 'GET', //read option
    mode: 'cors', // cors used
    cache: 'default', // set to default
    credentials: 'omit', // omit credentials
    headers: {
    'Content-Type': 'application/json'
    // 'Content-Type': 'application/x-www-form-urlencoded',
    },
};
const post_url = "http://127.0.0.1:8239/api/events/create"; // for POST

function submit_Form() {
    try {
        fetch(read_url, read_options) // this is the read fetch
            // response is RESTful, process needs to be sequential
            .then(response => {
            // check for response errors
            if (response.status !== 200) {
                const errorMsg = 'Database response error: ' + response.status;
                console.log(errorMsg);
            };
            // valid response is JSON
            response.json().then(data => {
                // SKIPPING VALIDATION FUNCTIONS TO SHOW FETCH:
                const body = {
                    "name": document.getElementById('name').value,
                    "email": document.getElementById('email').value,
                    "event_name": document.getElementById('event_name').value,
                    "event_details": document.getElementById('event_details').value,
                    "date": datefix,
                    "start_time": document.getElementById('start_time').value,
                    "end_time": document.getElementById('end_time').value,
                    "password": document.getElementById('password').value
                };
                const post_options = {
                    method: 'POST',
                    body: JSON.stringify(body),
                    headers: {
                        'Content-Type':'application/json',
                        'Authorization': 'Bearer my-token',
                    },
                };
                console.log(body);
                fetch(post_url, post_options)
                    .then(response =>
                        response.json().then(data => {
                            console.log(data);
                        })
                    )
                alert("Thank you, " + form_list[0] + ", for submitting an event!\n\n(Warning: You cannot submit two events at the same time! You may only have one event at a time.)");

The code below shows the delete request, which can also be used as an update request for the purposes of this site. It utilizes the delete endpoint from the API file, and submits a body containing only the event ID so that the delete_ID Python function from before can be used.

const del_url = "http://127.0.0.1:8239/api/events/delete"; // for DELETE

function delete_Event() {
        var del_ename = document.getElementById("event_name_del").value;
        var del_password = document.getElementById("password_del").value;
        var success = false;
        fetch(read_url, read_options)
            // fetch to look for most updated event data
            .then(response => {
            // check for response errors
            if (response.status !== 200) {
                const errorMsg = 'Database response error: ' + response.status;
                console.log(errorMsg);
            };
            response.json().then(data => {
                data.forEach(event => {
                    if (event['event_name'] == del_ename && event['password'] == del_password) {
                        // if all validations successful
                        const del_ID = event['id'];
                        const body = {
                            'id':del_ID
                        };
                        const del_options = {
                            method: 'DELETE',
                            body: JSON.stringify(body),
                            headers: {
                                'Content-Type':'application/json',
                                'Authorization': 'Bearer my-token',
                            },
                        };
                        console.log(body);
                        fetch(del_url, del_options)
                            .then(response =>
                                response.json().then(data => {
                                    console.log(data);
                                })
                            )
                        alert('You have successfully deleted the event "' + event['event_name'] + '" from the events database.');
                        success = true;
                    }
                })
                if (success == false) {alert("There was an error in one of the two fields you have filled in. Make sure that your event name and password both match the case used when first created. (You can copy-paste the event name from the data below.)")}
            })
        })
    }

Video of Complete GitHub Pages Website

Click here to see a video going over the full functionality of the site, including evidence of the backend and frontend connection. You can also click here to see the site itself, though the database won't work unless the local server is run so the backend-reliant parts won't show up.