Node.js勉強の第一歩：天気予報表示アプリ作成に挑戦

## はじめに

今回は、JavaScriptの基本操作に加えて、APIの活用と非同期処理に挑戦し、**天気APIを使って天気予報を表示するアプリ**を作ってみました。

当初は、フロントエンドだけで完結する構成を想定していましたが、GitHubで管理する際にAPIキーが公開されてしまうという問題に直面しました。
そこで、**Node.jsを使ってバックエンドを作成し、APIキーを安全に管理する方法**も学ぶことにしました。

この記事では、私が実際に試行錯誤しながら学んだ過程を共有していきます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841606/1315e18f-4096-4b1b-afa2-0187f8dc3de2.png" width="500">

## Node.jsの役割とバックエンドについて

Node.jsは、バックエンドの処理をJavaScriptで行うための環境です。
フロントエンドからのリクエストを受け取り、外部APIとの通信やデータ処理を行い、その結果をフロントエンドに返します。

今回の場合、具体的には次のような流れになります。

1. ユーザーがブラウザで都市名を入力
2. 入力された都市名が、Node.jsで動くバックエンドに送られる
3. バックエンドが天気APIにアクセスし、APIキーを使って天気情報を取得
4. 取得した天気情報を、フロントエンドに返して画面に表示

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3841606/32e02b48-a55b-4649-a08a-ad2f8026c223.png)

## ディレクトリ構成

以下、具体的な実装方法についてみていきます。

まずはディレクトリ構成です。

```
weather-app/
├── public/
│   ├── index.html
│   ├── style.css
│   └── main.js
├── .env
└── server.js
```

- **`public/`**：ブラウザからアクセスされるHTML・CSS・JSファイルをまとめる場所
- **`.env`**：APIキーなどの機密情報を記載。コードとは別管理にしてセキュリティを保つ
- **`server.js`**：Node.jsで動くサーバー本体。APIとの通信やデータのやり取りを行う

## フロントエンドの実装

フロントエンドでは、次のようにしてバックエンドにリクエストを送信しています。

```js
const res = await axios.get(`/weather?city=${city}`);
```

最初は、これが単なるWebページのURLだと思っていましたが、実は「**APIエンドポイント**」と呼ばれるもので、例えば `/weather?city=都市名` のように特定のデータを取得するためのルートを指定しています。

この例では、`axios.get(...)` を使って、`/weather` にリクエストを送り、その際に都市名の情報を **クエリ文字列（`?city=都市名`）** として付加しています。

バックエンドではこの都市名を受け取り、該当する天気情報を取得してレスポンスとして返してくれます。
返ってきたデータを元に、画面を更新すれば天気情報が表示される、という流れです。

## バックエンドの実装

### ① 必要なライブラリの読み込みと環境変数の設定

```js
const express = require("express");
const axios = require("axios");
const dotenv = require("dotenv");
```

### 説明：

- `express`: サーバー機能を簡単に扱えるフレームワーク
- `axios`: 外部API（天気API）にリクエストを送るためのライブラリ
- `dotenv`: `.env`ファイルに書いたAPIキーなどを使えるようにする

---

### ② 環境変数を読み込む設定（dotenvの利用）

```js
dotenv.config();
```

### 説明：

- `dotenv.config();` で、`.env` ファイルに書かれた環境変数を Node.js の中で使えるようにする

---

### ③ サーバーの基本設定

```js
const app = express();
const PORT = 3000;
```

### 説明：

- `app`: Express アプリケーションの本体。ルーティングやミドルウェアの設定を行う
- `PORT`: サーバーを動かすポート番号

---

### ④ 静的ファイルの公開設定

```js
app.use(express.static("public"));
```

### 説明：

- `public` フォルダのファイル（HTML、CSS、JSなど）を、ブラウザから直接アクセスできるようにする
- 例えば、`http://localhost:3000/index.html` にアクセスすると `public/index.html` が表示されるようになる

---

### ⑤ 天気データ取得用のルートを定義

```js
// API呼び出し
app.get("/weather", async (req, res) => {
  // API呼び出しに必要な値の取得
  const city = req.query.city;
  const apiKey = process.env.API_KEY;

  try {
    // OpenWeatherMap APIを使って指定された都市の天気情報を取得
    const response = await axios.get(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`
    );

    // 取得した天気データをフロントエンドに返す
    res.json(response.data);
  } catch (error) {
    res.status(500).json({ error: "天気情報の取得に失敗しました" });
  }
});
```

### 説明：

- `/weather?city=Tokyo` のようなリクエストを受け取り、都市名を取得
- バックエンドでAPIキーを使って OpenWeatherMap API にアクセス
- 結果を JSON 形式でフロントエンドに返す
- エラー時にはステータス500でエラーメッセージを返す

---

### ⑥ サーバーの起動

```js
app.listen(PORT, () => {
  console.log(`Server is running on <http://localhost>:${PORT}`);
});
```

### 説明：

- 指定したポートでサーバーを起動
- コンソールに起動したことがわかるメッセージを出力

## 初心者としてのつまずきポイント・学び

### 非同期処理の話

`axios.get()` はサーバーに天気情報を取りに行く処理ですが、これは外部と通信するため、終わるまで待ってから次に進む必要があります。

そのために `async/await` を使っています。

- 関数の前に `async` をつけると、「この関数は非同期処理を含んでいますよ」と宣言できる
- その中で `await axios.get(...)` のように書くと、**レスポンスが返ってくるまで処理を一時停止**してくれる

外部との通信には時間がかかることを意識し、結果が返ってくるまで待つ必要があるということを学びました。

---

### APIキーの取り扱いの話

天気情報を取得するためには、OpenWeatherMapなどのサービスから**APIキー**を取得する必要があります。

このAPIキーは他人に知られてはいけない情報なので、JavaScriptのコードの中に**直接書くのはNG**です。

そこで `.env` ファイルを使えば、**APIキーを外から見えないように隠すことができます**。

```ini
# .env ファイルに記述する内容
API_KEY=自分のAPIキー
```

これを `dotenv` パッケージを使って `server.js` で読み込むことで、環境変数として使えるようになります。

```js
const apiKey = process.env.API_KEY;
```

そして、GitHubでコードを公開する際は `.env` ファイルを**絶対に公開しないこと**が重要です。

`.gitignore` に `.env` を追加しておくことで、誤ってアップロードしてしまうのを防げます。

なお、`.gitignore` を作成する際に「Node」を選んでおけば、`.env` のほか `node_modules` なども自動的に無視対象に設定されるので便利です。

## 感想

- 本来は別で学ぶ予定だったNode.jsを、今回のプロジェクトで一気にまとめて習得しました
- この実装を通して、バックエンドの役割や仕組みを具体的に理解でき、フロントエンドとの連携も実際に体感できたことで理解がさらに深まりました