Jak powstał Avia Weather (cz. 2): skąd brać dane i jak ukryć klucz API

9.10.2026 · 3 min czytania

W części 1 ustaliliśmy pomysł i stack. Mam ładny interfejs, który… nie pokazuje żadnej pogody. Pora to zmienić — bo aplikacja pogodowa bez prawdziwych danych to tylko makieta.

Skąd wziąć prawdziwe METAR-y i TAF-y?#

Nie będę przecież ręcznie przepisywał depesz. Potrzebuję źródła, które poda mi aktualne dane dla dowolnego lotniska. Padło na AVWX — API, które zwraca METAR, TAF i informacje o stacji w czytelnym JSON-ie. Jest darmowy plan, który w zupełności wystarcza do nauki i małego projektu.

Jest jeden haczyk: żeby z niego korzystać, trzeba się uwierzytelnić tokenem.

Pierwszy odruch (zły): token we frontendzie#

Mój pierwszy pomysł był prosty: skoro to React woła API, to wkleję token do kodu i po sprawie. I tu zapaliła mi się lampka.

Nawet jeśli schowam token do zmiennej środowiskowej z prefiksem VITE_ — to nic nie daje! Vite wkompilowuje takie zmienne do paczki, która leci do przeglądarki. Prefiks VITE_ oznacza wręcz „to jest publiczne, możesz tego używać na froncie”. Dokładnie takiego zachowania chciałem uniknąć.

Rozwiązanie: funkcja serwerowa jako pośrednik#

Potrzebowałem czegoś pomiędzy przeglądarką a AVWX — kawałka kodu, który działa na serwerze, trzyma token u siebie i tylko podaje mi gotowe dane. Na Netlify robi to funkcja serwerowa (serverless function).

Przepływ wygląda tak:

  1. Frontend woła moją funkcję: /.netlify/functions/avwx?station=EPWA
  2. Funkcja (na serwerze) dokłada token i woła AVWX
  3. AVWX zwraca dane do funkcji
  4. Funkcja oddaje czysty JSON do frontendu

Token nigdy nie opuszcza serwera. Przeglądarka widzi tylko moją funkcję, a nie AVWX ani klucza.

Uproszczony szkielet funkcji (netlify/functions/avwx.mjs):

export default async (req) => {
  const url = new URL(req.url);
  const station = url.searchParams.get("station") ?? "";

  // Walidacja: tylko 3 lub 4 litery (kod IATA albo ICAO)
  if (!/^[A-Za-z]{3,4}$/.test(station)) {
    return new Response("Nieprawidłowy kod stacji", { status: 400 });
  }

  const token = process.env.AVWX_TOKEN; // tylko na serwerze!
  const res = await fetch(`https://avwx.rest/api/metar/${station}`, {
    headers: { Authorization: `Bearer ${token}` },
  });

  return new Response(await res.text(), {
    status: res.status,
    headers: { "Content-Type": "application/json" },
  });
};

Dwie rzeczy, na które zwróć uwagę:

Po stronie frontendu — jeden punkt wejścia#

Zamiast wołać funkcję w trzech miejscach (METAR, TAF, stacja), zrobiłem sobie jedną funkcję pomocniczą i trzy cienkie opakowania na niej. Ale o tym — jak ładnie to posprzątać i jak zamienić te kosmiczne kody na ludzki język — w części 3.

Więcej z tej serii

  1. 1.Jak powstał mój METAR (cz. 1): pomysł i stack
  2. 2.Jak powstał Avia Weather (cz. 2): skąd brać dane i jak ukryć klucz API
  3. 3.Jak powstał Avia Weather (cz. 3): jak rozszyfrować METAR na ludzki język