Connecting Flask Web Client to Flask REST API
Build a Flask Web App with Secure MySQL Login (REST API + Frontend)
Today,
we are creating a simple Flask web client to consume our earlier API services.
We
will demonstrate login, dashboard, admin panel access — all using JWT
authentication!
You'll
see how Frontend and Backend work together securely.
What is a Web Client?
A
web client is the gateway between users and your backend.
A web client is a software application or
interface that requests and displays data from a web server, enabling users to
interact with websites, web apps, or APIs.
It acts as the frontend that users see and
interact with, while communicating with backend servers to fetch or submit
data.
How a Web Client Works ?
Sends
a Request → Uses HTTP/HTTPS (e.g., GET /users).
Receives a Response → Gets data (HTML, JSON,
XML) from the server.
JSON
(JavaScript Object Notation) is a lightweight data interchange format used to store and
transmit structured data between systems.
It is human-readable and machine-parsable,
making it ideal for APIs, configuration files, and web
applications.
Key
Features of JSON
Text-Based –
Uses plain text to represent data.
Language-Independent – Works with any programming language.
Easy to Read & Write – Simple syntax similar to JavaScript
objects.
Supports Nested Data – Can represent complex structures.
HTML Forms: A Complete Guide:
HTML
forms are essential for collecting user input on websites. They enable login
systems, registration, contact forms, surveys, and more.
HTMLforms are the backbone of user interaction on the web. Whether you're building
a login page, survey, or checkout system, mastering forms is crucial for web
development.
Directory Structure of Web client
WebClientApp/
├──
client.py
├──
templates/
│ ├── base.html
│ ├── login.html
│ ├── register.html
│ ├── dashboard.html
│ ├── admin_panel.html
├──
static/
│ └── style.css (optional)
The code for client.py is as under:
from flask import Flask, render_template, request, redirect, url_for, session, flash
import requests
app = Flask(__name__)
app.secret_key = 'testapi' # Important for session management
# API URL (adjust if hosted elsewhere)
API_BASE_URL = "http://localhost:5000"
# Home Route
@app.route('/')
def home():
return redirect(url_for('login'))
# Register Page
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
data = {
"username": request.form['username'],
"password": request.form['password'],
"role": request.form['role']
}
response = requests.post(f"{API_BASE_URL}/register", json=data)
if response.status_code == 201:
flash('Registered successfully! Please log in.', 'success')
return redirect(url_for('login'))
else:
flash(response.json().get('error', 'Registration failed.'), 'danger')
return render_template('register.html')
# Login Page
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
data = {
"username": request.form['username'],
"password": request.form['password']
}
response = requests.post(f"{API_BASE_URL}/login", json=data)
if response.status_code == 200:
session['token'] = response.json()['access_token']
flash('Logged in successfully!', 'success')
return redirect(url_for('dashboard'))
else:
flash(response.json().get('error', 'Login failed.'), 'danger')
return render_template('login.html')
# Dashboard Page (Protected)
@app.route('/dashboard')
def dashboard():
token = session.get('token')
if not token:
flash('Please log in first.', 'warning')
return redirect(url_for('login'))
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/dashboard", headers=headers)
if response.status_code == 200:
return render_template('dashboard.html', data=response.json())
else:
flash(response.json().get('error', 'Access denied.'), 'danger')
return redirect(url_for('login'))
# Admin Panel (Protected for Admin Role)
@app.route('/admin')
def admin_panel():
token = session.get('token')
if not token:
flash('Please log in first.', 'warning')
return redirect(url_for('login'))
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{API_BASE_URL}/admin", headers=headers)
if response.status_code == 200:
return render_template('admin_panel.html', data=response.json())
else:
flash(response.json().get('error', 'Access denied.'), 'danger')
return redirect(url_for('dashboard'))
# Logout
@app.route('/logout')
def logout():
session.clear()
flash('Logged out successfully.', 'success')
return redirect(url_for('login'))
if __name__ == "__main__":
app.run(port=5001, debug=True)
1) base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}Flask Web Client{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Web Client</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav ml-auto">
{% if session.token %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('dashboard') }}">Dashboard</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('admin_panel') }}">Admin Panel</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('logout') }}">Logout</a></li>
{% else %}
<li class="nav-item"><a class="nav-link" href="{{ url_for('login') }}">Login</a></li>
<li class="nav-item"><a class="nav-link" href="{{ url_for('register') }}">Register</a></li>
{% endif %}
</ul>
</div>
</nav>
<div class="container mt-4">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
</body>
</html>
2) login.html
{% extends "base.html" %}
{% block title %}Login{% endblock %}
{% block content %}
<h2>Login</h2>
<form method="POST">
<div class="mb-3">
<label>Username:</label>
<input type="text" name="username" class="form-control" required>
</div>
<div class="mb-3">
<label>Password:</label>
<input type="password" name="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
{% endblock %}
3) register.html
{% extends "base.html" %}
{% block title %}Register{% endblock %}
{% block content %}
<h2>Register</h2>
<form method="POST">
<div class="mb-3">
<label>Username:</label>
<input type="text" name="username" class="form-control" required>
</div>
<div class="mb-3">
<label>Password:</label>
<input type="password" name="password" class="form-control" required>
</div>
<div class="mb-3">
<label>Role:</label>
<select name="role" class="form-control" required>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
<button type="submit" class="btn btn-success">Register</button>
</form>
{% endblock %}
4) dashboard.html
{% extends "base.html" %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<h2>Dashboard</h2>
<p>Welcome to the user dashboard!</p>
<pre>{{ data | tojson(indent=2) }}</pre>
{% endblock %}
5) admin_panel.html
{% extends "base.html" %}
{% block title %}Admin Panel{% endblock %}
{% block content %}
<h2>Admin Panel</h2>
<p>Welcome, Admin!</p>
<pre>{{ data | tojson(indent=2) }}</pre>
{% endblock %}

No comments: