はじめに
今回、Vanila JSでOpenWeather APIを利用したアプリ開発中に発生した問題とその解決方法を紹介します。
この記事は、私自身の学習過程の中で得た気づきをまとめたものです。
まだ理解が浅い部分や誤りが含まれている可能性があります。
もし間違いや勘違いがありましたら、ご指摘していただけると嬉しいです。
アプリ概要

都市名を入力し、検索するとその都市名、温度、天気が表示されるという非常にシンプルな構成です。
問題点
APIキーが取得できてしまう
APIキーを取得されると
・APIキー不正利用による無料枠を超えての課金
・攻撃者による大量クエストにより、サービス停止
・悪意ある利用による違法活動
などのセキュリティリスクにつながる。
APIキーを.envで管理しても以下の方法で公開されてしまう
・開発者ツールのネットワークで表示されてしまう。

・ビルド : ビルド後に作成されるdist/assetsの中にAPIキーが表示されてしまう。
解決策
APIキーをサーバーサイドで管理する
クライアントサイドと違い、攻撃者はサーバーを見ることができないためサーバーサイドで管理することにより、安全に管理することができる。
今回は、Next.jsで開発する。
具体的な実装
Next.jsをインストールする。
npx create-next-app@latest
.envファイルを作成する。
touch .env
.envファイルに自分で作成したAPIキーを記述する。
OPENWEATHER_API_KEY=YOUR_API_KEY
.gitignoreに.envの記述があるか確認する。
クライアントサイドとサーバーサイドに分離する。
今回は、シンプルなアプリなのでapp/page.tsxにクライアントサイドの記述をする。
サーバーサイドは、app/api/route.tsに記述する。
import { NextRequest, NextResponse } from "next/server";
const API_KEY = process.env.OPENWEATHER_API_KEY;
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const cityName = searchParams.get("cityName");
if (!cityName?.trim()) {
return NextResponse.json(
{ error: "都市名が入力されていません。" },
{ status: 400 }
);
}
if (!API_KEY) {
return NextResponse.json(
{ error: "APIキーが設定されていません。" },
{ status: 500 }
);
}
const res = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${cityName}&appid=${API_KEY}`
);
if (!res.ok) {
if (res.status == 404) {
return NextResponse.json(
{ error: "都市が見つかりませんでした。" },
{ status: 404 }
);
} else if (res.status == 401) {
return NextResponse.json(
{ error: "APIキーが正しくありません。" },
{ status: 401 }
);
} else {
return NextResponse.json(
{ error: "エラーが発生しました。" },
{ status: res.status }
);
}
}
const data = await res.json();
return NextResponse.json(data);
}
API_KEYという変数を定義して、.envからAPIキーを読み込む。
"use client";
import { useState } from "react";
interface WeatherData {
name: string;
main: {
temp: number;
};
weather: Array<{
main: string;
}>;
}
export default function Home() {
const [cityName, setCityName] = useState("");
const [weatherData, setWeatherData] = useState<WeatherData | null>(null);
const getCurrentWeather = async (cityName: string) => {
try {
const res = await fetch(`/api?cityName=${cityName}`);
if (!res.ok) {
const errorData = await res.json();
throw new Error(errorData);
}
const data = await res.json();
setWeatherData(data);
return data;
} catch (err) {
console.error(err);
}
};
const handleSearch = () => {
if (cityName.trim()) {
getCurrentWeather(cityName);
setCityName("");
}
};
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center space-y-4">
<h1 className="font-bold text-4xl mb-6">Weather Forecast</h1>
<input
type="text"
className="outline-none border rounded-md mr-1"
value={cityName}
onChange={(e) => setCityName(e.target.value)}
placeholder="都市名を入力"
/>
<button
type="submit"
className="bg-gray-700 rounded-md p-1 cursor-pointer hover:bg-gray-500"
onClick={handleSearch}
>
検索
</button>
{weatherData && (
<div className="mt-6 space-y-2">
<p className=" text-xl">{weatherData.name}</p>
<p className="text-3xl font-bold">
{(weatherData.main.temp - 273.15).toFixed(1)}℃
</p>
<p className="text-xl">{weatherData.weather[0].main}</p>
</div>
)}
</div>
</div>
);
}
これらの実装により、APIキーが流出していないことを確認できました。

まとめ
このように.envはサーバーサイド側で管理することにより、APIキーを安全に管理することができました。