View/CRUD Concepts
Frontend Data Capture
HTML to obtain input is a key step in a frontend system that works with APIs that support database operations.

HTML5 table is a way to organize input
- th labels
- td input types
- onclick starts an action
<table>
<tr>
<th><label for="name">Name</label></th>
<th><label for="email">Email</label></th>
<th><label for="password">Password</label></th>
<th><label for="phone">Phone</label></th>
</tr>
<tr>
<td><input type="text" name="name" id="name" required></td>
<td><input type="email" name="email" id="email" placeholder="abc@xyz.org" required></td>
<td><input type="password" name="password" id="password" required></td>
<td><input type="tel" name="phone_num" id="phone_num"
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
placeholder="999-999-9999"></td>
<td ><button onclick="create_User()">Create</button></td>
</tr>
</table>
Table
An HTML5 form is another way to organize input
- form action vs onclick
- p tags
- labels
- input
<form action="create_User()">
<p><label>
Name:
<input type="text" name="name" id="name" required>
</label></p>
<p><label>
User ID:
<input type="text" name="uid" id="uid" required>
</label></p>
<p><label>
Password:
<input type="password" name="password" id="password" required>
Verify Password:
<input type="password" name="passwordV" id="passwordV" required>
</label></p>
<p><label>
Phone:
<input type="tel" name="phone_num" id="phone_num"
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
placeholder="999-999-9999">
</label></p>
<p><label>
Birthday:
<input type="date" name="dob" id="dob">
</label></p>
<p>
<button>Create</button>
</p>
</form>
Form
API Request and Response
In an API database project, the key idea is to build a system for information exchange. For instance, user information exchange would require login, create, read, update, and delete operations in a Backend Database.
- Login to existing account: The request requires credentials, and the response returns a token.

- Create new user records: The request requires a token, body, and the response returns the created JSON object of the user from the database.

- Read logged in user: The request requires a valid token, and the response returns a the logged in JSON user object from the database.

- Update user data: The request requires a valid token, body, and the response returns the updated JSON object of the user from the database.

- Delete a user record: The request requires a valid Admin token, and the response only returns a 204 success status.

