Hava Durumu Web Uygulaması: Adım Adım OpenWeather API Entegrasyonu (fetch ile)

Projeyle Öğrenme

Hava Durumu Web Uygulaması: Adım Adım OpenWeather API Entegrasyonu (fetch ile)

Projeyle Öğrenme
11 dk okuma süresi
Bu rehberde, OpenWeather API ile tarayıcı tarafında fetch kullanarak çalışan bir hava durumu web uygulamasını adım adım kuruyoruz. Endpoint seçimi (Current Weather vs One Call), units/lang parametreleri, sağlam hata yakalama, XSS’e daha güvenli DOM render yaklaşımı, paralel istek (Promise.all) ve API anahtarını üretimde korumaya yönelik pratik seçenekler ele alınıyor.
Hava Durumu Web Uygulaması: Adım Adım OpenWeather API Entegrasyonu (fetch ile)

Bu projede, OpenWeather API ile veri çekip tarayıcıda fetch kullanarak çalışan basit bir hava durumu uygulaması geliştireceğiz. Amaç: doğru endpoint’i seçmek, units/lang ayarlarını yapmak, hataları doğru yakalamak ve üretimde API anahtarını daha güvenli tutmak için uygulanabilir bir yaklaşım kurmak.

Bu projede ne yapacağız?

Bu yazı, proje tabanlı öğrenme yaklaşımıyla küçük ama gerçekçi bir hava durumu web uygulaması geliştirmenize yardımcı olur. Hedefimiz:

  • Kullanıcının girdiği şehre (veya koordinata) göre hava durumunu API’den almak
  • Tarayıcıda fetch ile veri çekme (async/await) ve hata yakalamayı doğru kurmak
  • units (Celsius/Fahrenheit) ve lang (Türkçe açıklama) gibi pratik parametreleri kullanmak
  • İsteğe bağlı olarak birden çok isteği Promise.all ile paralel çalıştırmak
  • Üretimde API anahtarını korumak için uygulanabilir bir yol haritası çizmek

Not: OpenWeather ürün/plan detayları ve bazı uç noktaların erişim koşulları zaman içinde değişebilir. En güncel gereksinimler için resmi dokümantasyonu kontrol edin: https://openweathermap.org/api ve One Call 3.x sayfası: https://openweathermap.org/api/one-call-3.


Ön koşullar (5 dakika)

  • Temel HTML/CSS ve JavaScript bilgisi
  • Bir metin editörü (VS Code gibi) ve tarayıcı (Chrome/Firefox)
  • OpenWeather hesabı ve API anahtarı (appid)

Güvenlik notu: Sadece öğrenme amaçlı basit demolar için anahtarı tarayıcı kodunda kullanmak mümkün olsa da, üretimde anahtarı istemci koduna gömmek risklidir. Aşağıda bunun için daha güvenli bir seçenek de anlatılıyor.


1) OpenWeather API anahtarını alma ve temel parametreler

OpenWeather uç noktalarını çağırmak için genellikle appid (API anahtarı) gerekir. Ayrıca yanıtın formatını/yerelleştirmesini kontrol eden parametreler sık kullanılır:

  • appid: API anahtarınız
  • units: metric/imperial gibi birim seçimi (örn. sıcaklık birimi)
  • lang: Açıklama metinlerinin dili (örn. tr)

Bu parametreler ve uç nokta ayrıntıları OpenWeather dokümantasyonunda açıklanır. Bazı özellikler veya sürümler belirli planlara bağlı olabilir; bu nedenle kendi hesabınız üzerinden plan/erişim durumunu doğrulamanız önemlidir (OpenWeather API).


2) Doğru endpoint’i seçin: Current Weather mı, One Call mı?

Bu projeyi iki yolla yapabilirsiniz:

  • Current Weather: Tek bir konum için anlık hava durumu (genelde daha basit başlangıç)
  • One Call (3.x): Tek istekte current, hourly, daily, alerts gibi birleşik veriler (daha kapsamlı)

