Build a New Year resolution generator with ChatGPT

Jan 1, 2025

Almost everyone makes New Year resolutions at the beginning or end of the year, or at least rethinks goals and objectives. For this article, considering the time of year, we’ll do something “different,” something simple yet practical. We’re going to create a small application that allows us to generate New Year resolutions for the upcoming year using Artificial Intelligence.

We’ll use OpenAI’s ChatGPT to send a request to its API and receive a set number of resolutions for the new year as a response. The main focus of this article, beyond its utility and functionality, is to demonstrate how to use ChatGPT via its API and leverage system prompts to obtain personalized and structured responses that can be consumed by the client-side application.

The idea is straightforward: a web page with a text box and a button. The text box will allow the user to input one or more words about the topic they’d like resolutions for—for example, “Software Development,” “Sports,” “Health,” “Leisure and Entertainment,” etc. Upon pressing the button, a request will be sent to the ChatGPT API with the entered text, and the generated resolutions will be displayed.

To achieve this, we’ll need an OpenAI account, which will allow us to obtain an API key to use the ChatGPT API. Once we have the API key, we’ll be able to send requests to the ChatGPT API to generate our resolutions. We’ll use HTML, CSS, JavaScript, and Docker to create and launch the application. In the GitHub repository, you can find the complete source code for the application. Here’s the HTML file with the structure of our page:

    
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>New Year's Resolutions Generator 2025</title>
      <link rel="stylesheet" href="styles.css">
    </head>
    <body>
      <header>
        <h1>🎉 Resolutions for 2025 🎉</h1>
        <p>Generate motivating goals for the New Year</p>
      </header>
      <main>
        <div class="form-container">
          <div class="input-container">
            <input type="text" id="topic" placeholder="Enter a topic (e.g. Technology, Health)" />
            <button onclick="generateGoals()">Generate Resolutions</button>
          </div>
          <div id="output" class="results"></div>
        </div>
      </main>
      <footer>
        <p>💡 Start the New Year with inspiration 💡</p>
        <p><a href="https://betazeta.dev" target="_blank" class="beta-link">By BetaZetaDev</a></p>
      </footer>
      <script src="script.js"></script>
    </body>
    </html>
    

The generateGoals() method is located in the client’s JavaScript file within the frontend folder and is responsible for making the request to the server (backend), among other tasks:


    const response = await fetch("http://localhost:3000/generate", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ topic }),
    });
    

Thanks to the accompanying CSS, we apply a simple design using cards that simulate handwritten notes, giving it a look that fits the context of our application. Additionally, we store the OpenAI API key in a .env file and use two JavaScript files: one to handle the application’s logic and another for the server-side functionality.


    require("dotenv").config();
    const express = require("express");
    const cors = require("cors");
    const bodyParser = require("body-parser");
    const axios = require("axios");
    
    const app = express();
    const port = 3000;
    
    app.use(cors());
    app.use(bodyParser.json());
    
    const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
    
    app.post("/generate", async (req, res) => {
      const { topic } = req.body;
    
      if (!topic) {
        return res.status(400).send({ error: "The topic is required." });
      }
    
      try {
        const response = await axios.post(
          "https://api.openai.com/v1/chat/completions",
          {
            model: "gpt-4o",
            messages: [
              {
                role: "system",
                content: `You are an assistant that must always return valid JSON data. The responses must be written in Spanish. Generate exactly 5 resolutions for the given topic. Follow this strict JSON format:
                          {
                            "goals": [
                              "Resolution 1",
                              "Resolution 2",
                              "Resolution 3",
                              "Resolution 4",
                              "Resolution 5"
                            ]
                          }
                          Do not include any additional text, comments, or explanations. Only return valid JSON.`
              },
              {
                role: "user",
                content: `Generate resolutions for the topic: ${topic}`
              }
            ],
            temperature: 0.7,
          },
          {
            headers: {
              "Content-Type": "application/json",
              Authorization: `Bearer ${OPENAI_API_KEY}`,
            },
          }
        );
    
        // Retrieve the response content
        let content = response.data.choices[0].message.content.trim();
    
        // Remove code block delimiters (```json and ```)
        if (content.startsWith("```json")) {
          content = content.slice(7).trim(); // Remove "```json"
        }
        if (content.endsWith("```")) {
          content = content.slice(0, -3).trim(); // Remove "```"
        }
    
        // Try to parse the content as JSON
        try {
          const parsed = JSON.parse(content);
    
          if (!parsed.goals || !Array.isArray(parsed.goals) || parsed.goals.length !== 5) {
            throw new Error("The JSON does not have the expected format.");
          }
    
          // Format and send the response
          res.send({
            title: `New Year's Resolutions for ${topic}`,
            goals: parsed.goals,
          });
        } catch (parseError) {
          console.error("Error parsing JSON:", parseError.message, content);
          res.status(500).send({ error: "The response is not valid JSON." });
        }
      } catch (error) {
        console.error("Error calling the OpenAI API:", error.response?.data || error.message);
        res.status(500).send({ error: "There was an issue generating the resolutions." });
      }
    });
    
    app.listen(port, () => {
      console.log(`Server running at http://localhost:${port}`);
    });
    

The relevant part of our application runs on the server and is responsible for managing requests to ChatGPT. As mentioned earlier, it utilizes the system prompt, which specifies the requirements that ChatGPT’s response must meet. We also use this prompt to define the structure of the JSON we expect to receive, enabling us to parse the response correctly once received and display the resolutions on the page. It is important to note that in one of ChatGPT API’s updates, they introduced Structured Outputs. The issue with doing it as in our example is that ChatGPT might not strictly adhere to the requirements specified in the system prompt, potentially returning a response in a different format. Thus, it is crucial to keep this in mind. To ensure the response is correct and follows the expected format without surprises, we should use Structured Outputs.

Another detail to consider is that, to securely manage API keys in general and avoid exposing them in the source code, it is ideal to use a backend server. The backend handles the requests and returns the results. This is a common practice as it separates the business logic from the frontend and keeps the API keys secure, preventing unauthorized access and misuse.

The project structure would look like this:


    .
    ├── `backend`
    │   ├── .env
    │   ├── package.json
    │   └── server.js
    ├── docker-compose.yml
    ├── Dockerfile.`backend`
    ├── Dockerfile.`frontend`
    └── `frontend`
        ├── index.html
        ├── script.js
        └── styles.css

To run the application, we will use Docker, already set up to launch one container for the frontend and another for the backend. We will use a docker-compose.yml file, along with two Dockerfiles. You can view all the files and their contents in the repository.

Once all the necessary files are in place, we can build and run the application using the following command:


    docker compose up --build -d
  

This will launch both Docker containers in the background, frontend and backend, which will communicate with each other. Requests from the frontend are sent to the backend, which handles the API call and processes the response before sending it back to the client. Once the containers are running, you can access the application from your web browser at http://localhost:8080 and start generating your New Year resolutions.

This has been a simple yet practical example, demonstrating how to use artificial intelligence in web applications, along with the use of system prompts to obtain personalized responses from ChatGPT. We also showcased how to separate business logic from the frontend by using a backend server in a straightforward way, without the need for complex frameworks or additional libraries beyond what is strictly necessary. It is worth noting that the frontend container in Docker is not actually required, as the application can be run directly by opening the index.html file in a browser. However, Docker was used in this example to practice containerization.

Lastly, we wish you a very Happy New Year! May all the resolutions you set come to fruition, and may you achieve great success in your personal and professional endeavors.

Happy New Year and Happy Coding!

Related posts

That may interest you