ФронтендJavaScriptHTML / CSS

Приложение погоды на JavaScript: API, поиск города и состояния интерфейса

Делаем погодное приложение на JavaScript: ищем координаты города, запрашиваем текущую погоду, показываем загрузку и ошибки, не прячем API-ключ в браузере.

Кодик

Автор

5 мин чтения

Погодный виджет лучше начать с API без секретного ключа и трёх явных состояний: загрузка, результат, ошибка. Вы превратите название города в координаты, запросите текущую температуру и не оставите пользователя перед пустой карточкой.

Запрос погоды состоит из двух шагов. Сначала геокодер ищет координаты по строке, затем прогноз использует широту и долготу. Такое разделение полезно: погода не обязана понимать, что означает «Казань», ей нужны числа.

1Ищем

Геокодер превращает город в координаты.

2Запрашиваем

Погодный endpoint возвращает текущие значения.

3Показываем

Один renderState отвечает за загрузку, ошибку и готовую карточку.

Как устроен запрос погоды

Для учебного проекта используем Open-Meteo: отдельный геокодер и forecast endpoint. AbortController отменит предыдущий поиск, если пользователь быстро отправит новую форму. response.ok проверяется до response.json.

ШагВходРезультат
ГеокодингСтрока городаname, latitude, longitude
ПрогнозКоординатыtemperature_2m, wind_speed_10m
РендерДанные или ошибкаПонятная карточка
Новый поискСигнал AbortControllerСтарый запрос отменён

После этого шага у каждой части есть одна роль. Интерфейс собирает действие, логика меняет состояние, а вывод показывает признак успеха. Это проще отлаживать, чем один большой обработчик.

Два запроса погоды
Город сначала становится координатами. Новый поиск отменяет предыдущую цепочку

Сначала соберите минимальный сценарий. Не добавляйте оформление, хранение и дополнительные режимы одновременно. Добейтесь одного правильного прохода от входных данных до результата, а затем проверяйте каждое улучшение отдельно.

Пишем два fetch и обновление карточки

Функция requestJSON проверяет HTTP-статус. encodeURIComponent защищает query-параметр от пробелов и специальных символов. Ошибка AbortError не показывается пользователю: она означает, что её заменил более свежий запрос.

let controller;
async function requestJSON(url, signal){
  const response=await fetch(url,{signal});
  if(!response.ok) throw new Error('HTTP ' + response.status);
  return response.json();
}
async function loadWeather(city){
  controller?.abort(); controller=new AbortController();
  showStatus('Загружаем…');
  try{
    const geo=await requestJSON(
      'https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1&language=ru',
      controller.signal
    );
    const place=geo.results?.[0];
    if(!place) throw new Error('Город не найден');
    const weather=await requestJSON(
      'https://api.open-meteo.com/v1/forecast?latitude=' + place.latitude + '&longitude=' + place.longitude + '&current=temperature_2m,wind_speed_10m&timezone=auto',
      controller.signal
    );
    showWeather(place.name,weather.current);
  }catch(error){
    if(error.name!=='AbortError') showStatus(error.message==='Город не найден'?error.message:'Не удалось загрузить погоду');
  }
}
Что должно работать после первого запуска

Название города кодируется перед URL. Старый поиск отменяется новым. HTTP-ошибка не маскируется под успешный JSON. Пустой результат и сетевой сбой получают разные понятные сообщения.

Где хранить API-ключ
Код браузера виден всем. Сравнение показывает, какой вариант легче поддерживать и проверять.

Почему API-ключ нельзя спрятать во frontend

API-ключ, записанный в JavaScript браузера, увидит любой посетитель в исходниках и Network. Ограничения домена уменьшают риск, но не превращают ключ в секрет. Для платного или привилегированного API запрос нужно проводить через собственный backend.

СостояниеЧто видит пользовательЧто доступно
idleФорма и пример городаПоиск
loadingИндикатор загрузкиНовая отправка отменяет старую
successГород, температура, ветерПовторный поиск
errorПонятное сообщениеИсправить запрос и повторить

Сохраняйте в одном месте то, что может измениться. Если одно и то же значение скопировано в три функции, после первой правки они начнут расходиться. Один объект состояния и одна функция отрисовки снимают эту проблему.

Смотрите на состояние, а не только на красивый экран. Перезагрузка, повторный клик, пустой ввод и медленный ответ часто открывают ошибки, которые не видны в идеальном демонстрационном проходе.

Практика: проверяем ошибки и быстрый поиск

Проверьте нормальный город, строку с опечаткой, пустой ввод и отключённую сеть. Затем быстро отправьте два разных города. На экране должен остаться результат последнего запроса, а не того, который случайно ответил позже. Посмотрите ответ API в Network и сверьте единицы измерения. Не добавляйте знак градуса или скорость ветра по памяти, если сервис уже прислал точную подпись. Повторите поиск города с одинаковым названием в разных странах. Если интерфейс показывает только первое совпадение, добавьте страну или регион в подпись, чтобы пользователь понимал, какие координаты выбраны.

Проверка своими руками
  1. Найдите свой город кириллицей.
  2. Введите набор случайных символов.
  3. Отключите сеть в DevTools.
  4. Быстро отправьте Москва, затем Казань.
  5. Проверьте карточку на 320 пикселях.
  6. Добавьте единицы измерения из ответа API.
Готово, если выполняются все пункты

Пустой запрос не уходит в сеть. Город с пробелом корректно кодируется. Быстрый второй поиск отменяет первый. Загрузка, ошибка и успех выглядят как разные состояния.

Состояния интерфейса
Пустого экрана быть не должно. Пройдите все четыре сценария перед публикацией проекта.

Доводим погодный виджет до готового состояния

После текущей погоды добавьте прогноз на три дня и сохранение последнего города. Перед этим вынесите преобразование weather code в отдельную функцию и протестируйте неизвестный код. Интерфейс не должен зависеть от идеального ответа API.

Нет проверки response.ok

Ответ 404 пытаются читать как успех.

Секретный ключ в JS

Его можно увидеть и использовать.

Нет loading

Пустая карточка кажется сломанной.

Гонки запросов

Старый ответ перезаписывает новый.

Сверьтесь с первичным источником. Поведение используемых функций и ограничения примера проверяйте по официальной документацией Open-Meteo. Документация особенно важна, когда меняются версии, политика платформы или формат ответа API.

Что получится в итоге

Готовый результат проекта: Приложение погоды на JavaScript: API, поиск города и состояния интерфейса
Готовая погодная карточка показывает город, температуру, состояние, влажность и ветер, а поиск честно сообщает о загрузке и ошибке.

Короткие ответы
Зачем сначала искать координаты?

Погодный endpoint принимает широту и долготу, а не произвольное название.

Можно ли спрятать ключ во frontend?

Нет, код и сетевые запросы видны пользователю.

Зачем AbortController?

Чтобы старый медленный ответ не заменил результат нового поиска.

Как понять, что проект готов?

Он повторяет целевой сценарий несколько раз подряд, понятно реагирует на ошибочный ввод и не теряет состояние при обычных действиях.

Можно ли копировать код целиком?

Можно как стартовую точку, но сначала замените значения из примера, затем запустите проект по шагам. Если вы не можете объяснить одну строку, её лучше разобрать до следующего шага.

Подключите настоящее API без пустых экранов

Асинхронность и DOM разбираются в курсе JavaScript. Основу запроса закрепите по гайду Fetch API.

Если браузер блокирует запрос, сравните ситуацию со статьёй о CORS. Для карточки пригодится переключение темы. В Кодике удобно подменять города и проверять все состояния по очереди.