Next.js中使用环境变量隐藏API密钥及现有代码修改方案问询
Next.js 隐藏OpenWeather API密钥解决方案
步骤1:配置环境变量
在项目根目录创建.env.local文件,添加如下内容:
OPENWEATHER_API_KEY=你实际的OpenWeather API密钥
- 不要给这个变量加
NEXT_PUBLIC前缀,加了前缀会暴露到客户端代码,密钥就泄露了 - 确保
.env.local已经添加到.gitignore文件中,避免被提交到代码仓库
步骤2:创建服务端API路由
Next.js的API路由运行在服务端,不会泄露服务端环境变量,用来代理第三方接口请求刚好合适。
在pages/api目录下创建weather.js文件,代码如下:
export default async function handler(req, res) { const { q, units = 'metric' } = req.query; const API_KEY = process.env.OPENWEATHER_API_KEY; try { // 第一步查询城市坐标 const firstRes = await fetch( `https://api.openweathermap.org/data/2.5/weather?q=${q}&units=${units}&appid=${API_KEY}` ); if (!firstRes.ok) throw new Error('城市查询失败'); const firstData = await firstRes.json(); const { lon, lat } = firstData.coord; // 第二步查询天气详情 const weatherRes = await fetch( `https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&units=${units}&exclude=&appid=${API_KEY}` ); if (!weatherRes.ok) throw new Error('天气查询失败'); const weatherData = await weatherRes.json(); // 合并需要的数据返回给前端 res.status(200).json({ weather: weatherData, city: `${firstData.name}, ${firstData.sys.country}`, searchInput: firstData.name }); } catch (err) { res.status(500).json({ error: err.message }); } }
步骤3:修改原有的Context代码
把原来客户端直接调用第三方接口的逻辑,改成调用你刚写的本地API路由,不需要再传密钥:
import { createContext, useState, useEffect } from 'react'; const WeatherContext = createContext({ searchLocation: (input) => {}, btnHandler: (input) => {}, weather: '', isLoading: true, }); export function WeatherContextProvider(props) { const [weather, setWeather] = useState({}); const [city, setCity] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); const [unit, setUnit] = useState('metric'); const [searchInput, setSearchInput] = useState(''); const btnHandler = () => { setUnit(prev => prev === 'metric' ? 'imperial' : 'metric'); }; const searchLocation = async (input = searchInput, units = unit) => { const queryInput = loading ? 'London' : input; try { const response = await fetch( `/api/weather?q=${encodeURIComponent(queryInput)}&units=${units}` ); const data = await response.json(); if (!response.ok) { throw new Error(data.error || '请求失败'); } setWeather(data.weather); setCity(data.city); setSearchInput(data.searchInput); setLoading(false); setError(false); } catch (error) { setError(error.message); console.log(error); } }; useEffect(() => { searchLocation(); }, [unit, searchInput]); const context = { searchLocation: searchLocation, city: city, weather: weather, isLoading: loading, error: error, btnHandler: btnHandler, unit: unit, searchInput: searchInput, }; return ( <WeatherContext.Provider value={context}> {props.children} </WeatherContext.Provider> ); } export default WeatherContext;
补充说明
- 如果你用的是Next.js 13+ App Router,只需要把API路由创建在
app/api/weather/route.js路径下,导出对应的GET方法即可,其余逻辑完全一致 - 可以根据需要给API路由加请求频率限制,避免接口被滥用
内容的提问来源于stack exchange,提问作者illyria
相关产品推荐
相关产品推荐