OpenWeather, One Call API’nin bir istekte birden çok veri bölümünü döndürebildiğini belirtir (OpenWeather API; One Call 3.x). Ancak erişim koşulları planlara göre farklılaşabileceğinden, eğitim içeriğinde kullandığınız sürümü netleştirmeniz iyi olur.

Kısa karşılaştırma tablosu

İhtiyaç Öneri Neden
En hızlı MVP (minimum çalışan ürün) Current Weather Kurulumu basit; tek çağrı ve az veri
Günlük/saatlik tahmin, uyarılar One Call 3.x Veriler tek JSON içinde birleşik gelebilir
Plan/erişim değişikliklerine karşı temkinli içerik Dokümantasyona referans + hesap kontrol notu Erişim/plan ayrıntıları değişebilir

3) Basit arayüzü kurun (HTML)

Minimal bir arayüz yeterli: şehir girişi, birim seçimi, buton, sonuç alanı. Aşağıdaki iskeleti kendi projenize uyarlayabilirsiniz:

index.html (örnek iskelet)
<div>
  <h1>Hava Durumu</h1>
  <label for="cityInput">Şehir</label>
  <input id="cityInput" placeholder="Örn: İstanbul" />
  <label for="unitSelect">Birim</label>
  <select id="unitSelect">
    <option value="metric" selected>Celsius (metric)</option>
    <option value="imperial">Fahrenheit (imperial)</option>
  </select>
  <button id="searchBtn">Getir</button>
  <div id="result"></div>
</div>

Bu örnekte yalnızca yapı var. Stil vermek isterseniz ayrı bir CSS dosyası ekleyebilirsiniz.


4) fetch ile veri çekme: sağlam istek ve hata yakalama

Fetch API tarayıcıda HTTP istekleri yapmak için modern bir yöntemdir. fetch() bir Promise döndürür; JSON gövdesi için response.json() çağrılır. Ayrıca HTTP hataları (404/401 gibi) her zaman “network error” üretmez; bu yüzden response.ok kontrolü önemlidir (MDN: Using the Fetch API).

İki tip hata görmeniz normaldir:

  • Network hatası: İnternet yok, DNS/SSL problemi, CORS engeli vb. durumlarda fetch genellikle bir TypeError ile reject olur (try/catch’e düşer).
  • HTTP hatası: Sunucu yanıt verir ama 401/404/429 gibi durum kodu döner. Bu durumda fetch resolve olur; sizin response.ok ile hatayı “aktif” etmeniz gerekir.

Current Weather ile örnek (şehir adına göre)

Aşağıdaki örnek, şehir adını kullanarak Current Weather endpoint’ine istek atma fikrini gösterir. Örnek endpoint yolu /data/2.5/weather ve parametreler için resmi dokümantasyonu baz alın (OpenWeather API).

app.js (örnek)
const API_KEY = "YOUR_API_KEY";

async function getCurrentWeatherByCity(city, units = "metric") {
  const url = new URL("https://api.openweathermap.org/data/2.5/weather");
  url.searchParams.set("q", city);
  url.searchParams.set("appid", API_KEY);
  url.searchParams.set("units", units); // metric => Celsius, imperial => Fahrenheit
  url.searchParams.set("lang", "tr");

  const response = await fetch(url.toString());
  if (!response.ok) {
    throw new Error("HTTP " + response.status);
  }
  return await response.json();
}

Sonucu ekrana basma (XSS’e daha güvenli render)

Tarayıcıda string birleştirip innerHTML ile basmak, kontrolsüz içeriklerde XSS riskini artırabilir. Bu demo için daha güvenli yaklaşım: DOM elemanları oluşturup textContent ile doldurmak.

function renderCurrentWeather(data, units = "metric") {
  const result = document.getElementById("result");
  result.replaceChildren(); // içerik temizle

  const name = data?.name ?? "";
  const temp = data?.main?.temp;
  const desc = data?.weather?.[0]?.description ?? "";
  const unitSymbol = units === "imperial" ? "°F" : "°C";

  const titleEl = document.createElement("p");
  const strongEl = document.createElement("strong");
  strongEl.textContent = name;
  titleEl.appendChild(strongEl);

  const tempEl = document.createElement("p");
  tempEl.textContent = "Sıcaklık: " + String(temp) + " " + unitSymbol;

  const descEl = document.createElement("p");
  descEl.textContent = "Durum: " + desc;

  result.append(titleEl, tempEl, descEl);
}