Backend API
In a Python Flask database project, the API endpoint serves as a middle layer between the frontend request and the backend database. The backend receives requests and prepares responses.
This is a list of common operations in these API endpoints:
- Validates Input: An API stops the “Garbage In” and sends error responses in case of garbage.
- Query of Database: A User SQLAlchemy command will query data according to the method requirement and the body of the request.
- Class Method Call: A User method will perform a CRUD operation on the database according to the request.
- RESTful Response: The return statement sends data back to the request.
CRUD Handling
Observe and study this API in detail. Each endpoint contains comments explaining its functionality. A key element of this lesson is to learn the interactions between the API endpoint and the User table in the database.
class _CRUD(Resource): # Users API operation for Create, Read, Update, Delete
def post(self): # Create method
"""
Create a new user.
Reads data from the JSON body of the request, validates the input, and creates a new user in the database.
Returns:
JSON response with the created user details or an error message.
"""
# Read data for json body
body = request.get_json()
''' Avoid garbage in, error checking '''
# validate name
name = body.get('name')
password = body.get('password')
if name is None or len(name) < 2:
return {'message': f'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': f'User ID is missing, or is less than 2 characters'}, 400
# check if uid is a GitHub account
_, status = GitHubUser().get(uid)
if status != 200:
return {'message': f'User ID {uid} not a valid GitHub account' }, 404
''' User object creation '''
#1: Setup minimal User object using __init__ method
user_obj = User(name=name, uid=uid)
#2: Save the User object to the database using custom create method
user = user_obj.create(body) # pass the body elements to be saved in the database
if not user: # failure returns error message
return {'message': f'Processed {name}, either a format error or User ID {uid} is duplicate'}, 400
# return response, the created user details as a JSON object
return jsonify(user.read())
@token_required()
def get(self):
"""
Retrieve all users.
Retrieves a list of all users in the database.
Returns:
JSON response with a list of user dictionaries.
"""
# retrieve the current user from the token_required authentication check
current_user = g.current_user
""" User SQLAlchemy query returning list of all users """
users = User.query.all() # extract all users from the database
# prepare a json list of user dictionaries
json_ready = []
for user in users:
user_data = user.read()
if current_user.role == 'Admin' or current_user.id == user.id:
user_data['access'] = ['rw'] # read-write access control
else:
user_data['access'] = ['ro'] # read-only access control
json_ready.append(user_data)
# return response, a list of user dictionaries in JSON format
return jsonify(json_ready)
@token_required()
def put(self):
"""
Update user details.
Retrieves the current user from the token_required authentication check and updates the user details based on the JSON body of the request.
Returns:
JSON response with the updated user details or an error message.
"""
# Retrieve the current user from the token_required authentication check
current_user = g.current_user
# Read data from the JSON body of the request
body = request.get_json()
''' Admin-specific update handling '''
if current_user.role == 'Admin':
uid = body.get('uid')
# Admin is updating themself
if uid is None or uid == current_user.uid:
user = current_user
else: # Admin is updating another user
""" User SQLAlchemy query returning a single user """
user = User.query.filter_by(_uid=uid).first()
if user is None:
return {'message': f'User {uid} not found'}, 404
else:
# Non-admin can only update themselves
user = current_user
# Accounts are desired to be GitHub accounts, change must be validated
if body.get('uid') and body.get('uid') != user._uid:
_, status = GitHubUser().get(body.get('uid'))
if status != 200:
return {'message': f'User ID {body.get("uid")} not a valid GitHub account' }, 404
# Update the User object to the database using custom update method
user.update(body)
# return response, the updated user details as a JSON object
return jsonify(user.read())
@token_required("Admin")
def delete(self):
"""
Delete a user.
Deletes a user from the database based on the JSON body of the request. Only accessible by Admin users.
Returns:
JSON response with a success message or an error message.
"""
body = request.get_json()
uid = body.get('uid')
""" User SQLAlchemy query returning a single user """
user = User.query.filter_by(_uid=uid).first()
# bad request
if user is None:
return {'message': f'User {uid} not found'}, 404
# Read and then Delete the User object using custom methods
user_json = user.read()
user.delete()
# 204 is the status code for delete with no json response
return f"Deleted user: {user_json}", 204 # use 200 to test with Postman
Response Handling
Response handling goes back to the requester. This example illustrates data JavaScript may receive on a read and how it would construct a table that, in turn, could activate other Update and Delete actions.
- JSON response is required: It is hardcoded in this example. Typically, JSON will come from a JavaScript fetch.
- JSON object is required: In this case, a list of JSON objects. Each object allows access to elements in JSON using JavaScript dot notation (e.g.,
user._name). - DOM editing: This is a significant part of the remainder of this example. DOM elements often nest inside other DOM elements. For instance, each
tdis nested in atr. Find examples of DOM create and append in the code below. - Notice the definition of table and build your own map or visual of how these elements are put together.
<table>
<thead>
<tr>
<th>Name</th>
<th>ID</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="table">
<!-- javascript generated data -->
</tbody>
</table>
<script>
// Static json, this can be used to test data prior to API and Model being ready
const json = '[{"_name": "Thomas Edison", "_uid": "toby"}, {"_name": "Nicholas Tesla", "_uid": "nick"}, {"_name": "John Mortensen", "_uid": "jm1021"}, {"_name": "Eli Whitney", "_uid": "eli"}, {"_name": "Hedy Lemarr", "_uid": "hedy"}]';
// Convert JSON string to JSON object
const data = JSON.parse(json);
// prepare HTML result container for new output
const table = document.getElementById("table");
data.forEach(user => {
// build a row for each user
const tr = document.createElement("tr");
// td's to build out each column of data
const name = document.createElement("td");
const id = document.createElement("td");
const action = document.createElement("td");
// add content from user data
name.innerHTML = user._name;
id.innerHTML = user._uid;
// add action for update button
var updateBtn = document.createElement('input');
updateBtn.type = "button";
updateBtn.className = "button";
updateBtn.value = "Update";
updateBtn.style = "margin-right:16px";
updateBtn.onclick = function () {
alert("Update: " + user._uid);
};
action.appendChild(updateBtn);
// add action for delete button
var deleteBtn = document.createElement('input');
deleteBtn.type = "button";
deleteBtn.className = "button";
deleteBtn.value = "Delete";
deleteBtn.style = "margin-right:16px"
deleteBtn.onclick = function () {
alert("Delete: " + user._uid);
};
action.appendChild(deleteBtn);
// add data to row
tr.appendChild(name);
tr.appendChild(id);
tr.appendChild(action);
// add row to table
table.appendChild(tr);
});
</script>
| Name | ID | Actions |
|---|