In these tutorials, we have seen how to create an API for EMI calculator using FastAPI and how to create a standalone web page that can calculate EMI using Alpine.js. In this tutorial, we are going to combine FastAPI and Alpine.js to create a web application with an Alpine.js frontend.
If you observe the stand alone tutorial we were calculating only the EMI. Other values also can be calculated writing the respective functions in javascript itself. Here we are repurposing the existing FastAPI API and Alpine.js standalone page, we will learn how to combine these technologies to create a web application that offers both backend functionality and frontend interactivity.
Repurposing the Existing FastAPI
The existing FastAPI code consists of the basic setup for creating a FastAPI application. It includes importing the necessary modules and creating an instance of the FastAPI app.
from fastapi import FastAPI
import numpy_financial as npf
app = FastAPI()
To repurpose the existing FastAPI application to serve an HTML file, we need to add additional code that handles static file serving and sets up the root endpoint ("/") to return the index.html file.
First, we import the required modules for serving static files and handling file responses:
from fastapi import FastAPI
from starlette.responses import FileResponse
from fastapi.staticfiles import StaticFiles
import numpy_financial as npf
import numpy as np
Next, we mount a new route ("/static") in our FastAPI app using the app.mount()
method. This route is responsible for serving static files such as CSS, JavaScript, and images. We specify the directory where our static files are located using the directory
parameter. In this example, the static files are stored in the "static" directory.
app.mount("/static", StaticFiles(directory="static"), name="static")
After setting up the static file serving, we define a new endpoint for the root URL ("/") using the @app.get
decorator. We pass the response_class=FileResponse
parameter to specify that the response should be treated as a file response.
Inside the endpoint function, we use the FileResponse
class to create a file response for the "static/index.html" file. This means that when a user visits the root URL of our web application, FastAPI will serve the index.html file located in the "static" directory.
@app.get("/", response_class=FileResponse)
async def read_index():
return FileResponse("static/index.html")
By adding this code, we have repurposed the existing FastAPI application to serve the index.html file and any other static files required by our web application.
In addition to serving the HTML file, we also need to modify the existing API endpoints to return all the values required for the integration with Alpine.js. In the original code, only the calculated EMI value was returned. Now, we include the principal, tenure, rate of interest, and EMI values in the response.
Here's an example of modifying the "/calculate-emi" endpoint:
@app.get("/calculate-emi")
async def get_emi(
principal: float = 100000,
tenure: int = 6,
rate_of_interest: float = 12,
):
emi = npf.pmt(rate_of_interest / 100 / 12, tenure, -principal, when="end")
context = {
"principal": principal,
"tenure": tenure,
"rate_of_interest": rate_of_interest,
"emi": round(emi, 2),
}
return context
Similarly, you need to modify the code of other endpoints ("/calculate-tenure", "/calculate-principal", and "/calculate-rate") to include the necessary values in the response.
After integrating the modifications, the final code will look like this:
from fastapi import FastAPI
from starlette.responses import FileResponse
from fastapi.staticfiles import StaticFiles
import numpy_financial as npf
import numpy as np
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/", response_class=FileResponse)
async def read_index():
return FileResponse("static/index.html")
@app.get("/calculate-emi")
async def get_
emi(
principal: float = 100000,
tenure: int = 6,
rate_of_interest: float = 12,
):
emi = npf.pmt(rate_of_interest / 100 / 12, tenure, -principal, when="end")
context = {
"principal": principal,
"tenure": tenure,
"rate_of_interest": rate_of_interest,
"emi": round(emi, 2),
}
return context
@app.get("/calculate-tenure")
async def get_tenure(
principal: float = 100000,
emi: float = 17200,
rate_of_interest: float = 12,
):
tenure = npf.nper(rate_of_interest / 100 / 12, -emi, principal, when="end")
context = {
"principal": principal,
"emi": emi,
"rate_of_interest": rate_of_interest,
"tenure": np.round(tenure, 0),
}
return context
@app.get("/calculate-principal")
async def get_principal(
tenure: int = 6, emi: float = 17200, rate_of_interest: float = 12
):
principal = npf.pv(rate_of_interest / 100 / 12, tenure, -emi, when="end")
context = {
"tenure": tenure,
"emi": emi,
"rate_of_interest": rate_of_interest,
"principal": np.round(principal, 2),
}
return context
@app.get("/calculate-rate")
async def get_rate(principal: float = 100000, tenure: int = 6, emi: float = 17200):
rate = npf.rate(tenure, -emi, principal, 0, when="end") * 12 * 100
context = {
"principal": principal,
"tenure": tenure,
"emi": emi,
"rate_of_interest": np.round(rate, 0),
}
return context
With these modifications, the FastAPI application can serve the HTML file and provide the necessary values for integration with Alpine.js, enabling the creation of a web application that combines the backend functionality of FastAPI with the frontend interactivity of Alpine.js.
Repurposing the Existing Frontend
In order to repurpose the existing frontend code, we need to make some changes to enhance its functionality and enable communication with the FastAPI backend. To achieve this, we will introduce the use of Axios, a popular JavaScript library for making HTTP requests to APIs. Additionally, we will modify the data structure to align with the naming conventions commonly used in Python and FastAPI.
Existing code
<script>
const app = () => ({
input_data: {
Principal: 100000,
RateOfInterest: 12,
Tenure: 6,
EMI: 0.0,
},
output_data: { EMI: 0, Instalments: [] },
state: { activeButton: null },
TPAY: function () {
const total_payment = this.input_data.EMI * this.input_data.Tenure || 0;
return total_payment.toFixed(2);
},
calculateEMI: function () {
const principal = parseFloat(this.input_data.Principal);
const rateOfInterest = parseFloat(this.input_data.RateOfInterest);
const tenure = parseInt(this.input_data.Tenure);
if (!isNaN(principal) && !isNaN(rateOfInterest) && !isNaN(tenure)) {
const monthlyInterest = rateOfInterest / 12 / 100;
const emi =
(principal *
monthlyInterest *
Math.pow(1 + monthlyInterest, tenure)) /
(Math.pow(1 + monthlyInterest, tenure) - 1);
this.input_data.EMI = emi.toFixed(2);
} else {
this.input_data.EMI = -1;
}
return this.input_data.EMI;
},
resetInputs: function () {
this.input_data.Principal = 100000;
this.input_data.Tenure = 6;
this.input_data.RateOfInterest = 12;
this.calculateEMI();
},
});
</script>
Purpose of Changes
- Introducing Axios: Axios is a widely-used
JavaScript library that simplifies the process of sending asynchronous HTTP requests from the browser. By incorporating Axios into our frontend code, we can easily communicate with the FastAPI backend and retrieve data from the API endpoints.
- Updating Data Structure:
The existing frontend code uses property names like "Principal" and "RateOfInterest," which don't match the naming conventions commonly used in Python and FastAPI. To ensure consistency and improve readability, we will modify the property names to "principal," "rate_of_interest," and so on. This aligns the frontend code with the parameter names expected by the FastAPI backend.
Libraries Used
- Axios:
Axios is a JavaScript library that simplifies the process of making HTTP requests from the browser. It provides an intuitive interface for sending GET requests to API endpoints, handling responses, and handling errors. By incorporating Axios, we can leverage its features to interact with the FastAPI backend seamlessly.
Code Changes
<script>
const app = () => ({
input_data: {
principal: 100000,
rate_of_interest: 12,
tenure: 6,
emi: 0.0,
},
output_data: { tpay: 0, instalments: [] },
state: { activeButton: null },
getData(endpoint) {
axios
.get(endpoint, { params: this.input_data })
.then((response) => {
this.input_data = response.data;
this.output_data.tpay = (
this.input_data.emi * this.input_data.tenure || 0
).toFixed(2);
})
.catch((error) => {
console.error(error);
});
},
resetInputs() {
this.input_data.principal = 100000;
this.input_data.tenure = 6;
this.input_data.rate_of_interest = 12;
this.getData("/calculate-emi");
},
});
</script>
Explanation
- The
getData
Function:
The getData
function is added to handle the API call to the
backend. It utilizes Axios to send a GET request to the specified endpoint
, passing the input_data
as parameters. Upon receiving the response from the backend, the data is assigned to the input_data
object, and the output_data.tpay
property is calculated based on the updated input_data
.
- Updating the
resetInputs
Function:
The resetInputs
function is updated to match the modified property names within the input_data
object. Additionally, it now calls the getData
function with the "/calculate-emi" endpoint after resetting the inputs. This ensures that the initial data is fetched from the backend API when the inputs are reset.
By incorporating these changes and utilizing Axios, the repurposed frontend code enables seamless communication between the frontend and the FastAPI backend, allowing the frontend to make API calls and receive responses asynchronously.
Adding Additional Buttons
To add the additional buttons required for performing different calculations, follow these steps:
- Locate the existing HTML code for the reset button inside the
<thead>
section:
<div class="reset-container">
<button class="reset-button" x-on:click="resetInputs">Reset</button>
</div>
- Below the existing reset button code, add the following HTML code for the additional buttons:
<div class="reset-container">
<button class="reset-button" x-on:click="getData('/calculate-emi')">
Calculate EMI
</button>
<button class="reset-button" x-on:click="getData('/calculate-principal')">
Calculate Principal
</button>
<button class="reset-button" x-on:click="getData('/calculate-rate')">
Calculate Rate
</button>
<button class="reset-button" x-on:click="getData('/calculate-tenure')">
Calculate Tenure
</button>
<button class="reset-button" x-on:click="resetInputs">Reset</button>
</div>
These new buttons have the reset-button
class for styling purposes and use the x-on:click
directive to bind the corresponding functions in the Alpine.js code.
By adding these additional buttons, you have enabled the ability to perform different calculations using the EMI calculator. Each button is associated with a specific API endpoint and triggers the corresponding function in the Alpine.js code when clicked. The API endpoint is passed as a parameter to the getData
function, which will make a request to the backend API and update the input and output data accordingly.
Setting Up the Application Directory Structure
To organize our application files, we will create a directory named "emic" and structure it as follows:
- Create the "emic" directory:
mkdir emic
- Repurpose the existing
pyproject.toml
file:
Copy the existing pyproject.toml
file to the "emic" directory:
cp pyproject.toml emic/
- Create the "app" directory:
mkdir emic/app
- Copy the "main.py" file and make the necessary code changes:
Copy the existing main.py
file to the "emic/app" directory:
cp main.py emic/app/
- Create the "static" directory:
mkdir emic/static
- Copy the "index.html" file and make the necessary changes:
Copy the existing index.html
file to the "emic/static" directory:
cp index.html emic/static/
After performing these steps, the application directory structure will look like this:
emic
├── app
│ └── main.py
├── pyproject.toml
└── static
└── index.html
This directory structure ensures that the necessary files are organized in separate folders for better management of the application's backend, frontend, and static files.
Running the Application
To run the application, follow these steps:
Open your terminal and navigate to the root directory of the "emic" application.
Execute the following command to start the application server:
poetry run uvicorn app.main:app --reload
This command uses Poetry to run the Uvicorn server and specifies the entry point of the application as app.main:app
. The --reload
flag enables automatic reloading of the server whenever code changes are made.
- Once the server is running, open your web browser and visit http://localhost:8000. This will take you to the web page of the EMI calculator application.
- You can also access the API documentation by visiting http://localhost:8000/docs. This page provides detailed information about the API endpoints and allows you to interact with them.
By following these steps, you will be able to run the EMI calculator application locally. The web page will display the calculator interface, and you can use it to perform EMI calculations. Additionally, the API documentation will provide you with the necessary details to understand and interact with the application's backend API.
Top comments (0)