Buton olayını bağlayın (units seçimiyle)

document.getElementById("searchBtn").addEventListener("click", async () => {
  const city = document.getElementById("cityInput").value.trim();
  const units = document.getElementById("unitSelect").value;
  if (!city) return;

  try {
    const data = await getCurrentWeatherByCity(city, units);
    renderCurrentWeather(data, units);
  } catch (e) {
    const result = document.getElementById("result");
    result.replaceChildren();
    const msg = document.createElement("p");
    msg.textContent = "Veri alınamadı. Lütfen tekrar deneyin.";
    result.appendChild(msg);
  }
});

Bu akış, MDN’nin vurguladığı temel prensiplerle uyumludur: response.ok kontrolü, JSON parse ve hata yakalama (MDN Fetch).


5) One Call 3.x ile genişletme (koordinatla)

Daha zengin bir deneyim için One Call 3.x düşünebilirsiniz. One Call 3.x örnek çağrı formatı, lat, lon, exclude, units, lang gibi parametreleri gösterir (One Call 3.x).

Örnek istek fikri (endpoint: /data/3.0/onecall):

async function getOneCall(lat, lon, units = "metric") {
  const url = new URL("https://api.openweathermap.org/data/3.0/onecall");
  url.searchParams.set("lat", String(lat));
  url.searchParams.set("lon", String(lon));
  url.searchParams.set("appid", API_KEY);
  url.searchParams.set("units", units);
  url.searchParams.set("lang", "tr");
  // url.searchParams.set("exclude", "minutely"); // ihtiyaca göre

  const response = await fetch(url.toString());
  if (!response.ok) throw new Error("HTTP " + response.status);
  return await response.json();
}

Pratik öneri: One Call koordinat ister. Şehir adıyla arama yapmak istiyorsanız önce şehri koordinata çevirmeniz gerekir. Hangi yolu seçerseniz seçin, kullandığınız uç noktanın güncel dokümantasyonunu referans alın (OpenWeather API).


6) İsteğe bağlı: paralel isteklerle hız kazanma (Promise.all)

Aynı ekranda birden fazla veri göstermek isteyebilirsiniz (ör. Current Weather + tahmin). Bu durumda istekleri sırayla beklemek yerine paralel başlatmak kullanıcı deneyimini iyileştirebilir. Bu yaklaşım, eğitim içeriklerinde de yaygındır (freeCodeCamp).

Aşağıdaki örnekte iki isteği paralel yapıyoruz: Current Weather ve (opsiyonel) tahmin isteği. Tahmin endpoint’i/erişimi hesabınıza göre değişebilir; resmi sayfadan kontrol edin (OpenWeather API).

async function getForecastByCity(city, units = "metric") {
  const url = new URL("https://api.openweathermap.org/data/2.5/forecast");
  url.searchParams.set("q", city);
  url.searchParams.set("appid", API_KEY);
  url.searchParams.set("units", units);
  url.searchParams.set("lang", "tr");

  const response = await fetch(url.toString());
  if (!response.ok) throw new Error("HTTP " + response.status);
  return await response.json();
}

async function loadDashboard(city, units) {
  const [current, forecast] = await Promise.all([
    getCurrentWeatherByCity(city, units),
    getForecastByCity(city, units)
  ]);
  renderCurrentWeather(current, units);
  // forecast verisini ayrı bir alanda gösterebilirsiniz (liste/kart vb.)
}

7) API anahtarını koruma: üretim için minimal proxy

Tarayıcıda çalışan uygulamada API anahtarı kullanıcıya görünür hale gelebilir. Riski azaltmanın yaygın yolu: anahtarı backend’de saklamak ve front-end’in kendi sunucunuza istek atmasıdır (freeCodeCamp).

