はじめに
この記事では、AIアシスタント「Bob」と対話しながら、複数の気象予報会社のAPIから明日の天気予報を取得して比較表示するWebアプリケーションを作成した過程を紹介します。
成果物
Bobとのやり取りで以下のツールができました。
5社の予報を一括で取得して参照することができます。
今回取得した予報は各気象会社に予報のばらつきがありませんでしたが、日によっては気象会社によって全然違う予報を出していることがあります。各気象会社での予報のばらつきから、間接的にその予報の信頼度を推し量ることができると考え、このツールを作ってみました。

開発の流れ
1. 最初のリクエスト
私: 複数の気象予報会社から明日の予報データを取得して表で可視化するツールを作成してください
Bob: 了解しました。まず、プロジェクトディレクトリを作成します。
Bobは以下の手順でプロジェクトを構築してくれました:
- プロジェクトディレクトリの作成
- package.jsonの作成
- バックエンド(Express サーバー)の実装
- フロントエンド(HTML/CSS/JavaScript)の実装
- 依存パッケージのインストール
- アプリケーションのテスト
2. 最初の実装
Bobが最初に実装してくれたAPIは以下の4つでした:
- Open-Meteo: オープンソースの気象予報API
- 7Timer!: 天文・気象予報サービス
- Brightsky (DWD): ドイツ気象局のデータ
- MeteoFrance: デモデータ
あまり聞き馴染みのないサービスが並んでいます。APIをAPI Key不要で提供しているところをBobが選定してくれました。
デモデータは微妙なのでMeteoFranceだけ削除してもらうことにしました。
3. API の変更リクエスト(1回目)
私: MeteoFranceは削除して、気象庁を追加してください
Bob: 了解しました。気象庁のAPIを追加します。
Bobは気象庁の公式APIが一般公開されていないことを理解し、デモデータを生成する実装を提案してくれました。
4. API の変更リクエスト(2回目)
デモデータはいらないので、APIを無償で提供しているOpenWeatherMap APIとAccuWeatherのサービスも追加してもらうことにしました。
私: 気象庁のデータも削除しましょう。OpenWeatherMap APIとAccuWeather APIを追加してください。API Keyはすでに持っています
Bob: 了解しました。気象庁のデータを削除して、OpenWeatherMapとAccuWeatherのAPIを追加します。まず、APIキーを環境変数として設定できるように.envファイルのサンプルを作成します。
Bobは以下の対応をしてくれました:
-
.env.exampleファイルの作成 - APIキーの入力を促す質問
-
.envファイルの作成 -
package.jsonにdotenvパッケージを追加 -
server.jsの更新(環境変数からAPIキーを読み込み) - OpenWeatherMapとAccuWeatherのAPI実装
-
.gitignoreファイルの作成(APIキーの保護) - READMEの更新
最終的な実装
プロジェクト構造
weather-forecast-viewer/
├── package.json # プロジェクト設定
├── server.js # Express サーバー
├── .env # APIキー(Gitには含めない)
├── .env.example # APIキーのサンプル
├── .gitignore # Git除外設定
├── public/
│ └── index.html # フロントエンド UI
└── README.md # ドキュメント
使用している気象予報API(5社)
- Open-Meteo - オープンソースの気象予報API
- 7Timer! - 天文・気象予報サービス
- Brightsky (DWD) - ドイツ気象局のデータ
- OpenWeatherMap - 世界的に有名な気象予報API
- AccuWeather - 高精度な気象予報サービス
バックエンド実装のポイント
1. 環境変数の管理
require('dotenv').config();
const OPENWEATHERMAP_API_KEY = process.env.OPENWEATHERMAP_API_KEY;
const ACCUWEATHER_API_KEY = process.env.ACCUWEATHER_API_KEY;
2. OpenWeatherMap API の実装
async function getOpenWeatherMapForecast(lat, lon) {
try {
if (!OPENWEATHERMAP_API_KEY) {
console.error('OpenWeatherMap API Key is not set');
return null;
}
const url = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${OPENWEATHERMAP_API_KEY}&units=metric&lang=ja`;
const response = await axios.get(url);
// 明日のデータを取得
const tomorrow = new Date(Date.now() + 86400000);
const tomorrowData = response.data.list.filter(item => {
const itemDate = new Date(item.dt * 1000);
return itemDate.toDateString() === tomorrow.toDateString();
});
const temps = tomorrowData.map(d => d.main.temp);
const precips = tomorrowData.map(d => d.rain ? d.rain['3h'] || 0 : 0);
return {
source: 'OpenWeatherMap',
date: tomorrow.toISOString().split('T')[0],
maxTemp: Math.max(...temps),
minTemp: Math.min(...temps),
precipitation: precips.reduce((a, b) => a + b, 0),
weather: tomorrowData[0].weather[0].description,
unit: '°C'
};
} catch (error) {
console.error('OpenWeatherMap API Error:', error.message);
return null;
}
}
3. AccuWeather API の実装
AccuWeatherは2段階のAPI呼び出しが必要です:
- 緯度・経度からLocation Keyを取得
- Location Keyを使って予報データを取得
async function getAccuWeatherForecast(lat, lon) {
try {
if (!ACCUWEATHER_API_KEY) {
console.error('AccuWeather API Key is not set');
return null;
}
// Location Keyを取得
const locationUrl = `http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=${ACCUWEATHER_API_KEY}&q=${lat},${lon}&language=ja`;
const locationResponse = await axios.get(locationUrl);
const locationKey = locationResponse.data.Key;
// 5日間予報を取得
const forecastUrl = `http://dataservice.accuweather.com/forecasts/v1/daily/5day/${locationKey}?apikey=${ACCUWEATHER_API_KEY}&language=ja&metric=true`;
const forecastResponse = await axios.get(forecastUrl);
// 明日のデータ
const tomorrow = forecastResponse.data.DailyForecasts[1];
return {
source: 'AccuWeather',
date: new Date(tomorrow.Date).toISOString().split('T')[0],
maxTemp: tomorrow.Temperature.Maximum.Value,
minTemp: tomorrow.Temperature.Minimum.Value,
precipitation: tomorrow.Day.PrecipitationProbability || 0,
weather: tomorrow.Day.IconPhrase,
unit: '°C'
};
} catch (error) {
console.error('AccuWeather API Error:', error.message);
return null;
}
}
4. 複数APIの並列実行
app.get('/api/forecast', async (req, res) => {
const lat = parseFloat(req.query.lat) || DEFAULT_LAT;
const lon = parseFloat(req.query.lon) || DEFAULT_LON;
try {
const forecasts = await Promise.all([
getOpenMeteoForecast(lat, lon),
get7TimerForecast(lat, lon),
getBrightskyForecast(lat, lon),
getOpenWeatherMapForecast(lat, lon),
getAccuWeatherForecast(lat, lon)
]);
const validForecasts = forecasts.filter(f => f !== null);
res.json({
location: { lat, lon },
forecasts: validForecasts,
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({ error: '予報データの取得に失敗しました' });
}
});
フロントエンド実装のポイント
特にこだわりがなかったのでBobに任せました。
1. レスポンシブデザイン
グラデーション背景と美しいカードデザインを採用:
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
2. データの取得と表示
async function fetchForecasts() {
const lat = document.getElementById('latitude').value;
const lon = document.getElementById('longitude').value;
try {
const response = await fetch(`/api/forecast?lat=${lat}&lon=${lon}`);
const data = await response.json();
displayForecasts(data);
} catch (error) {
// エラー処理
}
}
function displayForecasts(data) {
let tableHTML = `
<table>
<thead>
<tr>
<th>予報会社</th>
<th>日付</th>
<th>最高気温</th>
<th>最低気温</th>
<th>降水量</th>
<th>天気</th>
</tr>
</thead>
<tbody>
`;
data.forecasts.forEach(forecast => {
tableHTML += `
<tr>
<td class="source-name">${forecast.source}</td>
<td>${forecast.date}</td>
<td class="temp-max">${forecast.maxTemp.toFixed(1)}${forecast.unit}</td>
<td class="temp-min">${forecast.minTemp.toFixed(1)}${forecast.unit}</td>
<td class="precipitation">${forecast.precipitation.toFixed(1)} mm</td>
<td><span class="weather">${forecast.weather}</span></td>
</tr>
`;
});
tableHTML += `</tbody></table>`;
document.getElementById('result').innerHTML = tableHTML;
}
セットアップ方法
このあたりのコマンドもBobに実施してもらったので、特に自分では実行してません。
1. リポジトリのクローン
git clone <repository-url>
cd weather-forecast-viewer
2. 依存パッケージのインストール
npm install
3. APIキーの設定
.env.exampleを.envにコピーして、APIキーを設定:
cp .env.example .env
.envファイルを編集:
OPENWEATHERMAP_API_KEY=your_openweathermap_api_key
ACCUWEATHER_API_KEY=your_accuweather_api_key
4. サーバーの起動
npm start
ブラウザで http://localhost:3000 にアクセス
Bobとの開発で学んだこと
Bobは一度にすべてを実装するのではなく、以下のように段階的に進めてくれました:
- プロジェクト構造の作成
- 基本的なAPIの実装
- フロントエンドの実装
- テストと確認
- 要望に応じた修正
実は最初に実装したときは何度試しても気象データの取得がうまくいきませんでした。
その対策として最初は実装したい気象予報APIを絞って、後から追加していくこととしました。
まとめ
AIアシスタント「Bob」と対話しながら、複数の気象予報APIを比較するWebアプリケーションを作成しました。
開発のポイント
✅ 柔軟な対応: 途中で要件が変わっても、Bobは適切に対応してくれました
✅ セキュリティ: APIキーの管理など、セキュリティ面も考慮されています
✅ 実用的な実装: 5つの異なるAPIを統合し、実際に動作するアプリケーションが完成
✅ 美しいUI: レスポンシブデザインで見やすい表形式の表示
今後の拡張案
- より多くの気象予報APIの追加
- 週間予報の表示
- グラフによる可視化
- 予報精度の比較機能
- 位置情報の自動取得
参考リンク
おわりに
Bobとの対話型開発は、アイデアを素早く形にするのに非常に有効でした。特に、複数のAPIを統合するような複雑なタスクでも、段階的に進めることで確実に実装できました。
皆さんもぜひ、Bobを活用した開発にチャレンジしてみてください!
<2026/2/1時点>
早期アクセス継続中です。ぜひこの機会にIBM Bobとお友達になってみてください。
https://community.ibm.com/community/user/blogs/yoshiomi-segawa/2025/12/12/ibm-bob
IBM Bobのコミュニティ"Bob サロン"もオープン予定です。夕方に短時間のハイブリッド開催を月一ペースで実施しますのでお気軽にお立ち寄りください。
https://community.ibm.com/community/user/groups/community-home/recent-community-events?communitykey=eb449c0f-27c1-4223-bbd7-01874dfe4714
