1. Project Overview & Architecture
Welcome to Major Project 5: Full-Stack REST API & Web Dashboard! In this final major project, you will bridge back-end Python logic with modern web architecture. You will construct a complete, lightweight RESTful backend server using Flask that handles resource management over HTTP (GET, POST, DELETE), enforces payload validation, manages status codes, and serves an interactive real-time web dashboard.
Project Core Objectives
- RESTful Architecture: Build routes corresponding to standard HTTP verbs (
GETfor retrieval,POSTfor creation,DELETEfor removal). - JSON Payload Validation: Inspect incoming HTTP headers and body data, returning explicit HTTP error codes (
400 Bad Request,404 Not Found) when payloads fail schema checks. - State Synchronization: Maintain an in-memory data store with unique primary key generation and dynamic state updating.
- Frontend Integration: Serve an embedded Single-Page Application (SPA) dashboard that uses asynchronous JavaScript (
fetch) to update the UI without reloading the page.
2. Functional Requirements & Feature Specifications
Your REST API & Web Dashboard must fulfill the following technical endpoints and specifications:
| Endpoint / Route | HTTP Verb | Functional Purpose | Success / Error Codes |
|---|---|---|---|
/ |
GET |
Serves the HTML/JS Web Dashboard UI. | 200 OK |
/api/items |
GET |
Returns array of all recorded inventory items as JSON. | 200 OK |
/api/items |
POST |
Accepts JSON payload and appends a new resource record. | 201 Created, 400 Bad Request |
/api/items/<id> |
DELETE |
Removes a specific resource item by unique integer ID. | 200 OK, 404 Not Found |
3. Complete Reference Implementation
Study the self-contained Flask REST API server and embedded dashboard application below:
from flask import Flask, jsonify, request, render_template_string
app = Flask(__name__)
# In-memory database datastore
inventory = [
{"id": 1, "name": "Developer Laptop", "category": "Electronics", "qty": 12, "price": 1299.99},
{"id": 2, "name": "Ergonomic Desk", "category": "Furniture", "qty": 8, "price": 349.50}
]
next_id = 3
# Single-Page Web Dashboard UI (HTML/JS)
HTML_DASHBOARD = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>REST API Dashboard</title>
<style>
body { font-family: system-ui, -apple-system, sans-serif; margin: 2rem; background: #f8f9fa; color: #212529; }
h1 { font-size: 1.5rem; margin-bottom: 1rem; }
.card { background: white; padding: 1.5rem; border-radius: 8px; border: 1px solid #dee2e6; margin-bottom: 1.5rem; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 0.5rem; margin-bottom: 1rem; }
input, button { padding: 0.5rem; border: 1px solid #ced4da; border-radius: 4px; font-size: 0.9rem; }
button { background: #0d6efd; color: white; border: none; cursor: pointer; font-weight: 600; }
button:hover { background: #0b5ed7; }
button.delete-btn { background: #dc3545; padding: 0.25rem 0.5rem; font-size: 0.8rem; }
button.delete-btn:hover { background: #bb2d3b; }
table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
th, td { text-align: left; padding: 0.75rem; border-bottom: 1px solid #dee2e6; font-size: 0.9rem; }
th { background: #e9ecef; }
</style>
</head>
<body>
<h1>📦 Inventory REST API & Dashboard</h1>
<div class="card">
<h3 style="margin-top:0;">Add New Item (POST /api/items)</h3>
<div class="form-grid">
<input type="text" id="itemName" placeholder="Item Name" />
<input type="text" id="itemCategory" placeholder="Category" />
<input type="number" id="itemQty" placeholder="Quantity" />
<input type="number" step="0.01" id="itemPrice" placeholder="Price ($)" />
</div>
<button onclick="addItem()">Submit item via API</button>
</div>
<div class="card">
<h3 style="margin-top:0;">Live Inventory State (GET /api/items)</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Category</th>
<th>Quantity</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody id="inventoryTable"></tbody>
</table>
</div>
<script>
async function loadInventory() {
const res = await fetch('/api/items');
const data = await res.json();
const tbody = document.getElementById('inventoryTable');
tbody.innerHTML = '';
data.forEach(item => {
tbody.innerHTML += `
<tr>
<td>#${item.id}</td>
<td><strong>${item.name}</strong></td>
<td>${item.category}</td>
<td>${item.qty}</td>
<td>$${item.price.toFixed(2)}</td>
<td><button class="delete-btn" onclick="deleteItem(${item.id})">Delete</button></td>
</tr>
`;
});
}
async function addItem() {
const name = document.getElementById('itemName').value.trim();
const category = document.getElementById('itemCategory').value.trim();
const qty = parseInt(document.getElementById('itemQty').value);
const price = parseFloat(document.getElementById('itemPrice').value);
if (!name || !category || isNaN(qty) || isNaN(price)) {
alert('Please fill out all input fields accurately.');
return;
}
const response = await fetch('/api/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, category, qty, price })
});
if (response.ok) {
document.getElementById('itemName').value = '';
document.getElementById('itemCategory').value = '';
document.getElementById('itemQty').value = '';
document.getElementById('itemPrice').value = '';
loadInventory();
} else {
const err = await response.json();
alert('API Error: ' + err.error);
}
}
async function deleteItem(id) {
const response = await fetch(`/api/items/${id}`, { method: 'DELETE' });
if (response.ok) {
loadInventory();
} else {
alert('Failed to delete item.');
}
}
// Initial fetch on view mount
loadInventory();
</script>
</body>
</html>
"""
@app.route("/")
def index():
"""Serves the Single-Page Web Dashboard."""
return render_template_string(HTML_DASHBOARD)
@app.route("/api/items", methods=["GET"])
def get_all_items():
"""Returns the current list of inventory records as JSON."""
return jsonify(inventory), 200
@app.route("/api/items", methods=["POST"])
def create_new_item():
"""Validates payload and inserts a new inventory record."""
global next_id
payload = request.get_json()
# Payload validation checks
if not payload:
return jsonify({"error": "Missing or invalid JSON payload"}), 400
required_fields = ["name", "category", "qty", "price"]
for field in required_fields:
if field not in payload:
return jsonify({"error": f"Missing required field: '{field}'"}), 400
try:
new_record = {
"id": next_id,
"name": str(payload["name"]).strip(),
"category": str(payload["category"]).strip(),
"qty": int(payload["qty"]),
"price": float(payload["price"])
}
except (ValueError, TypeError):
return jsonify({"error": "Data type mismatch for qty or price"}), 400
inventory.append(new_record)
next_id += 1
return jsonify(new_record), 201
@app.route("/api/items/<int:item_id>", methods=["DELETE"])
def remove_item(item_id):
"""Deletes an item record by ID if it exists."""
global inventory
target_item = next((item for item in inventory if item["id"] == item_id), None)
if not target_item:
return jsonify({"error": f"Item with ID #{item_id} not found"}), 404
inventory = [item for item in inventory if item["id"] != item_id]
return jsonify({"message": f"Successfully deleted item #{item_id}"}), 200
if __name__ == "__main__":
print("🚀 REST API Server running at http://127.0.0.1:5000")
app.run(debug=True, port=5000)
4. Common Project Pitfalls
⚠️ Project Watchlist:
- Incorrect HTTP Status Codes: Always return specific status codes (e.g.,
201 Createdon POST success,400 Bad Requestfor invalid input, and404 Not Foundfor missing resources) instead of generic 200 codes for everything. - Unvalidated Request Payloads: Accessing key names like
payload["name"]without verifying their presence will crash the backend server with an unhandledKeyError. - Cross-Origin Resource Sharing (CORS): If your web dashboard is hosted on a different domain or port than your API server, browsers will block network calls unless CORS headers are configured.
5. Interactive Project Workspace
Build & Test: REST API & Web Dashboard
Use the editor below to craft your REST API endpoints. Try adding a PUT endpoint to support updating existing inventory quantities!
Major Project 5 Completed!
Outstanding achievement! You have mastered full-stack Python development by building a production-grade REST API and web interface.
6. Key Takeaways & Graduation Achievement
- REST APIs provide structured web interfaces for interacting with application resources over HTTP.
- Using proper HTTP status codes makes back-end services predictable and easy to integrate with front-end applications.
- Input validation protects servers from malformed data payloads and runtime exceptions.
🎓 Python Course Completed!
Congratulations on finishing all lessons and major projects! You have built a solid, real-world practical understanding of modern Python development.