はじめに
UdemyのWeb Developer BootcampでNode.jsやExpressの学習を進めていた中で、試験的にWeb APIも使用して、
簡易的な現在の天気を検索するアプリを作成してみました。
概要
今回作成したのは、OpenWeatherAPI という気象情報を取得できるWeb APIを使用しました。
URLはコチラから。
ユーザー登録が済めばすぐにAPIキーの取得が可能です。
APIキーは有効化されるまで少し時間がかかるので注意してください。私は約20分ほどかかりました。
無料版と有料版で、取得できる回数や日数が変わってきますが、今回は無料版を使用するため、現在の天気を取得していきます。(無料版APIでも1分間に60リクエストかつ1ヶ月100万リクエスト可能らしい)
検索ページで都市名を入力すると、その地域の気象情報を取得できる形にします。
Node.js と Express
Node.js は、JavaScriptをサーバーサイドで実行するためのランタイム環境です。
Node.jsを使えば、クライアントサイドだけでなく、サーバーサイドもJavaScriptを使用してアプリケーション開発が可能です。
Express は、Node.jsの上に構築された軽量なWebアプリケーションフレームワークです。Expressを使うことで、Node.jsのみだと補いきれないような、ルーティングやミドルウェアといった便利な機能が簡単に追加されます。
例えば、リクエストを受け付けるサーバーの起動や、HTTPレスポンスと関連コンテンツの作成が可能です。
フォルダ構成
フォルダ構成は下記になります。
/weatherSearch
/views
index.ejs
weather.ejs
error.ejs
app.js
package.json
実際のコード
実際のコードは下記になります。
① app.js
まずは、ユーザーがフォームに都市名を入力すると、その都市の天気情報を取得して結果を返すコードです。
Expressを使用して実装しています。
const express = require('express');
const axios = require('axios');
const app = express();
const port = 8080;
// OpenWeatherMapのAPIキー
const apiKey = 'ここには取得したAPIキーを入力してください';
// テンプレートエンジンとしてEJSを使用(HTMLファイルの代わりに利用する)
app.set('view engine', 'ejs');
// POSTリクエストのbodyを解析するために必要になる
app.use(express.urlencoded({ extended: true }));
// ルートページにアクセスすると検索フォームを表示
app.get('/', (req, res) => {
res.render('index'); // views/index.ejs を表示
});
// フォーム送信後、天気情報を取得して表示
app.post('/weather', (req, res) => {
const city = req.body.city; // フォームから送信された都市名を取得
const url = `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&lang=ja&units=metric`;
axios.get(url)
.then(response => {
const weatherData = {
city: response.data.name,
weather: response.data.weather[0].description,
temp: response.data.main.temp,
windspeed: response.data.wind.speed
};
res.render('weather', { weather: weatherData }); // 取得した天気情報をweather.ejsで表示
})
.catch(error => {
console.error("エラーが発生しました。", error);
res.render('error', { errorMessage: '天気情報を取得できませんでした。' });
});
});
// サーバーを起動
app.listen(port, () => {
console.log(`サーバーがポート${port}で起動しました`);
});
② index.ejs
検索画面です。
今回は、テンプレートエンジンの1つである EJS(Embedded JavaScript)テンプレートを使って動的に内容を表示させています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天気検索</title>
</head>
<body>
<h1>天気を知りたい都市を入力してください</h1>
<form action="/weather" method="POST">
<label for="city">都市名:</label>
<input type="text" id="city" name="city" required>
<button type="submit">検索</button>
</form>
</body>
</html>
③ weather.ejs
結果表示画面です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天気情報</title>
</head>
<body>
<h1><%= weather.city %>の天気情報</h1>
<p>天気: <%= weather.weather %></p>
<p>温度: <%= weather.temp %>°C</p>
<p>風速: <%= weather.windspeed %>m/s</p>
<a href="/">戻る</a>
</body>
</html>
④ error.ejs
エラー発生時の画面になります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>エラー</title>
</head>
<body>
<h1>エラーが発生しました</h1>
<p><%= errorMessage %></p>
<a href="/">戻る</a>
</body>
</html>
コード解説
上記コードについて解説します。
① app.js
-
モジュールの読み込み
const express = require('express'); const axios = require('axios');
- express : Node.js用のWebフレームワークで、簡単にWebサーバーを構築できるようになります。この行でexpressモジュールを読み込んでいます。
- axios : HTTPリクエストを送るためのライブラリで、ここでは、OpenWeatherMap APIへのリクエストに使われます。
ちなみに、ここでrequireというメソッドが使われていますが、これはモジュールをインポートするための関数です。
ここでは詳細の説明は省略しますが、Node.jsはモジュールシステムを持っており、requireを使うことで、他のファイルや外部ライブラリで定義された機能(関数、オブジェクト、クラスなど)を現在のファイルに読み込むことができます。
例えば、NPM(Node Package Manager) は、Node.jsのパッケージ管理ツールです。(今回も使用しています)
NPMを使用すると、外部ライブラリやモジュールを簡単にプロジェクトにインストールし、requireを使ってそのライブラリをプロジェクトで使用することができます。package.jsonについて
パッケージ構成の中に、package.json というファイルがありました。Node.jsの環境構築ができていれば、
npm init
とコマンドで入力すると、package.jsonファイルが作成されます。
このファイルには、プロジェクトの依存関係やスクリプトなどが記述されるのですが、npm install express npm install axios
というように、使用したいモジュールをインストールすると、node_modulesディレクトリ が作成され、その中にexpressパッケージとaxiosパッケージがダウンロードされます。
また、package.json の dependenciesにexpressとaxios が追加されます。参考までに、下記のような形です。
package.json{ "name": "weathersearch", "version": "1.0.0", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "description": "", "dependencies": { "axios": "^1.7.7", "ejs": "^3.1.10", "express": "^4.21.0" } }
-
Expressアプリケーションの作成とポート設定
const app = express(); const port = 8080;
-
app : Expressのアプリケーションオブジェクトで、ルーティング(HTTPリクエストを処理するためのエンドポイント(URLとHTTPメソッド)を定義し)やミドルウェアの設定に使用します。
-
port : ポート番号を8080に設定。ここで指定したポートでクライアントからのリクエストを受け付けます。
-
-
OpenWeatherMapのAPIキーの設定
const apiKey = 'ここには取得したAPIキーを入力してください';
- apiKey : OpenWeatherMap APIにアクセスするためのAPIキー。このAPIキーを使用して認証を行い、天気情報を取得します。
上述しましたが、APIキーは有効化されるまで少し時間がかかるので注意してください。私は大体20分くらいかかりました。有効化してから実行しないとエラーが発生します。
-
テンプレートエンジンの設定
app.set('view engine', 'ejs');
- app.set('view engine', 'ejs') : ExpressでテンプレートエンジンとしてEJSを使用することを指定しています。setを使用することでこの後に登場するres.renderメソッドでEJSファイルをレンダリングできるようになります。
-
POSTリクエストのbody解析
app.use(express.urlencoded({ extended: true }));
- app.use(express.urlencoded({ extended: true })) : フォームから送信されたデータ(POSTリクエストのbody)を解析するためのコードです。POSTリクエストやPUTリクエストなど、HTTPリクエストのボディに含まれるデータ(例えば、フォームの送信データ)を解析することが可能となります。
-
ルートパスへのGETリクエストハンドリング
app.get('/', (req, res) => { res.render('index'); // views/index.ejs を表示 });
-
app.get('/', (req, res)) : /ルート(今回で言うとlocalhost:8080)にアクセスされたときのGETリクエストを処理します。
-
res.render('index') : viewsフォルダ内のindex.ejsテンプレートをレンダリングして表示します。
-
-
天気情報を取得するPOSTリクエストの処理
app.post('/weather', (req, res) => { const city = req.body.city; // フォームから送信された都市名を取得 const url = `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&lang=ja&units=metric`; axios.get(url) .then(response => { const weatherData = { city: response.data.name, weather: response.data.weather[0].description, temp: response.data.main.temp, windspeed: response.data.wind.speed }; res.render('weather', { weather: weatherData }); // 取得した天気情報をweather.ejsで表示 }) .catch(error => { console.error("エラーが発生しました。", error); res.render('error', { errorMessage: '天気情報を取得できませんでした。' }); }); });
-
app.post('/weather', (req, res)) : /weatherパスに対するPOSTリクエストを処理します。今回はフォームから送信されたデータを元に天気情報を取得するためのエンドポイントになります。
-
const city = req.body.city; : フォームから送信された都市名を取得します。req.body.cityはPOSTリクエストのbodyからcityフィールドの値を取得します。
-
const url = ... : OpenWeatherMap APIへのリクエストURLを組み立てています。cityとapiKeyをURLパラメータとして、指定された都市の天気情報を取得します。
-
axios.get(url) : axiosを使ってAPIリクエストを送信しています。
-
then(response => { ... }) : リクエストが成功した場合にAPIから返されたレスポンスを処理します。response.dataにはAPIのレスポンスデータが含まれています。
- weatherData : 今回取得したい気象情報(都市名、天気の説明、気温、風速)をオブジェクトとして格納しています。
- res.render('weather', { weather: weatherData }) : 取得した天気情報をweather.ejsテンプレートに渡して表示します。
-
catch(error => { ... }) : リクエストが失敗した場合のエラーハンドリングです。エラーが発生した場合、エラーメッセージを表示するerror.ejsをレンダリングします。
-
-
サーバーの起動
app.listen(port, () => { console.log(`サーバーがポート${port}で起動しました`); });
-
app.listen(port) : Expressサーバーを指定したポート番号で起動します。今回のケースでは http:localhost:8080 でのアクセスが可能です。
-
console.log(...) : 分かりやすいようにここではサーバーが起動したことをコンソールに表示しています。
-
② ejs(3種類ともやってることは同じなのでここではweather.ejsを取り上げます)
作成したejsですが、下記のコードで動的なデータの埋め込みを行っています。
<h1><%= weather.city %>の天気情報</h1>
<p>天気: <%= weather.weather %></p>
<p>温度: <%= weather.temp %>°C</p>
<p>風速: <%= weather.windspeed %>m/s</p>
EJSテンプレートでは、<%= %> の構文を使って、サーバーから渡されたデータをHTMLに埋め込みます。
今回の例で言うと、weatherというオブジェクトから、city、weather、temp、windspeedというプロパティを取得して、HTMLに埋め込んでいる、という形です。
出力結果
http:localhost:8080にアクセスすると、下記が表示されます。
都市名はTokyoというようにアルファベットで入力します。
検索すると、
このように東京都の天気情報が取得できました。
おまけ
今回は「天気」「温度」「風速」の3種類のデータを取得しましたが、別のデータを表示させることも可能です。今回の結果で取得できたAPIレスポンスボディは下記になります。
{
"coord": {
"lon": 139.6917,
"lat": 35.6895
},
"weather": [
{
"id": 803,
"main": "Clouds",
"description": "曇りがち",
"icon": "04n"
}
],
"base": "stations",
"main": {
"temp": 24.11,
"feels_like": 24.35,
"temp_min": 23.47,
"temp_max": 24.79,
"pressure": 1016,
"humidity": 68,
"sea_level": 1016,
"grnd_level": 1014
},
"visibility": 10000,
"wind": {
"speed": 6.69,
"deg": 50
},
"clouds": {
"all": 75
},
"dt": 1727686200,
"sys": {
"type": 2,
"id": 268395,
"country": "JP",
"sunrise": 1727642111,
"sunset": 1727684831
},
"timezone": 32400,
"id": 1850144,
"name": "東京都",
"cod": 200
}
例えば湿度も取得したいという場合であれば、humidity という項目を取得してあげれば表示が可能です。
app.js の変更部分のみ下記に記載します。
axios.get(url)
.then(response => {
const weatherData = {
city: response.data.name,
weather: response.data.weather[0].description,
temp: response.data.main.temp,
windspeed: response.data.wind.speed,
humidity: response.data.main.humidity
};
res.render('weather', { weather: weatherData }); // 取得した天気情報をweather.ejsで表示
})
.catch(error => {
console.error("エラーが発生しました。", error);
res.render('error', { errorMessage: '天気情報を取得できませんでした。' });
});
そして、weather.ejs も一部変更してあげます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>天気情報</title>
</head>
<body>
<h1><%= weather.city %>の天気情報</h1>
<p>天気: <%= weather.weather %></p>
<p>温度: <%= weather.temp %>°C</p>
<p>風速: <%= weather.windspeed %>m/s</p>
<p>湿度: <%= weather.humidity %>%</p>
<a href="/">戻る</a>
</body>
</html>
下記が実行結果です。
問題なく取得できました。
まとめ
Node.jsやExpressのキャッチアップと併せて、Web APIの仕組みも理解できてよかったです。
フロントやAPIの理解が深まると作成するWEBアプリケーションの幅も随分広がると思うので、バックエンドの学習と併せて学習を続けていきたいです。