Hedef akış:

  • Front-end: /api/weather?city=İstanbul&units=metric çağırır (anahtar yok).
  • Backend: Ortam değişkeninden API anahtarını alır, OpenWeather’e çağrı yapar.
  • Backend: OpenWeather yanıtını döndürür; API anahtarını asla yanıt gövdesine eklemez.

Minimal Express örneği (uygulamaya hazır iskelet)

server.js (örnek)
// npm i express
import express from "express";

const app = express();
const PORT = process.env.PORT || 3000;
const OPENWEATHER_API_KEY = process.env.OPENWEATHER_API_KEY;

app.get("/api/weather", async (req, res) => {
  try {
    const city = String(req.query.city || "").trim();
    const units = String(req.query.units || "metric");
    if (!city) return res.status(400).json({ error: "city gerekli" });
    if (!OPENWEATHER_API_KEY) return res.status(500).json({ error: "Sunucu API anahtarı eksik" });

    const url = new URL("https://api.openweathermap.org/data/2.5/weather");
    url.searchParams.set("q", city);
    url.searchParams.set("appid", OPENWEATHER_API_KEY);
    url.searchParams.set("units", units);
    url.searchParams.set("lang", "tr");

    const owRes = await fetch(url.toString());
    const body = await owRes.text();
    return res.status(owRes.status).type("application/json").send(body);
  } catch (err) {
    return res.status(500).json({ error: "Proxy hatası" });
  }
});

app.listen(PORT, () => console.log("Server running on " + PORT));

Bu iskelet üzerine rate limit, cache ve log’larda hassas bilgileri maskeleme gibi ek adımlar genellikle faydalıdır.


8) Sık hatalar ve hızlı teşhis listesi

  • 401/403: API anahtarı eksik/yanlış veya hesap/plan erişimi gereken bir uç nokta kullanılıyor olabilir. OpenWeather hesabınız ve dokümantasyonu kontrol edin (OpenWeather API).
  • 404: Yanlış endpoint yolu/sürümü (ör. One Call 3.x yerine farklı bir yol) kullanılıyor olabilir. Örnekleri resmi sayfayla karşılaştırın (One Call 3.x).
  • 429: Çok sık istek atılıyor olabilir (oran sınırı). Arama alanına debounce eklemek ve kısa süreli önbellek yardımcı olabilir.
  • “Fetch error” ama status yok: Bu çoğu zaman network/CORS gibi durumlarda görülür. MDN’nin anlattığı gibi bu tür hatalarda fetch reject olabilir; HTTP hataları için ise response.ok kontrolü şarttır (MDN).
  • Yanıt var ama ekran boş: JSON yapısında beklediğiniz alanlar farklı olabilir. Render fonksiyonunda optional chaining gibi kontrollerle kırılganlığı azaltın.

9) Mini kontrol listesi (yayına hazır hale getirme)

  • Birim seçimi net mi? (TR için genellikle metric/Celsius; kullanıcıya seçenek sunmak iyi olur.)
  • Lang parametresi ayarlı mı? (Türkçe açıklamalar için lang=tr)
  • Hata mesajı kullanıcıya anlaşılır şekilde gösteriliyor mu?
  • Arama girdisi boş/çok kısa olduğunda istek atmayı engelliyor musunuz?
  • API anahtarı üretimde istemciye sızmayacak şekilde konumlandırıldı mı? (proxy/backend)
  • OpenWeather dokümantasyon linkleri ve sürüm notları belirtilmiş mi?

Sonraki adımlar (projeyi bir üst seviyeye taşıyın)

  • Otomatik tamamlama (autocomplete): Şehir aramasında kullanıcı deneyimini iyileştirir (freeCodeCamp).
  • Önbellek: Aynı şehir için kısa süreli cache ile hız ve kota yönetimi.
  • Erişilebilirlik: Form etiketleri, klavye ile kullanım, anlamlı hata metinleri.
  • Dağıtım: Front-end’i statik hosting’e, proxy’yi serverless fonksiyona taşıma (anahtar güvenliği için).

Projeyi büyütürken, uç noktalar ve erişim/plan koşulları için resmi dokümantasyonu “tek doğru kaynak” olarak referans almayı unutmayın.


Kaynaklar