DEV Community

Prasanth
Prasanth

Posted on

Building a Weather Application with Spring Boot and OpenWeatherMap API

  • Build a simple Weather App using Spring Boot, the OpenWeatherMap API, and Thymeleaf as the front-end template engine. Users can enter a city name, and the application will fetch and display current weather details.
  1. To get Api and APIKeys from “https://home.openweathermap.org/“
  2. After signup/login
  3. Get APIKey from “https://home.openweathermap.org/api_keys“
  4. Get APIURL: “https://api.openweathermap.org/data/2.5/weather?q={city name}&appid={API key}“

example:-

https://api.openweathermap.org/data/2.5/weather?q=Thiruvallur&appid=93c30d18b6d15d780cc94b291984ad88

Weather Response:

  • you get response in JSON data format

Note:-

  • JSON response came from API call , now we need return the response to now created project.

  • Now we want to read to json data ,but we do not know how to write program , Don worry we need to add dependencies , that one is Jackason DataBind (Jackson Databind is a Java library that allows you to easily convert (bind) JSON data to Java objects and vice versa.)

Main Use of Jackson Databind:-

  • Read JSON => Java Object (Deserialization)
  • Write Java Object => JSON (Serialization)

Link:-

https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind/2.19.2

  • copy dependencies and paste to you pom.xml

  • one website (you app or website) can get data from another website (open weather) using public ApI, This is Apis work.

1. Overall Project Flow

Request-Response Flow:-

User => Enters City in Form (HTML) => Controller => Service => OpenWeatherMap API
<= Response => Data sent to View via Model => Weather Displayed on Web Page

2.WeatherResponse.java (Model Layer)

Purpose: maps the Api Response to java objects using jackson.

Contains:

  • name = city name
  • main = temperature, humidity, etc.
  • weather[] = an array of weather descriptions @JsonIgnoreProperties(ignoreUnknown = true) , ensure that any unnecesary Json fields.
package com.example.demo;



import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
// we need particular  JSON data another data ignore

@JsonIgnoreProperties(ignoreUnknown = true)
public class WeatherResponse {
    // only need those response  from like https://api.openweathermap.org/data/2.5/weather?q=Thiruvallur&appid=93c30d18b6d15d780cc94b291984ad88

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Main getMain() {
        return main;
    }
    public void setMain(Main main) {
        this.main = main;
    }
    public Weather[] getWeather() {
        return weather;
    }
    public void setWeather(Weather[] weather) {
        this.weather = weather;
    }
    private  String name ;
    private  Main main ;  // Main class
    private  Weather[] weather;  // weather class for array 


}
// we want to use this response this class only that why private (like one time usage)  and that why we create  above the class's in same class
class  Main 
{
    private double temp;
    private int humidity;
    private  int  sea_level;
    private   int pressure ;
    public double getTemp() {
        return temp;
    }
    public void setTemp(double temp) {
        this.temp = temp;
    }

    public int getHumidity() {
        return humidity;
    }
    public void setHumidity(int humidity) {
        this.humidity = humidity;
    }
    public int getSea_level() {
        return sea_level;
    }
    public void setSea_level(int sea_level) {
        this.sea_level = sea_level;
    }
    public int getPressure() {
        return pressure;
    }
    public void setPressure(int pressure) {
        this.pressure = pressure;
    }
    public int getGrnd_level() {
        return grnd_level;
    }
    public void setGrnd_level(int grnd_level) {
        this.grnd_level = grnd_level;
    }
    private int grnd_level; 


}
class  Weather
{
    private  String  description;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    } 

}
// now we  done json object  convert  to java object


Enter fullscreen mode Exit fullscreen mode

