Jak jsem postavil weather platformu, která se neprodražuje při škálování
Předpověď počasí je komodita zadarmo. Hodnota je dělat s ní něco proaktivního — a neuvařit se na nákladech placeného API. Případovka full-stack TypeScript monorepa.

Proč další weather appka
Předpověď počasí dnes dává uživatelům zdarma každý majoritní operační systém. Apple i Google mají své nativní widgety, které jsou vizuálně atraktivní, rychlé a předinstalované na miliardách zařízení. Když se vývojář rozhodne postavit novou aplikaci pro předpověď počasí, první otázka, kterou od komunity i investorů dostane, nezní „kde vezmeš data“, ale „proč by si tvou aplikaci měl vůbec někdo stáhnout a otevřít“ a „jak ji chceš provozovat, aby tě poplatky za API nezruinovaly hned první měsíc“.
Trh s meteorologickými aplikacemi je přesycený vizuálně dokonalými produkty. Krásné animované ikonky, plynulé přechody a detailní mapy se staly komoditou. Uživatelé však málokdy chtějí trávit čas pouhým prohlížením grafů. Skutečná hodnota moderní softwarové služby nespočívá v tom, že uživateli pasivně ukáže, že bude pršet. Hodnota vzniká v okamžiku, kdy aplikace provede proaktivní krok — varuje uživatele včas, přizpůsobí se jeho specifickému kontextu nebo za něj automaticky vykoná akci. Lidé neplatí za to, aby se na počasí dívali, ale aby minimalizovali rizika, ušetřili čas nebo ochránili svůj majetek.
Při návrhu Weather InTouch jsem se rozhodl stavět na dvou základních pilířích. Prvním je proaktivní práce s daty, kdy se aplikace nesnaží být jen dalším pasivním dashboardem, ale aktivním pomocníkem. Druhým pilířem je přísná optimalizace provozních nákladů. Cílem bylo navrhnout takovou architekturu, která udrží náklady na placená API na absolutním minimu, ideálně blízko nule, a to i v případě náhlého nárůstu uživatelské základny. Tento článek popisuje, jak takovou robustní, škálovatelnou a nákladově efektivní aplikaci postavit od základu.
Monorepo o třech aplikacích a jedné pravdě o datech
Udržovat synchronizaci mezi frontendem, backendem a propagačním webem bývá v mnoha projektech zdrojem neustálé frustrace. Změna jednoho datového pole na backendu často vede k chybám při kompilaci frontendu nebo k rozbití API kontraktu. Z toho důvodu je Weather InTouch koncipován jako TypeScript monorepo postavené na bázi npm workspaces. Tento přístup umožňuje sdílet kód, typové definice a konfigurační soubory napříč celým ekosystémem s minimální režií.
Celé monorepo se skládá ze tří hlavních aplikací a několika sdílených balíčků:
- Frontendová aplikace: Klientské rozhraní postavené na platformě Angular 22. Angular byl zvolen pro svou robustní architekturu, silnou podporu pro dependency injection a vestavěné nástroje pro správu stavu, což usnadňuje práci s komplexními reaktivními formuláři a datovými toky.
- Backendové API: Serverová část běžící na frameworku NestJS 11. NestJS poskytuje modulární strukturu, která ideálně doplňuje Angular na frontendu, a umožňuje snadnou integraci s databázemi a externími službami.
- Statický web: Vlastní TypeScript static-site generator (SSG), který slouží pro generování marketingové prezentace a dokumentace. Použití vlastního lehkého generátoru eliminuje závislost na těžkopádných CMS a zajišťuje maximální rychlost načítání a optimální SEO bez nutnosti provozovat další serverovou infrastrukturu.
Jediný zdroj pravdy pro datové struktury
Srdcem monorepa je sdílený balíček DTO (Data Transfer Objects). Tento balíček definuje přesné tvary všech dat, která protékají mezi backendem a frontendem. Backend i frontend importují identické TypeScript typy a rozhraní. Pokud dojde ke změně struktury předpovědi počasí, kompilátor okamžitě upozorní na všech místech v klientské aplikaci, kde je třeba kód upravit.
// packages/shared-dtos/src/weather.dto.ts
export interface CoordinateDto {
latitude: number;
longitude: number;
}
export interface CurrentWeatherDto {
temperature: number;
windSpeed: number;
relativeHumidity: number;
weatherCode: number;
updatedAt: string;
}
export interface WeatherForecastResponseDto {
coordinates: CoordinateDto;
current: CurrentWeatherDto;
timezone: string;
}
Díky tomuto přístupu odpadá jakékoli ruční přepisování datových struktur nebo spoléhání se na neaktuální API dokumentaci.
Architektonické rozdělení rolí
Z hlediska bezpečnosti a výkonu je striktně definováno, kde se data zpracovávají. Backend plně vlastní veškerý přístup k placeným API, řeší autorizaci uživatelů, správu předplatného a komunikaci s platební bránou. Frontend je navržen jako maximálně bezestavový (stateless).
Jednotlivé komponenty a vizuální karty na frontendu nedostávají surová data z externích API. Vstupují do nich již očištěná, transformovaná a lokalizovaná data připravená k okamžitému zobrazení. Tento přístup výrazně zjednodušuje testování klientské aplikace, protože komponenty lze snadno testovat pomocí mockovacích dat odpovídajících sdíleným DTO rozhraním.
Páteř zdarma, placené API jen na špičku
Základní chybou při návrhu meteorologických aplikací je okamžité napojení všech datových toků na drahá komerční API. Poskytovatelé jako OpenWeather nebo Apple WeatherKit si účtují poplatky za každý tisíc požadavků. Pokud vaše aplikace provede dotaz na server při každém otevření widgetu nebo při každém posunu mapy, účty za infrastrukturu mohou rychle přesáhnout generované příjmy.
Architektura Weather InTouch tento problém řeší hybridním modelem získávání dat. Jako hlavní datová páteř slouží bezplatné API Open-Meteo. To poskytuje vysoce přesná data pro aktuální stav, hodinovou předpověď a denní předpověď až na 7 dní dopředu. Open-Meteo nabízí pro nekomerční i komerční využití velmi příznivé podmínky a jeho datové modely jsou pro většinu běžných scénářů naprosto dostačující.
Placené API (v tomto případě OpenWeather) se používá pouze jako doplňková vrstva (overlay) pro zpřesnění specifických okamžitých hodnot v reálném čase, kde bezplatné modely mohou vykazovat mírné zpoždění. Tato placená vrstva je však v backendu implementována za konfigurovatelným přepínačem funkcí (feature flag).
Princip řízené degradace (Graceful Degradation)
Pokud placené API vyčerpá svůj denní limit, dojde k chybě sítě nebo je záměrně vypnuto z důvodu úspory nákladů, systém automaticky a bez zásahu uživatele přepne na náhradní datový zdroj. Uživatel sice může přijít o drobnou nuanci v přesnosti okamžité teploty, ale aplikace zůstává plně funkční a poskytuje kompletní předpověď z primárního bezplatného zdroje.
Následující ukázka ilustruje, jak NestJS služba řeší toto záložní chování při selhání placeného poskytovatele:
// apps/api/src/weather/weather.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { OpenMeteoService } from './open-meteo.service';
import { OpenWeatherService } from './open-weather.service';
import { WeatherForecastResponseDto } from '@weather-intouch/dtos';
@Injectable()
export class WeatherService {
private readonly logger = new Logger(WeatherService.name);
constructor(
private readonly openMeteo: OpenMeteoService,
private readonly openWeather: OpenWeatherService,
) {}
async getWeatherForecast(lat: number, lon: number): Promise<WeatherForecastResponseDto> {
const baseForecast = await this.openMeteo.fetchForecast(lat, lon);
try {
// Pokus o obohacení dat z placeného API, pokud je aktivní
const refinedCurrent = await this.openWeather.fetchCurrentOverlay(lat, lon);
return {
...baseForecast,
current: {
...baseForecast.current,
temperature: refinedCurrent.temperature ?? baseForecast.current.temperature,
relativeHumidity: refinedCurrent.humidity ?? baseForecast.current.relativeHumidity,
updatedAt: new Date().toISOString(),
},
};
} catch (error) {
this.logger.warn(`Placené API selhalo nebo je nedostupné. Používám záložní data z Open-Meteo. Error: ${error.message}`);
return baseForecast;
}
}
}
Tímto způsobem je zajištěno, že výpadek jednoho poskytovatele nebo náhlé omezení API klíče nezpůsobí nedostupnost celé aplikace.
Cost-control engine jako páteř celého produktu
Skutečné kouzlo úspory nákladů se neodehrává na úrovni výběru poskytovatele, ale ve způsobu, jakým s daty pracujeme uvnitř naší infrastruktury. Pokud tisíc uživatelů v jednom městě otevře aplikaci ve stejný okamžik, není důvod posílat tisíc dotazů na externí meteorologické servery. Počasí se nemění každou sekundu a na vzdálenosti několika metrů se hodnoty neliší.
Pro snížení zátěže a nákladů byl vyvinut robustní caching engine, který stojí na dvou klíčových konceptech: zaokrouhlování souřadnic na prostorovou mřížku a vzorci Single-Flight. Podrobnější pohled na tuto problematiku nabízí celý case study projektu.
Prostorová mřížka a sdílení cache
Když mobilní zařízení odesílá GPS souřadnice, posílá je s přesností na šest až osm desetinných míst. To představuje přesnost v řádu milimetrů až centimetrů. Pro potřeby předpovědi počasí je však taková přesnost kontraproduktivní, protože generuje nekonečné množství unikátních klíčů pro cache.
Weather InTouch proto implementuje zaokrouhlování souřadnic na čtyři desetinná místa. V geografické šířce střední Evropy to odpovídá mřížce o velikosti přibližně 11 × 11 metrů.
- Souřadnice
50.087234, 14.421123se zaokrouhlí na50.0872, 14.4211. - Sousední uživatel stojící o deset metrů vedle se zaokrouhlí na stejné hodnoty.
Díky tomuto zaokrouhlení sdílejí všichni uživatelé v daném mikrosegmentu identický záznam v cache. Cache je navržena jako dvouvrstvá:
- In-memory cache: Rychlá paměť přímo v běžící instanci NestJS pro okamžitou odezvu.
- Redis cache: Distribuovaná sdílená paměť pro synchronizaci mezi více instancemi backendu.
Placené API je tak kontaktováno maximálně jedním voláním placeného API na lokalitu za definovaný časový interval (který se pohybuje v řádu desítek minut).
Prevence zahlcení pomocí Single-Flight
Při vysoké návštěvnosti může nastat situace zvaná cache stampede nebo dogpiling. V okamžiku, kdy platnost záznamu v cache vyprší a na server dorazí desítky souběžných požadavků na stejnou lokalitu, všechny tyto požadavky zjistí, že cache je prázdná, a současně se pokusí poslat dotaz na externí API. To vede k okamžitému zablokování limitů a zbytečným nákladům.
Vzorec Single-Flight (v Node.js implementovaný pomocí mapy aktivních příslibů — Promises) zajišťuje, že pro každou unikátní lokalitu může v jeden okamžik běžet pouze jeden aktivní odchozí požadavek na externí API.
// apps/api/src/weather/single-flight.manager.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class SingleFlightManager {
private activeRequests = new Map<string, Promise<unknown>>();
async execute<T>(key: string, fetchFn: () => Promise<T>): Promise<T> {
const existingPromise = this.activeRequests.get(key);
if (existingPromise) {
// Pokud již požadavek na stejný klíč běží, vrátíme existující Promise
return existingPromise as Promise<T>;
}
// Vytvoříme nový požadavek a uložíme ho do mapy
const promise = fetchFn().finally(() => {
this.activeRequests.delete(key);
});
this.activeRequests.set(key, promise);
return promise;
}
}
Pokud tedy na server dorazí 150 souběžných požadavků na předpověď pro Prahu 1, provede se pouze jeden fyzický dotaz na externí API. Zbylých 149 požadavků počká na dokončení tohoto jediného dotazu a následně obdrží identický výsledek.
Co aplikace umí dnes
Weather InTouch je v současné verzi funkční MVP v aktivním vývoji, které už nabízí ucelenou sadu funkcí. Aplikace byla od počátku navrhována s důrazem na modularitu, což umožňuje snadné přidávání nových datových vrstev.
Mezi hlavní integrované funkce patří:
- Aktuální stav a vícedenní předpověď: Standardní přehled o teplotě, vlhkosti, tlaku a rychlosti větru doplněný o výhled na následujících 7 dní.
- Hodinový multi-graf: Interaktivní vizualizace, která uživateli umožňuje sledovat vývoj teploty, srážek a větru hodinu po hodině na jedné časové ose.
- Srážkový radar: Mapová vrstva zobrazující pohyb srážkových oblastí s možností přehrávání posledních snímků.
- Kvalita ovzduší, pylové zpravodajství a UV index: Klíčová data pro alergiky a outdoorové nadšence, která integrují indexy kvality ovzduší (AQI) a koncentrace hlavních pylových alergenů.
- Oficiální meteo výstrahy: Systém zpracovává a zobrazuje oficiální meteo výstrahy ve formátu CAP (Common Alerting Protocol) vydávané národními hydrometeorologickými ústavy.
- Správa lokalit: Uživatelé si mohou ukládat své oblíbené polohy pro rychlý přístup.
- Lokalizace a jednotky: Kompletní podpora pro 20 světových jazyků a automatické přepínání mezi metrickým a imperiálním systémem jednotek.
Bezpečnost a správa uživatelů
Z hlediska backendové architektury (NestJS + MongoDB + Redis) je kladen velký důraz na bezpečnost a soukromí uživatelů.
- Autentizace: Přihlašování je realizováno pomocí bezpečného ověření přes Google a Apple účty (token-on-backend validace). Server přijme autorizační token ze zařízení, validuje jej proti autoritě a vystaví vlastní bezpečný JWT token s omezenou platností.
- Billing: Správa předplatného a plateb probíhá přes integrovanou platformu Stripe. Systém rozlišuje tři úrovně přístupu: Free, Plus a Pro.
- GDPR soulad: Každý uživatel má v nastavení profilu možnost jedním kliknutím exportovat veškerá svá uložená data (historii vyhledávání, uložené lokality, nastavení) nebo požádat o okamžité a kompletní smazání účtu z databáze MongoDB bez nutnosti kontaktovat podporu.
Co je na roadmapě
Současná verze Weather InTouch představuje stabilní základ, na kterém lze dále stavět. Protože cílem projektu je posunout se od pasivního zobrazování dat k proaktivní asistenci, plánované funkce se zaměřují na automatizaci a integraci do širšího technologického ekosystému.
Watchdog s uživatelskými prahy
Tato funkce umožní uživatelům definovat si vlastní pravidla pro sledování počasí. Namísto obecného varování před deštěm si uživatel bude moci nastavit specifické podmínky, například: „Pošli mi upozornění, pokud rychlost větru v mé lokalitě překročí 15 m/s“ nebo „Upozorni mě den předem, pokud teplota klesne pod bod mrazu, abych stihl zazimovat rostliny.“
Automace a webhooky
Propojení předpovědi počasí s chytrou domácností (Smart Home) nebo podnikovými systémy. Pomocí odchozích webhooků bude možné automaticky spouštět akce na základě meteorologických podmínek. Pokud systém detekuje blížící se silný déšť, odešle webhook do systému domácí automatizace, který následně zatáhne střešní okna nebo odloží plánovanou závlahu trávníku.
Embedovatelný widget pro weby třetích stran
Umožní provozovatelům webových stránek (například penzionům, sportovním areálům nebo lokálním zpravodajským portálům) snadno vložit přizpůsobený widget s předpovědí počasí přímo do jejich obsahu. Widget bude plně responzivní, stylovatelný pomocí CSS proměnných a napojený na náš optimalizovaný caching engine, což zajistí minimální zátěž pro hostitelský web.
Multi-tenant vrstva a mobilní aplikace
Pro firemní zákazníky se připravuje multi-tenant rozhraní, které umožní správu flotily zařízení nebo sledování počasí na stovkách poboček současně pod jedním administrátorským účtem. Souběžně s tím je v plánu vývoj nativní mobilní aplikace, která plně využije push notifikace na pozadí a widgety na domovské obrazovce pro okamžité doručování kritických výstrah.
Pokud vás zajímají technické detaily implementace, architektura databáze nebo způsob integrace radarových snímků, navštivte celý case study projektu, kde naleznete podrobný rozbor celého vývojového cyklu a infrastrukturního nastavení.

