
We will be creating a web app, which will help the user input a theme. And the AI will generate a haiku poem based on the theme. A haiku is a type of poem that originated in Japan, which has seventeen syllables.
A web app consists of a Front End and Back End. Everything and anything the user is able to see, the User Interface (UI), the input box for the theme, the submit button, and the generated poem is Front End.
Back End is also the process that happens behind the scenes that is hidden from the end user. Like connecting to the Gemini API, receiving the theme in an endpoint, and generating a response from the AI, returning it back to the Front End.
For our Front End we will be using plain old HTML, CSS, and Javascript. No fancy front-end frameworks like React or Angular.
The Back End will be built using Python as the language and Flask as the web framework and integrate Google's Gemini LLM for AI capabilities.
When developing web apps, there are two architectural approaches:
Monolithic Architecture – The Front End and Back End are combined in a single codebase, with the Back End serving the Front End.
Microservices Architecture – The Front End and Back End are separate, communicating via APIs.
Here we will look at creating the web app using Monolithic Architecture.
We will start by creating a project folder and opening it in our editor. I am using VS Code; you can go ahead and use the editor of your choice.

I also have the integrated terminal open.
You can open the terminal as follows.

We will start in the terminal by creating a Python virtual environment. A Python virtual environment is an isolated environment that allows you to manage project-specific dependencies, ensuring that they don't interfere with other projects or system-wide packages.
Run the following command in the terminal to create an environment.
python3 -m venv env
Then to activate the environment, run the following command.
source env/bin/activate
Done, now we will move on to installing the needed packages/dependencies. We will be using pip, which is a package manager for Python that allows you to install, update, and manage libraries and dependencies from the Python Package Index (PyPI).
In the terminal, run the following command to install Flask and the Gemini API library.
pip install flask google-generativeai
Once the packages are installed. Run the below command to keep track of the installed packages.
pip freeze > requirements.txt
Freezing requirements.txt
locks the versions of the dependencies used in a project, ensuring consistency across different environments and preventing issues caused by version mismatches when others install the dependencies.

The requirements.txt file will look like this. We can see our Flask and Gemini libraries are listed in there.
Great, now let's start some coding. First create a file named main.py.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
The code initializes a basic Flask app by importing the Flask
class and creating an app instance with app = Flask(__name__)
. A route is defined for the root URL (/
) using the @app.route('/')
decorator, where the hello_world()
function returns the message "Hello, World!" when accessed. Lastly, app.run(debug=True)
starts the server in debug mode, allowing the app to run locally for testing purposes.
Let's run the app and visit the root URL to get an idea of the workings.
To run the app, in the terminal execute the following command.
python main.py
We can see our server has now started.

We will now visit the URL http://127.0.0.1:5000 in the browser. Open your browser and in the search bar enter the above URL.

We can see the Hello, World! printed.
In our code, we have defined a route for the / endpoint. And added a corresponding function named hello_world. The idea is that, when a user visits the URL, this hello_world function will execute and will return the text Hello, World! to the user's browser.
Now that we have an idea of the backend and how to run the web app server. Let's move on to the frontend to get the setup done.
Create a folder named templates. And add a file named index.html inside the folder.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AI Haiku Poet Gemini</title>
</head>
<body>
<main class="container">
<h4>AI Haiku Poet Gemini</h4>
<form id="haiku-form">
<div class="grid" style="display: flex; align-items: center;">
<input type="text" id="theme" name="theme" placeholder="Enter Haiku Theme" required
autocomplete="off" style="width: 70%;">
<button type="submit" style="margin-left: 10px; width: 30%">Submit</button>
</div>
</form>
<div id="haiku-result"></div>
</main>
</body>
</html>
Our index.html file is pretty simple. It has the base structure, with a form containing the input box for the user to enter the theme. And a submit button, and a div tag with an id of haiku-result where we will display the generated poem.
Now back to the backend. Instead of returning Hello, World!, we will return our index.html file as a response.

Our code will now look like this. We have imported the render_template function and are using it to serve the index.html file. Flask will automatically look for the file in the templates folder.
Now if we refresh our web app in the browser. Instead of Hello, World! We will see the below User Interface (UI).

The UI is looking pretty not pretty. So let's make it look pretty by using CSS. Create a new folder named static, and inside that folder create another folder named css. Inside the css folder, create a file named styles.css. We will have our styling coded in the styles.css file.
.container {
padding: 20px;
}
.grid {
display: flex;
align-items: center;
}
input {
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
background-color: #007BFF; /* Blue color */
color: white;
border: none;
border-radius: 5px;
transition: background-color 0.3s, transform 0.2s;
}
button:hover {
background-color: #0056b3; /* Darker blue on hover */
transform: scale(1.05); /* Slight zoom effect on hover */
}
button:active {
background-color: #004085; /* Even darker blue on click */
}
button:focus {
outline: none; /* Remove outline on focus */
}
Via styles.css we can target HTML elements in the index.html file to make it look prettier. CSS can also be written inline. But it is good practice to keep it in a separate file. So HTML for the structure, and CSS for the styling.
Our web app now looks like this