3.WeatherController.java (Controller Layer)

  • Controller handles the web API request.
  • Calls the service to get weather data.
  • Sends the data to the view using Model. model.addAttribute("weather", weatherResponse);
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class WeatherController {   
    @Autowired
    private  WeatherService weatherService; 
    @GetMapping("/")
    public String  display()
    {

        return "weather";

    }

    // now to create src/main/resource create weather.html under templates 
    @GetMapping("/weather")
    public String show_weather_details(@RequestParam String  city , Model model)
    {
        // @RequestParam  get data from the URL 
        /*    Example: If URL is http://localhost:8080/weather?city=Chennai
         *  Then city will have the value "Chennai"
      It reads the ?city=Chennai part from the URL.
         * Model model
         * it is used  to store data that you want to send to the HTML(Thymleaf) page
         * like Model is used to store and pass data from the controller to the view (HTML
         * 
         */

        /* now  Below  this thing  , to implement  to code to service class. 
         * Api key  ,we already defined application  properties.
         * City
         * APIURL
         * weather responce 
         * 
         */
        WeatherResponse weatherResponse =   weatherService.getWeather(city); // 7 
    model.addAttribute("weather", weatherResponse);
    return  "weather" ;
    }
}


Enter fullscreen mode Exit fullscreen mode

** 4. WeatherService.java (Service Layer)**

  • Purpose: Make the Rest Api call to OpenWeathermap using RestTemplate.

Uses:
return restTemplate.getForObject(APIURL, WeatherResponse.class, city, apiKey);

  • Uses @Value("${weather.api.key}") to inject the API key from application.properties
package com.example.demo;



import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class WeatherService {

//   Api key  ,we already defined application  properties   
    @Value("${weather.api.key}") // get the value in application properties
    private String  apiKey;
    //   * APIURL
    private final String APIURL = "https://api.openweathermap.org/data/2.5/weather?q={city}&appid={apiKey}";


    public WeatherResponse getWeather  (String city)
    {
    RestTemplate restTemplate = new RestTemplate();
 return restTemplate.getForObject(APIURL,WeatherResponse.class,city,apiKey); // 5
    //   * weather response 
    //How to  API Response data (Like weather data) get into to   browser page (htmlpage ), That one is  RestTemplate . 
   // Now we return APIURL,WeatherResponce.class(pojo class) ,city,apiKey with used to RestTemplate , to WeatherResponce class  .
    // now we connect to controller and response class  go to  controller class
    }
}


Enter fullscreen mode Exit fullscreen mode

5.Apllication properties
weather.api.key=YOUR_API_KEY_HERE

6.weather.html (View Layer - Thymeleaf)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Weather App</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background: linear-gradient(to right, #83a4d4, #b6fbff);
            color: #333;
            padding: 20px;
            text-align: center;
        }

        .weather-box {
            background-color: white;
            border-radius: 10px;
            padding: 30px;
            max-width: 400px;
            margin: 30px auto;
            box-shadow: 0 0 10px rgba(0,0,0,0.2);
        }

        input[type="text"] {
            padding: 10px;
            width: 60%;
            border: 1px solid #ccc;
            border-radius: 5px;
            margin-top: 20px;
        }

        button {
            padding: 10px 20px;
            border: none;
            background-color: #007BFF;
            color: white;
            border-radius: 5px;
            margin-left: 10px;
            cursor: pointer;
        }

        button:hover {
            background-color: #0056b3;
        }

        .info {
            margin-top: 20px;
        }

        .error {
            color: red;
        }
    </style>
</head>
<body>

    <h1>🌦 Weather Application</h1>

    <form action="/weather" method="get">
        <input type="text" name="city" placeholder="Enter city name" required>
        <button type="submit">Search</button>
    </form>

    <!-- Weather Result Section -->
    <div class="weather-box" th:if="${weather != null}">
        <h2 th:text="${weather.name}">City Name</h2>
        <p class="info">🌡 Temperature: <span th:text="${weather.main.temp}"></span> °C</p>
        <p class="info">💧 Humidity: <span th:text="${weather.main.humidity}"></span>%</p>
        <p class="info">🌤 Condition: <span th:text="${weather.weather[0].description}"></span></p>
    </div>

    <!-- Show message if weather not found or city not entered -->
    <div th:if="${weather == null}">
        <p class="error">No weather data available. Please enter a valid city.</p>
    </div>

</body>
</html>


  <!-- when you submit  that button it trigger the  weather  action ,  now back to controller class -->

Enter fullscreen mode Exit fullscreen mode

Final Results:-

Top comments (0)