Bir hava durumu widget'ı küçük görünür ama gerçek bir API entegrasyonunda ihtiyaç duyulan temel kararların çoğunu içerir: istek nereye atılacak, veri ne kadar cache'lenecek, hata olursa kullanıcı ne görecek ve yanıt formatı değişirse uygulama nasıl davranacak?
Bu rehberde API Deposu'ndaki Open-Meteo kaydını temel alarak Next.js App Router içinde server-rendered bir widget tasarlıyoruz. Open-Meteo'nun avantajı, tipik geliştirme ve düşük hacimli kullanımda API anahtarı gerektirmeden çalışmasıdır.
Ne inşa ediyoruz?
Bir Next.js server component şu işi yapacak:
- Enlem ve boylamla Open-Meteo forecast endpoint'ine istek atacak.
- Güncel sıcaklık, rüzgâr hızı ve WMO hava kodunu doğrulayacak.
- Başarılı yanıtı 10 dakika yeniden doğrulama süresiyle cache'leyecek.
- Ağ veya veri hatasında sayfanın geri kalanını bozmadan bir geri dönüş gösterecek.
Örnek konum İstanbul olsa da aynı yardımcı fonksiyon başka koordinatlarla da kullanılabilir.
Endpoint şekli
Open-Meteo belgelerinde güncel değerler, forecast endpoint'inin current parametresiyle seçilir. Bu rehberin isteği şu şekildedir:
GET https://api.open-meteo.com/v1/forecast
?latitude=41.0082
&longitude=28.9784
¤t=temperature_2m,wind_speed_10m,weather_code
&temperature_unit=celsius
&wind_speed_unit=kmhYanıtın current nesnesinde temperature_2m, wind_speed_10m ve weather_code alanlarını bekliyoruz. Sıcaklık ve rüzgâr birimini sorguda açıkça belirlemek, UI'daki °C ve km/sa etiketlerinin sağlayıcı varsayımlarına bağlı kalmasını engeller. TypeScript tipi yazmak tek başına dış veriyi güvenli yapmaz; JSON çalışma anında yine unknown kabul edilip kontrol edilmelidir.
Cache'li ve doğrulamalı fetch yardımcısı
Sağlayıcı erişimini UI bileşeninden ayırın. Aşağıdaki yardımcı; koordinatları, HTTP durumunu ve üç sayısal alanı kontrol eder. Beş saniyelik timeout da yanıt vermeyen isteğin sayfa render'ını süresiz bekletmesini önler.
// src/lib/weather.ts
export type CurrentWeather = {
temperature: number;
windSpeed: number;
weatherCode: number;
};
function isFiniteNumber(value: unknown): value is number {
return typeof value === "number" && Number.isFinite(value);
}
export async function getCurrentWeather(
latitude: number,
longitude: number,
): Promise<CurrentWeather | null> {
if (
!Number.isFinite(latitude) || latitude < -90 || latitude > 90
|| !Number.isFinite(longitude) || longitude < -180 || longitude > 180
) {
throw new Error("Geçersiz koordinat.");
}
const url = new URL("https://api.open-meteo.com/v1/forecast");
url.searchParams.set("latitude", String(latitude));
url.searchParams.set("longitude", String(longitude));
url.searchParams.set(
"current",
"temperature_2m,wind_speed_10m,weather_code",
);
url.searchParams.set("temperature_unit", "celsius");
url.searchParams.set("wind_speed_unit", "kmh");
try {
const response = await fetch(url, {
next: { revalidate: 600 },
signal: AbortSignal.timeout(5_000),
});
if (!response.ok) return null;
const payload: unknown = await response.json();
if (!payload || typeof payload !== "object") return null;
const current = (payload as { current?: unknown }).current;
if (!current || typeof current !== "object") return null;
const values = current as Record<string, unknown>;
if (
!isFiniteNumber(values.temperature_2m)
|| !isFiniteNumber(values.wind_speed_10m)
|| !isFiniteNumber(values.weather_code)
) {
return null;
}
return {
temperature: values.temperature_2m,
windSpeed: values.wind_speed_10m,
weatherCode: values.weather_code,
};
} catch {
return null;
}
}next: { revalidate: 600 }, Next.js veri cache'inin uygun olduğu üretim akışında aynı URL için yanıtı 600 saniye yeniden kullanır. Koordinatlar URL'nin parçası olduğu için farklı şehirler ayrı cache girdileri oluşturur. Cache süresini ürününüzün tazelik ihtiyacına göre belirleyin; bu örnekteki 10 dakika evrensel bir meteoroloji kuralı değildir.
Koordinatlar serbest kullanıcı girdisinden geliyorsa her küçük ondalık farkı yeni bir URL ve cache anahtarı üretir. Şehir tabanlı bir widget'ta önceden tanımlı koordinatlar kullanmak veya ürünün ihtiyaç duyduğu hassasiyete göre koordinatları normalize etmek, gereksiz cache çeşitlenmesini azaltır. Bu normalizasyonu yapmadan önce konum hassasiyetinin kullanım amacına etkisini değerlendirin.
Hava kodunu kullanıcı diline çevirin
Open-Meteo weather_code alanında WMO kodları döndürür. UI içinde ham 3 değerini göstermek yerine kullandığınız kodları etiketleyin ve bilinmeyen değer için açık bir varsayılan bırakın.
// src/lib/weather-codes.ts
const WEATHER_LABELS: Record<number, string> = {
0: "Açık",
1: "Çoğunlukla açık",
2: "Parçalı bulutlu",
3: "Kapalı",
45: "Sisli",
61: "Hafif yağmurlu",
63: "Yağmurlu",
65: "Kuvvetli yağmurlu",
71: "Hafif kar yağışlı",
73: "Kar yağışlı",
95: "Gök gürültülü fırtına",
};
export function describeWeather(code: number): string {
return WEATHER_LABELS[code] ?? "Durum bilinmiyor";
}Üretimde desteklemek istediğiniz WMO kodlarının tamamını Open-Meteo belgelerindeki tabloyla eşleştirin.
Server component içinde kullanım
// src/components/weather-widget.tsx
import { describeWeather } from "@/lib/weather-codes";
import { getCurrentWeather } from "@/lib/weather";
type WeatherWidgetProps = {
city: string;
latitude: number;
longitude: number;
};
export async function WeatherWidget(props: WeatherWidgetProps) {
const weather = await getCurrentWeather(props.latitude, props.longitude);
if (!weather) {
return <p role="status">Hava durumu şu anda alınamıyor.</p>;
}
return (
<section aria-label={`${props.city} hava durumu`}>
<strong>{props.city}</strong>
<p>{Math.round(weather.temperature)} °C</p>
<p>
{describeWeather(weather.weatherCode)} ·{" "}
{Math.round(weather.windSpeed)} km/sa
</p>
</section>
);
}Bir sayfada kullanım da doğrudan sunucu bileşeni olarak yapılır:
<WeatherWidget
city="İstanbul"
latitude={41.0082}
longitude={28.9784}
/>Hata ve üretim sınırları
Yardımcı bir pazarlama kartında null veya kısa bir "alınamıyor" durumu yeterli olabilir. Operasyonel karar veren bir ekranda ise hata türünü sunucu loguna aktarın, son başarılı verinin ne kadar eski olduğunu gösterin ve gerekiyorsa sözleşmesi uygun ikinci bir sağlayıcı tasarlayın. Sessiz fallback kullanmak, kesintiyi hiç izlememek anlamına gelmemelidir.
Open-Meteo çağrısını tek bir yardımcı sınırında tutmak; cache süresini, birimleri, timeout'u veya sağlayıcıyı UI'ı yeniden yazmadan değiştirmeyi sağlar. Kritik meteoroloji kullanımında bu örnek tek başına yeterlilik ya da doğruluk garantisi vermez.
Örnekte ağ hatası, timeout, HTTP hatası ve şema hatası aynı null sonucuna indirgenir; yardımcı bir kart için bu bilinçli bir sadeliktir. Dashboard gibi izlenmesi gereken bir yüzeyde { ok: true, data } ve { ok: false, reason } biçiminde ayrımlı bir sonuç döndürün. Böylece kullanıcıya güvenli bir genel mesaj gösterirken ölçümlerde sağlayıcı kesintisini geçersiz yanıttan ayırabilirsiniz. Bu ayrım, yalnızca yeniden denemeyle düzelebilecek hatalara retry uygulamanızı da kolaylaştırır.
İlgili API Deposu kayıtları
Kaynaklar
Sık Sorulan Sorular
›Open-Meteo için API anahtarı gerekiyor mu?
Hayır. Open-Meteo geliştirme ve düşük hacimli kullanım için API anahtarı olmadan çalışabilen nadir hava durumu API'lerinden biridir.
›Hava durumu verisini ne kadar sık yenilemeliyim?
Pazarlama sayfası veya dashboard widget'ı için 10-15 dakikalık cache çoğu durumda yeterlidir. Her sayfa görüntülemede API çağırmak gereksiz yük oluşturur.
›Widget frontend'de mi backend'de mi veri çekmeli?
Next.js server component ile sunucuda çekmek cache kontrolünü kolaylaştırır ve ilk yüklemede içeriği hazır gösterir. Client-side fetch de mümkündür, ancak cache ve hata yönetimi ayrıca düşünülmelidir.



