🚀 Adding JavaScript Functionality to Our Full-Stack FastAPI App
In this step, we are connecting our JavaScript files to the application. This allows our:
Register page
Login page
to function properly by sending data to FastAPI endpoints, handling form submissions, and managing authentication tokens (JWTs).
We’re using Jinja layout to include JavaScript files just like we included CSS files earlier.
📁 Adding JavaScript Files to layout.html
Open
layout.htmlin the templates folder.Add
<script>tags just before the closing</body>tag. This ensures the scripts load after the DOM.
<!-- jQuery Slim -->
<script src="{{ url_for('static', path='js/jquery-slim.js') }}"></script>
<!-- Popper.js -->
<script src="{{ url_for('static', path='js/popper.js') }}"></script>
<!-- Bootstrap JS -->
<script src="{{ url_for('static', path='js/bootstrap.js') }}"></script>
<!-- Base JS (custom app JS) -->
<script src="{{ url_for('static', path='js/base.js') }}" defer></script>✅ Notes:
deferensures base.js runs last, after the DOM is loaded.base.js relies on element IDs (like
registerFormorloginForm) to exist before running.
📌 How the JavaScript Works
1. Register Form
In base.js, we have:
const registerForm = document.getElementById("registerForm");
if (registerForm) {
registerForm.addEventListener("submit", async (e) => {
e.preventDefault();
// Check if passwords match
if (password.value !== password2.value) {
alert("Passwords do not match!");
return;
}
const payload = {
email: data.email,
username: data.username,
first_name: data.first_name,
last_name: data.last_name,
role: data.role,
phone_number: data.phone_number,
password: data.password
};
const response = await fetch("/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (response.ok) {
window.location.href = "/auth/login-page"; // Redirect to login page
} else {
alert("Registration failed!");
}
});
}Checks passwords match before sending data.
Creates a JSON payload for the FastAPI
/auth/registerendpoint.Redirects to login page on success.
2. Login Form
Similarly, for login:
const loginForm = document.getElementById("loginForm");
if (loginForm) {
loginForm.addEventListener("submit", async (e) => {
e.preventDefault();
const payload = {
username: username.value,
password: password.value
};
const response = await fetch("/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
if (response.ok) {
const data = await response.json();
document.cookie = `access_token=${data.access_token}; path=/`;
window.location.href = "/todos/todo-page"; // Redirect after login
} else {
alert("Login failed!");
}
});
}Receives JWT token from FastAPI.
Stores token in browser cookies for authenticated requests.
Redirects to the feature page (
/todos/todo-page).
🔑 Important Notes
Delete existing tokens before testing repeatedly:
Go to DevTools → Application → Cookies → Delete
access_tokenPrevents conflicts with old or expired tokens.
The
/todos/todo-pageroute doesn’t exist yet – this is the next step in building our full-stack app.JavaScript ensures the frontend communicates with FastAPI securely and efficiently.
✅ Summary
By adding JavaScript:
Forms now send data to FastAPI endpoints.
Password validation occurs on the client side.
JWT tokens are stored as cookies for authenticated API calls.
The layout template ensures scripts load consistently across pages.
With this setup, the login and registration pages are now fully functional on the frontend, ready to interact with the backend.
Next, we’ll implement the To-Do page, where users can add, edit, and view tasks—completing our full-stack FastAPI application.