But it is just a plain frontend UI that has a heading, an input field, and a submit button. To make the site do something, we need Javascript. What should happen when the Submit button is clicked?
We want our frontend to reach out to our backend route—let's say /generate-haiku. And pass it the user-entered input theme. For actions involved in a website, we need Javascript.
Before creating our Javascript file, let's hop into the backend to create the /generate-haiku route.
We will first install another package named python-dotenv. When dealing with most APIs (Application Programming Interfaces), they provide an API key that we can use to talk to their server.
We store the API keys and other secrets in a file named .env. To load the environment secrets we will be using the dotenv package.
Note: If you are using git, before adding the .env file, please add a .gitignore file and put .env inside it. So that your secrets are not pushed to GitHub.
In the terminal, run the pip installation command.
pip install python-dotenv
After the installation, run the pip freeze command again to add it to the requirements.txt file.
pip freeze > requirements.txt
Now we will create a .env file and add the environment variable
GEMINI_API_KEY=Your API Key
Where to get the API key? Visit Google AI Studio. And click Get API Key.

And then provide the consent of use to proceed.

And done! Now we have finished the sign-up.

Click Create API Key to generate a key for your project. And remember, it is never to be shared publicly. Keep your API Key a secret.

Copy and paste the key to your .env file.

Now back to main.py. We will start by importing the Gemini library and then initialising the model.
from flask import Flask, render_template
import google.generativeai as genai
from dotenv import load_dotenv
import os
load_dotenv()
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
genai.configure(api_key=GEMINI_API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash")
app = Flask(__name__)
Now our main.py looks like this. Added an import for genai and dotenv. We are initialising load_dotenv() to have our environment variable loaded. Then we are storing our secret in a variable of the same name. And initialising the genai with configuration and passing in the key. Then in the next step, we are initialising the GenerativeModel gemini-1.5-flash. We have options of using different LLMs (Large Language Model). For our project, let's stick to using Gemini 1.5 Flash.
Now the Gemini setup is done. Let's create the route, /generate-haiku.
from flask import Flask, render_template, jsonify, request
app = Flask(__name__)
@app.route('/')
def hello_world():
return render_template('index.html')
@app.route("/generate-haiku", methods=['POST'])
def generate_haiku():
data = request.get_json() # Get JSON data from the request
theme = data.get('theme', None) # Access the 'theme' key from the JSON data
response = model.generate_content(f"You are an expert haiku poet who is in love with nature. Write me a haiku about {theme}")
return jsonify({"haiku": response.text})
To the first line, where we are importing Flask, we now import a few other functions, namely jsonify, which helps in returning responses in JSON format. Most APIs practice returning data in JSON format. The request function is used to extract the theme field passed from the frontend form submission.
Here, we can see there is a difference in the way @app.route is created. We are passing in a parameter called methods and the value POST. By default, Flask routes are GET methods. We have different HTTP Methods like GET, PUT, POST, and DELETE for different functionalities.
We can check if our API is working correctly by using an API client like Postman or Thunder Client. For now let's use Thunder Client. Inside VS Code extensions, install the Thunder Client extension.

To launch the Thunder Client, on Mac press Cmd + Shift + P (Windows Ctrl + Shift + P) and search for Thunder Client and select Thunder Client: New Request.

It will open the API client for us to test our routes.

Now change from GET to POST method and replace the example URL with our app URL, which is http://localhost:5000/generate-haiku. Then from Query, move to Body.
Add the json block
{
"theme": "dewdrop"
}
Note: 127.0.0.1:5000, which we used earlier, is replaceable with localhost:5000.
Let's click the Send button and see what happens.

We can see the response in the right panel, with a status code of 200. There are many different HTTP status codes available; the most common one we would have seen is 404, not found. A status code of 200 means all is well and good.
Below in the terminal we can also see a log of the request; the method was POST, the route endpoint was /generate-haiku, and the response is of status code 200.
We have our backend now working. Let's now dive back into frontend.
Inside the static folder, where we have our css folder, along with the css folder, create another folder named js. And inside that, create a new file named script.js.
document.getElementById('haiku-form').addEventListener('submit', function (event) {
event.preventDefault(); // Prevent form submission
// Get the theme value
const theme = document.getElementById('theme').value;
fetch('/generate-haiku', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ theme: theme })
})
.then(response => response.json())
.then(data => {
// Display the Haiku in the result div
document.getElementById('haiku-result').innerHTML = `<p>${data.haiku}</p>`;
})
.catch(error => {
console.error('Error:', error);
});
});
Here in our script.js file, we are listening for the HTML element form with an id—haiku-form. When the form is submitted, the above code runs. First it extracts the user-entered theme. And then it does a backend API call using the fetch function to the route we just tested. The received response from the backend is then injected into the div with id haiku-result, within a paragraph tag.
We need to now import the script.js file inside our index.html. Add the script tag code to the bottom of the index.html file. Above the closing of the body tag and below the closing main tag.
</main>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>
Now let's restart our web app by running the command.
python main.py
Now revisit the url - http://127.0.0.1:5000 and enter a theme and submit the form.

And that's it, now we have a frontend which has the above elements. When a user enters a theme of their choice, and clicks submit button. The frontend hits the backend api of route /generate-haiku, and uses Gemini LLM to generate a haiku based on the user entered theme. And it is send back to the frontend, where in it is displayed back to the user.
Note: You can find the full source code at the GitHub Repo.

Write a comment ...